untranslated/untranslatable strings #247

Closed
opened 2026-09-03 18:04:48 +00:00 by mbunkus · 5 comments

I'm starting to look through the German translation (I'm a native speaker) & will provide a PR once I'm through.

While I'm doing that I've started noticing several places where strings remain in English and are missing from the de.ts translation file completely. I'll start posting them here. I will not post strings I would translate differently here as I'll take care of those with a PR as stated above.

This will be ongoing work. If you start fixing things before I'll report that I've gone through everything, please leave this issue open for me to post anything further I stumble across. Due to repo permissions I cannot re-open issues once they've been closed. Thanks.

Login form

Image Image

Settings

Menu

Image

General

"Message order" drop down: all but the last entry which is translated

Image Image Image

Appearance

I'm a bit torn about this one. However, "Classic" is not really a name of a product, it's more of an adjective, therefore it should be translated IMHO.

Image

Filters & rules

Sieve rule dialog

At least the one entry with the arrow. I'm a bit torn about the others. If you take them as verbatim header names, then leave them as-is. However, it's rather common to read them in German in various mail-related settings, e.g. "An" (To), "Betreff" (Subject) etc. When there are no obvious good translations one can use "List-Id-Kopfzeile" (this is the translation of "List-Id header", not of "List-Id", but then again a good translation is not verbatim but takes local idiosyncrasies into account).

And ihasmail already uses exactly those German terms in other places, e.g. on the "Out of office" settings page: "Betreff" for "Subject".

Image Image

Sieve rule list

All the summaries

Image

Labels

Label list

Image

I'm perfectly fine with not translating the word "Label" (singular & plural) itself as it's a technical term referring to something very specific in IMAP terms that's easily understood in Germany.

New label dialog

The buttons

Image

Templates

New template dialog

The whole lower section safe for the placeholders themselves which should obviously not be translated.

Image

Calendar & contacts

New color category dialog

The whole dialog

Image

Section below the color categories

Image

Privacy & security

Apart from the label in the menu? Nearly everything on this settings page. I'll spare you the three or so screenshots it would take.


I'll continue later.

Rebuilt from: GH Archive, notification email, session transcript.

