From b10149fd541107e3f8c6ae49a4f8a6d66b7b932e Mon Sep 17 00:00:00 2001 From: John Coffey Date: Thu, 3 Sep 2026 11:37:05 -0700 Subject: [PATCH] Translate three lists the code was rendering raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A native speaker reviewing the German catalogue reported strings appearing in English (#247). Three of the reported areas turned out not to be missing translations at all: the strings were there, and the code was rendering the English source instead of asking for one. The Sieve rule dialog rendered HEADER_CHOICES and HEADER_OPS labels directly. All sixteen are already in every catalogue -- "Subject" has been "Betreff" in de.ts all along, which is exactly the inconsistency the reporter noticed against the Out-of-office page, where it already reads Betreff. Wrapping the two dropdowns fixes nine languages at once and adds nothing to any catalogue. The keyboard shortcut panel rendered each binding's group and description directly. Those are registered in English at the call sites and should stay that way -- the binding table is data and the English is the catalogue key -- so the panel translates them at render instead. A binding added anywhere is then translatable without its registrar knowing i18n exists. The palette grid marks every name translate="no". That is right for ihasmail, Dracula, Gruvbox, Rosé Pine and Tokyo Night, which are names. "Classic" is an adjective describing the theme, not a name, so PaletteMeta gains a `translatable` flag for the one entry that is a word. Flagging the exception beats dropping the attribute from all six. No catalogue changes here: the strings the first two need are already present in all nine. "Classic" needs an entry, which lands with each language. --- web/src/lib/palette.ts | 12 +++++++++++- web/src/views/settings/AppearanceSettings.tsx | 4 +++- web/src/views/settings/RuleDialog.tsx | 4 ++-- web/src/views/settings/ShortcutsSettings.tsx | 10 ++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/web/src/lib/palette.ts b/web/src/lib/palette.ts index 6ca042a..a1ec3cb 100644 --- a/web/src/lib/palette.ts +++ b/web/src/lib/palette.ts @@ -23,10 +23,20 @@ export interface PaletteMeta { name: string; /** Shown in Settings and in NOTICE; who to credit and under what. */ credit?: string; + /** + * Whether the name is a word rather than a name. + * + * Five of these six are proper names -- ihasmail, Dracula, Gruvbox, Rosé + * Pine, Tokyo Night -- and are rendered translate="no" so a page translator + * leaves them alone. "Classic" is not a name, it is an adjective describing + * the theme, and a German reader should see "Klassisch". Reported by a + * native speaker reviewing the German catalogue (#247). + */ + translatable?: boolean; } export const PALETTES: PaletteMeta[] = [ - { id: "default", name: "Classic" }, + { id: "default", name: "Classic", translatable: true }, { id: "ihasmail", name: "ihasmail" }, { id: "dracula", name: "Dracula", credit: "Dracula Theme (MIT) — dark: Dracula, light: Alucard" }, { id: "gruvbox", name: "Gruvbox", credit: "gruvbox by morhetz (MIT)" }, diff --git a/web/src/views/settings/AppearanceSettings.tsx b/web/src/views/settings/AppearanceSettings.tsx index 883719b..d89826e 100644 --- a/web/src/views/settings/AppearanceSettings.tsx +++ b/web/src/views/settings/AppearanceSettings.tsx @@ -75,7 +75,9 @@ export function AppearanceSettings() { return ( ); })} diff --git a/web/src/views/settings/RuleDialog.tsx b/web/src/views/settings/RuleDialog.tsx index 0bb8006..02f6cc2 100644 --- a/web/src/views/settings/RuleDialog.tsx +++ b/web/src/views/settings/RuleDialog.tsx @@ -60,7 +60,7 @@ export function RuleDialog({ rule, onClose, onSave, applyMailbox, applyByDefault else if (v === "address") setTest(i, { type: "address", header: "from", part: "domain", op: "is", value: "" }); else setTest(i, { type: "header", header: v === "__custom__" ? "" : v, op: "contains", value: "" }); }}> - {HEADER_CHOICES.map((h) => )} + {HEADER_CHOICES.map((h) => )} @@ -75,7 +75,7 @@ export function RuleDialog({ rule, onClose, onSave, applyMailbox, applyByDefault ) : t.type === "true" ? : ( )} {t.type === "size" ? ( diff --git a/web/src/views/settings/ShortcutsSettings.tsx b/web/src/views/settings/ShortcutsSettings.tsx index b721b9f..9a9ecce 100644 --- a/web/src/views/settings/ShortcutsSettings.tsx +++ b/web/src/views/settings/ShortcutsSettings.tsx @@ -21,9 +21,15 @@ export function ShortcutsSettings() {
{groups.map(([group, items]) => (
-

{group}

+ {/* Group names and descriptions are registered in English at the + call sites -- see views/Shortcuts.tsx -- because the binding + table is data, not markup, and the English is the catalogue + key. Translating at render keeps the registration simple and + means a binding added anywhere is translatable without the + registrar knowing about i18n. */} +

{t(group)}

{items.map((b) => ( -
{b.description}
+
{t(b.description)}
))}
))}