Translate English built from template literals in attributes #349

Closed
opened 2026-09-14 15:26:32 +00:00 by jcoffey-dev · 0 comments
Owner

Follow-up to #348, which left English built with template literals as the one known gap.

Checker

scripts/i18n-literals.mjs now flags a template literal in a UI attribute or prop (the same positions #348 covered) when there are words between its values. Neither the catalogue-key exemption nor the sentence-shape test applies: a template can't be a key as written, and ${name} — shared by ${owner} starts with a value, not a capital letter. A template that is only punctuation around values, like ${name} (${size}), isn't reported. With any one site reverted, npm run i18n:check exits 1.

Fixed: 12 sites

Where Was Key
Quota bar title `${used} of ${total} used` {used} of {total} used
Address menu ariaLabel `Actions for ${email}` Actions for {address}
Folder unread count title `${own} here, ${n} in subfolders` {here} here, {inSubfolders} in subfolders
Recipient chip remove `Remove ${email}` Remove {address} (existing)
Contact editor title `Edit ${name}` Edit {name}
Shared calendar / address book `${name} — shared by ${owner}` {name} — shared by {owner}
Available calendar / address book `${name} — from ${owner}` {name} — from {owner}
Date / time field labels `${label} (date)`, "Date" {label} (date), {label} (time), Date, Time (existing)
Attachment title fallback "attachment" Attachment
Free/busy bar `${b.busyStatus}: …` {status}: {start} – {end}, with Busy / Tentative (existing) / Unavailable

The free/busy bar showed the raw JMAP value (confirmed, tentative, unavailable) in every language, English included. It now maps through BUSY_LABEL.

The last two rows aren't the checker's finds. The attachment fallback is a lower-case string inside a template, and the busy status had no words outside its values. Both were found while going through the list.

Translation

11 new keys × 9 catalogues: {used} of {total} used, Actions for {address}, {here} here, {inSubfolders} in subfolders, Edit {name}, {name} — shared by {owner}, {name} — from {owner}, {label} (date), {label} (time), {status}: {start} – {end}, Unavailable, Attachment.

Each catalogue goes from 1425/1441 to 1436/1452 translated, and the fallback count stays at 16. The wording follows each catalogue's existing terms (Unterordner / sous-dossiers / вложенные папки, Freigegeben, …). French gets its space before the colon, and Japanese and Chinese use full-width punctuation. The Japanese unread-count string leaves out a counter, because the count can be messages (通) or conversations (件).

Checked

  • npm run i18n:check passes, and exits 1 with a site reverted.
  • npm run typecheck -w web, npm run build -w web and npm test -w web (1215 passed) all pass.
  • A throwaway Vitest run loaded the German and Japanese catalogues and rendered four of the new keys with values. None came back with a placeholder or English left in.
  • Not viewed in the browser. These are tooltips and accessible names.

Merged 2026-09-14 as coffey-labs/ihasmail@7940a15a43

Rebuilt from: git history, session transcript.

Follow-up to #348, which left English built with template literals as the one known gap. ## Checker `scripts/i18n-literals.mjs` now flags a template literal in a UI attribute or prop (the same positions #348 covered) when there are words between its values. Neither the catalogue-key exemption nor the sentence-shape test applies: a template can't be a key as written, and `${name} — shared by ${owner}` starts with a value, not a capital letter. A template that is only punctuation around values, like `${name} (${size})`, isn't reported. With any one site reverted, `npm run i18n:check` exits 1. ## Fixed: 12 sites | Where | Was | Key | |---|---|---| | Quota bar title | `` `${used} of ${total} used` `` | `{used} of {total} used` | | Address menu `ariaLabel` | `` `Actions for ${email}` `` | `Actions for {address}` | | Folder unread count title | `` `${own} here, ${n} in subfolders` `` | `{here} here, {inSubfolders} in subfolders` | | Recipient chip remove | `` `Remove ${email}` `` | `Remove {address}` (existing) | | Contact editor title | `` `Edit ${name}` `` | `Edit {name}` | | Shared calendar / address book | `` `${name} — shared by ${owner}` `` | `{name} — shared by {owner}` | | Available calendar / address book | `` `${name} — from ${owner}` `` | `{name} — from {owner}` | | Date / time field labels | `` `${label} (date)` ``, `"Date"` | `{label} (date)`, `{label} (time)`, `Date`, `Time` (existing) | | Attachment title fallback | `"attachment"` | `Attachment` | | Free/busy bar | `` `${b.busyStatus}: …` `` | `{status}: {start} – {end}`, with Busy / Tentative (existing) / Unavailable | The free/busy bar showed the raw JMAP value (`confirmed`, `tentative`, `unavailable`) in every language, English included. It now maps through `BUSY_LABEL`. The last two rows aren't the checker's finds. The attachment fallback is a lower-case string inside a template, and the busy status had no words outside its values. Both were found while going through the list. ## Translation **11 new keys × 9 catalogues:** `{used} of {total} used`, `Actions for {address}`, `{here} here, {inSubfolders} in subfolders`, `Edit {name}`, `{name} — shared by {owner}`, `{name} — from {owner}`, `{label} (date)`, `{label} (time)`, `{status}: {start} – {end}`, `Unavailable`, `Attachment`. Each catalogue goes from 1425/1441 to 1436/1452 translated, and the fallback count stays at **16**. The wording follows each catalogue's existing terms (Unterordner / sous-dossiers / вложенные папки, Freigegeben, …). French gets its space before the colon, and Japanese and Chinese use full-width punctuation. The Japanese unread-count string leaves out a counter, because the count can be messages (通) or conversations (件). ## Checked - `npm run i18n:check` passes, and exits 1 with a site reverted. - `npm run typecheck -w web`, `npm run build -w web` and `npm test -w web` (1215 passed) all pass. - A throwaway Vitest run loaded the German and Japanese catalogues and rendered four of the new keys with values. None came back with a placeholder or English left in. - Not viewed in the browser. These are tooltips and accessible names. **Merged** 2026-09-14 as coffey-labs/ihasmail@7940a15a4363 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.