I'm starting to look through the German translation (I'm a native speaker) & will provide a PR once I'm through. While I'm doing that I've started noticing several places where strings remain in English and are missing from the `de.ts` translation file completely. I'll start posting them here. I will not post strings I would translate differently here as I'll take care of those with a PR as stated above. This will be ongoing work. If you start fixing things before I'll report that I've gone through everything, please leave this issue open for me to post anything further I stumble across. Due to repo permissions I cannot re-open issues once they've been closed. Thanks. ## Login form <img width="522" height="145" alt="Image" src="https://github.com/user-attachments/assets/2634c951-29c3-43a6-9f01-112e4b1e6df1" /> <img width="522" height="145" alt="Image" src="https://github.com/user-attachments/assets/e398ff03-9fea-476d-87be-a8b201a4b56d" /> ## Settings ### Menu <img width="331" height="666" alt="Image" src="https://github.com/user-attachments/assets/80b75df8-41ff-4ba1-aa4b-ac2ff799bcee" /> ### General "Message order" drop down: all but the last entry which is translated <img width="588" height="462" alt="Image" src="https://github.com/user-attachments/assets/729edacf-bcf2-48b0-ab72-a5513ec3e685" /> <img width="202" height="209" alt="Image" src="https://github.com/user-attachments/assets/f39f00c7-3b89-409b-bd13-c97614f07827" /> <img width="431" height="69" alt="Image" src="https://github.com/user-attachments/assets/2c3255e0-9673-4392-a673-4f8f6506fa4c" /> ### Appearance I'm a bit torn about this one. However, "Classic" is not really a name of a product, it's more of an adjective, therefore it should be translated IMHO. <img width="225" height="182" alt="Image" src="https://github.com/user-attachments/assets/d6845be1-7d28-4b2d-8708-45c35c7c2e39" /> ### Filters & rules #### Sieve rule dialog At least the one entry with the arrow. I'm a bit torn about the others. If you take them as verbatim header names, then leave them as-is. However, it's rather common to read them in German in various mail-related settings, e.g. "An" (To), "Betreff" (Subject) etc. When there are no obvious good translations one can use "List-Id-Kopfzeile" (this is the translation of "List-Id header", not of "List-Id", but then again a good translation is not verbatim but takes local idiosyncrasies into account). And ihasmail already uses exactly those German terms in other places, e.g. on the "Out of office" settings page: "Betreff" for "Subject". <img width="433" height="793" alt="Image" src="https://github.com/user-attachments/assets/5f1418b3-43a3-4dfc-849f-ab7ceef1d1fd" /> <img width="284" height="84" alt="Image" src="https://github.com/user-attachments/assets/7022ddfa-1e05-40a5-bac2-0e847fd27b82" /> #### Sieve rule list All the summaries <img width="1048" height="375" alt="Image" src="https://github.com/user-attachments/assets/5cb8c991-575d-4ffa-a422-b0a60f2bebad" /> ### Labels #### Label list <img width="1178" height="191" alt="Image" src="https://github.com/user-attachments/assets/69637888-df96-4f9b-aa81-149367a995f5" /> I'm perfectly fine with _not_ translating the word "Label" (singular & plural) itself as it's a technical term referring to something very specific in IMAP terms that's easily understood in Germany. #### New label dialog The buttons <img width="575" height="259" alt="Image" src="https://github.com/user-attachments/assets/b6c771d3-fafa-425c-9145-c013b6e55eea" /> ### Templates #### New template dialog The whole lower section safe for the placeholders themselves which should obviously not be translated. <img width="1071" height="509" alt="Image" src="https://github.com/user-attachments/assets/ebc1dcae-f74b-4860-bee4-6724f7c10213" /> ### Calendar & contacts #### New color category dialog The whole dialog <img width="565" height="240" alt="Image" src="https://github.com/user-attachments/assets/7b0f36c6-dfe4-4bc9-b752-8906fcd46661" /> #### Section below the color categories <img width="1185" height="430" alt="Image" src="https://github.com/user-attachments/assets/2444d3f3-956e-407c-8894-bb931a8086f5" /> ### Privacy & security Apart from the label in the menu? Nearly _everything_ on this settings page. I'll spare you the three or so screenshots it would take. --- I'll continue later. <sub>Rebuilt from: GH Archive, notification email, session transcript.</sub>
Author

…but I will likely find them and more since you are about 120ish commits behind main

I'm using the Docker image from the Github container registry, latest tag, pulled right before starting to write this issue. This is due to problems running the build from git on my work's test machine (npm fails to finish properly for some reason I couldn't be bothered to investigate).

> …but I will likely find them and more since you are about 120ish commits behind main I'm using the Docker image from the Github container registry, `latest` tag, pulled right before starting to write this issue. This is due to problems running the build from git on my work's test machine (`npm` fails to finish properly for some reason I couldn't be bothered to investigate).
Owner

Thank you — this was a genuinely useful report, and it turned out to be worth more than the list of screenshots suggested.

All of it is fixed and merged, and there is a new latest on GHCR with everything in it: 2026.9.3-pr259. You were testing 2026.8.30+pr129, so this is a fair jump.

The part I did not expect

Three of the areas you flagged were not missing translations at all. The strings were already in every catalogue and the code was rendering the English source instead of asking for one:

  • The Sieve rule dialog — the field and operator dropdowns rendered their labels directly. "Subject": "Betreff" has been in de.ts all along. That is exactly the inconsistency you noticed against the Out-of-office page, and it was a rendering bug rather than a translation gap.
  • The keyboard shortcut panel — every binding's group and description, same cause.
  • The settings menu — same again.

One fix, and all nine languages got those at once.

That also answers your question about the header names better than I could have: each catalogue had already decided. German translates From/To/Cc/Subject and leaves List-Id and X-Spam-Status alone, which I think is the right instinct — the first four are ordinary words a reader knows from every mail client, the last two are literal header names. Anything a catalogue omits falls back to English, so that stays a per-language decision rather than mine.

Classic

You were right, and I have taken your reasoning as written. Five of the six palette names are proper names — ihasmail, Dracula, Gruvbox, Rosé Pine, Tokyo Night — and stay translate="no". "Classic" is an adjective describing the theme, so it now carries a translatable flag and reads Klassisch.

The actual gap

You were right about the cause too. Features shipped after the catalogues were written and nobody went back. Two further sweeps after your list turned up more of the same:

  • Scheduled send was untranslated end to end — all four presets, and its three validation messages
  • The read-receipt explanations, and the "Requested, to …" line, which was a template literal with the address concatenated in
  • Compose toasts and three thrown errors that surface to the reader
  • Add star / Remove star, which had never been in any catalogue in any language
  • The delete confirmation, which read "message(s)" — a parenthesis standing in for agreement, so every inflecting language got the wrong form regardless

Also two counters that appended an s unless the count was one. That is English grammar written into the code; it produced correct German by coincidence and would never have been right for Russian. Those are plural forms now.

196 entries per language, across all nine, one pull request each. Every t() call site is now covered in every catalogue, including the ones reached through a variable — which is how Add star had hidden, since a scan for t("literal") cannot see them.

Still open, and known

Two things you will hit if you keep going, so you do not spend time re-reporting them:

  1. The Sieve rule list summaries — the item in your first comment. describeRule() builds those sentences by concatenation, so the fragments cannot be translated independently and the word order would be wrong in German regardless. It needs restructuring into whole sentences with placeholders.
  2. Recurrence descriptions in the event editor have the same problem, plus English ordinals and weekday names.

Both are on the list. They are a different shape of fix from everything above, which is why they are not in this batch.

Leaving this open as you asked. Anything further you find is welcome — and if the npm build on your test machine ever becomes worth chasing, I am happy to look at that too.

Thank you — this was a genuinely useful report, and it turned out to be worth more than the list of screenshots suggested. All of it is fixed and merged, and there is a new `latest` on GHCR with everything in it: **`2026.9.3-pr259`**. You were testing `2026.8.30+pr129`, so this is a fair jump. ## The part I did not expect Three of the areas you flagged were not missing translations at all. The strings were already in every catalogue and the code was rendering the English source instead of asking for one: - **The Sieve rule dialog** — the field and operator dropdowns rendered their labels directly. `"Subject": "Betreff"` has been in `de.ts` all along. That is exactly the inconsistency you noticed against the Out-of-office page, and it was a rendering bug rather than a translation gap. - **The keyboard shortcut panel** — every binding's group and description, same cause. - **The settings menu** — same again. One fix, and all nine languages got those at once. That also answers your question about the header names better than I could have: each catalogue had already decided. German translates `From`/`To`/`Cc`/`Subject` and leaves `List-Id` and `X-Spam-Status` alone, which I think is the right instinct — the first four are ordinary words a reader knows from every mail client, the last two are literal header names. Anything a catalogue omits falls back to English, so that stays a per-language decision rather than mine. ## Classic You were right, and I have taken your reasoning as written. Five of the six palette names are proper names — ihasmail, Dracula, Gruvbox, Rosé Pine, Tokyo Night — and stay `translate="no"`. "Classic" is an adjective describing the theme, so it now carries a `translatable` flag and reads **Klassisch**. ## The actual gap You were right about the cause too. Features shipped after the catalogues were written and nobody went back. Two further sweeps after your list turned up more of the same: - Scheduled send was untranslated end to end — all four presets, and its three validation messages - The read-receipt explanations, and the "Requested, to …" line, which was a template literal with the address concatenated in - Compose toasts and three thrown errors that surface to the reader - `Add star` / `Remove star`, which had never been in any catalogue in any language - The delete confirmation, which read `"message(s)"` — a parenthesis standing in for agreement, so every inflecting language got the wrong form regardless Also two counters that appended an `s` unless the count was one. That is English grammar written into the code; it produced correct German by coincidence and would never have been right for Russian. Those are plural forms now. **196 entries per language, across all nine**, one pull request each. Every `t()` call site is now covered in every catalogue, including the ones reached through a variable — which is how `Add star` had hidden, since a scan for `t("literal")` cannot see them. ## Still open, and known Two things you will hit if you keep going, so you do not spend time re-reporting them: 1. **The Sieve rule list summaries** — the item in your first comment. `describeRule()` builds those sentences by concatenation, so the fragments cannot be translated independently and the word order would be wrong in German regardless. It needs restructuring into whole sentences with placeholders. 2. **Recurrence descriptions** in the event editor have the same problem, plus English ordinals and weekday names. Both are on the list. They are a different shape of fix from everything above, which is why they are not in this batch. Leaving this open as you asked. Anything further you find is welcome — and if the `npm` build on your test machine ever becomes worth chasing, I am happy to look at that too.
Owner

latest has moved again — 2026.9.3-pr272. Worth a fresh pull before your next pass, because the two things I said were still outstanding are now done.

The Sieve rule list summaries you reported. describeRule() was assembling those by concatenation, which is why they could not simply be added to the catalogue: a translator handed " and " on its own cannot move it, and the fragments arrived in the order the English sentence chose. They are whole sentences with placeholders now, so the word order is yours to change. The joining is Intl.ListFormat, so an allof rule reads "A, B und C" and an anyof rule gets the disjunction German actually uses.

Recurrence descriptions in the event editor had the same shape and got the same treatment. Two things there you may have opinions about:

  • Ordinals are words now — "zweiten", not "2nd". The old code picked a suffix by arithmetic, which is English spelling rules in the source where no catalogue can reach them.
  • Weekday names come from Intl rather than a table. The old table carried short forms of "M", "T", "W", "T", "F", "S", "S" — which could never have become catalogue entries, because "T" is both Tuesday and Thursday and "S" is both Saturday and Sunday. A key cannot hold two translations. That was bad data rather than a missing translation, and no amount of translating would have fixed it.

Two more sweeps after your list also turned up things you had not reached yet: scheduled send was untranslated end to end, the read-receipt explanations, several compose toasts, and a delete confirmation that read "message(s)" — a parenthesis standing in for agreement, so every language that inflects got the wrong form no matter how complete the catalogue was. Also seven counted strings in Files and the calendar that had never been in any catalogue at all.

All nine languages are at 1248 entries. A scan of every t() call site, every value that reaches t() through a variable, and every plural form now reports nothing missing in any of them.

Still open, as you asked. Your German PR is very welcome whenever it is ready — and if anything I added reads wrong to you, that is exactly the review this language was marked Beta waiting for.

`latest` has moved again — **`2026.9.3-pr272`**. Worth a fresh pull before your next pass, because the two things I said were still outstanding are now done. **The Sieve rule list summaries** you reported. `describeRule()` was assembling those by concatenation, which is why they could not simply be added to the catalogue: a translator handed `" and "` on its own cannot move it, and the fragments arrived in the order the English sentence chose. They are whole sentences with placeholders now, so the word order is yours to change. The joining is `Intl.ListFormat`, so an `allof` rule reads "A, B und C" and an `anyof` rule gets the disjunction German actually uses. **Recurrence descriptions** in the event editor had the same shape and got the same treatment. Two things there you may have opinions about: - Ordinals are words now — "zweiten", not "2nd". The old code picked a suffix by arithmetic, which is English spelling rules in the source where no catalogue can reach them. - Weekday names come from `Intl` rather than a table. The old table carried short forms of "M", "T", "W", "T", "F", "S", "S" — which could never have become catalogue entries, because "T" is both Tuesday and Thursday and "S" is both Saturday and Sunday. A key cannot hold two translations. That was bad data rather than a missing translation, and no amount of translating would have fixed it. Two more sweeps after your list also turned up things you had not reached yet: scheduled send was untranslated end to end, the read-receipt explanations, several compose toasts, and a delete confirmation that read "message(s)" — a parenthesis standing in for agreement, so every language that inflects got the wrong form no matter how complete the catalogue was. Also seven counted strings in Files and the calendar that had never been in any catalogue at all. All nine languages are at 1248 entries. A scan of every `t()` call site, every value that reaches `t()` through a variable, and every plural form now reports nothing missing in any of them. Still open, as you asked. Your German PR is very welcome whenever it is ready — and if anything I added reads wrong to you, that is exactly the review this language was marked Beta waiting for.
Owner

latest has moved again — 2026.9.3-pr272. Worth a fresh pull before your next pass, because the two things I said were still outstanding are now done.

The Sieve rule list summaries you reported. describeRule() was assembling those by concatenation, which is why they could not simply be added to the catalogue: a translator handed " and " on its own cannot move it, and the fragments arrived in the order the English sentence chose. They are whole sentences with placeholders now, so the word order is yours to change. The joining is Intl.ListFormat, so an allof rule reads "A, B und C" and an anyof rule gets the disjunction German actually uses.

Recurrence descriptions in the event editor had the same shape and got the same treatment. Two things there you may have opinions about:

  • Ordinals are words now — "zweiten", not "2nd". The old code picked a suffix by arithmetic, which is English spelling rules in the source where no catalogue can reach them.
  • Weekday names come from Intl rather than a table. The old table carried short forms of "M", "T", "W", "T", "F", "S", "S" — which could never have become catalogue entries, because "T" is both Tuesday and Thursday and "S" is both Saturday and Sunday. A key cannot hold two translations. That was bad data rather than a missing translation, and no amount of translating would have fixed it.

Two more sweeps after your list also turned up things you had not reached yet: scheduled send was untranslated end to end, the read-receipt explanations, several compose toasts, and a delete confirmation that read "message(s)" — a parenthesis standing in for agreement, so every language that inflects got the wrong form no matter how complete the catalogue was. Also seven counted strings in Files and the calendar that had never been in any catalogue at all.

All nine languages are at 1248 entries. A scan of every t() call site, every value that reaches t() through a variable, and every plural form now reports nothing missing in any of them.

Still open, as you asked. Your German PR is very welcome whenever it is ready — and if anything I added reads wrong to you, that is exactly the review this language was marked Beta waiting for.

`latest` has moved again — **`2026.9.3-pr272`**. Worth a fresh pull before your next pass, because the two things I said were still outstanding are now done. **The Sieve rule list summaries** you reported. `describeRule()` was assembling those by concatenation, which is why they could not simply be added to the catalogue: a translator handed `" and "` on its own cannot move it, and the fragments arrived in the order the English sentence chose. They are whole sentences with placeholders now, so the word order is yours to change. The joining is `Intl.ListFormat`, so an `allof` rule reads "A, B und C" and an `anyof` rule gets the disjunction German actually uses. **Recurrence descriptions** in the event editor had the same shape and got the same treatment. Two things there you may have opinions about: - Ordinals are words now — "zweiten", not "2nd". The old code picked a suffix by arithmetic, which is English spelling rules in the source where no catalogue can reach them. - Weekday names come from `Intl` rather than a table. The old table carried short forms of "M", "T", "W", "T", "F", "S", "S" — which could never have become catalogue entries, because "T" is both Tuesday and Thursday and "S" is both Saturday and Sunday. A key cannot hold two translations. That was bad data rather than a missing translation, and no amount of translating would have fixed it. Two more sweeps after your list also turned up things you had not reached yet: scheduled send was untranslated end to end, the read-receipt explanations, several compose toasts, and a delete confirmation that read "message(s)" — a parenthesis standing in for agreement, so every language that inflects got the wrong form no matter how complete the catalogue was. Also seven counted strings in Files and the calendar that had never been in any catalogue at all. All nine languages are at 1248 entries. A scan of every `t()` call site, every value that reaches `t()` through a variable, and every plural form now reports nothing missing in any of them. Still open, as you asked. Your German PR is very welcome whenever it is ready — and if anything I added reads wrong to you, that is exactly the review this language was marked Beta waiting for.
Owner

Closing this, with thanks — it was the most productive report on the tracker, and it is worth saying why rather than just marking it done.

Everything you listed is fixed and in latest as of 2026.9.3-pr272, including the two I told you were still outstanding when you last read this: the Sieve rule list summaries and the recurrence descriptions. Both were being assembled by concatenation, which is why they could not simply be added to a catalogue — a translator handed " and " on its own cannot move it. They are whole sentences with placeholders now, joined with Intl.ListFormat, and the recurrence ordinals are words rather than arithmetic on an English suffix.

The part that made this report worth more than its list: three of the areas you flagged were not missing translations at all. The strings were in every catalogue and the code was rendering the English source instead of asking for one. "Subject": "Betreff" had been in de.ts the whole time. That is the inconsistency you spotted against the Out-of-office page, and finding it fixed nine languages at once.

Two things I am not claiming are finished, and closing this does not close either of them:

The nine translations are still marked Beta, and still unread by anyone who speaks them. That is on ROADMAP rather than here, because no amount of work on my side resolves it — a language loses the Beta mark when a speaker reads it and says so. Your German PR is very welcome whenever it suits, and if anything I added reads wrong to you, that is exactly the review the mark is waiting for.

Catalogues also do not stay complete on their own. Every feature since has carried its strings into all nine, and the check that enforces it runs against every t() call site, every value reaching t() through a variable, and every plural form. If you find English text in a translated interface again, it is a bug and worth a fresh issue.

You can reopen this yourself now. You said you could not, and you were right — read access does not include it. You are on the Triage role as of today, which does. Anything further you find, reopen or file, whichever fits.

Closing this, with thanks — it was the most productive report on the tracker, and it is worth saying why rather than just marking it done. Everything you listed is fixed and in `latest` as of **`2026.9.3-pr272`**, including the two I told you were still outstanding when you last read this: the Sieve rule list summaries and the recurrence descriptions. Both were being assembled by concatenation, which is why they could not simply be added to a catalogue — a translator handed `" and "` on its own cannot move it. They are whole sentences with placeholders now, joined with `Intl.ListFormat`, and the recurrence ordinals are words rather than arithmetic on an English suffix. The part that made this report worth more than its list: three of the areas you flagged were not missing translations at all. The strings were in every catalogue and the code was rendering the English source instead of asking for one. `"Subject": "Betreff"` had been in `de.ts` the whole time. That is the inconsistency you spotted against the Out-of-office page, and finding it fixed nine languages at once. **Two things I am not claiming are finished, and closing this does not close either of them:** The nine translations are still marked Beta, and still unread by anyone who speaks them. That is on [ROADMAP](https://github.com/Coffey-Labs/ihasmail/blob/main/ROADMAP.md) rather than here, because no amount of work on my side resolves it — a language loses the Beta mark when a speaker reads it and says so. Your German PR is very welcome whenever it suits, and if anything I added reads wrong to you, that is exactly the review the mark is waiting for. Catalogues also do not stay complete on their own. Every feature since has carried its strings into all nine, and the check that enforces it runs against every `t()` call site, every value reaching `t()` through a variable, and every plural form. If you find English text in a translated interface again, it is a bug and worth a fresh issue. **You can reopen this yourself now.** You said you could not, and you were right — read access does not include it. You are on the Triage role as of today, which does. Anything further you find, reopen or file, whichever fits.
This repo is archived. You cannot comment on issues.