Files
ihasmail-inbuxa/ROADMAP.md
T
jcoffey-dev 95dcb96086 Start extraction: an i18n core, and a way to see how far it has got
The groundwork in #145 gave the app a language to serve. This gives it
something to serve, and a way to measure the distance to the languages
actually planned.

The English text is the key. `t("Archive")` looks "Archive" up and returns the
English when it is not there, which buys three things worth more than tidy
symbolic keys: no English catalogue to keep in step with the code, a missing
translation that degrades to readable English rather than to
`mail.list.archive`, and an extraction step that is wrapping a string rather
than inventing a name for it. Names are where extraction stalls, and 55
components is a lot of small naming arguments. The cost is that editing English
copy orphans its translations, which is the right way round: the copy is the
product, and a stale German sentence should fall back to the new English.

`plural()` takes forms rather than (one, other), because two forms is an
English assumption that does not survive phase two of the plan. Russian and
Ukrainian need three, and choosing between them is not a question about the
number 1. Intl.PluralRules knows the rule for every language the browser knows,
so the catalogue supplies the forms and the runtime picks; a category the
catalogue does not carry falls back to `other` rather than rendering undefined.
Interpolation is named rather than positional for the same reason -- German
moves the parts of a sentence around and means the same thing.

Catalogues are dynamically imported, so a reader who never leaves English never
downloads one, and English needs no fetch at all. `applyLang` sets the lang
attribute before kicking the load, deliberately: lang is what stops Chrome
offering to translate and should not wait on a network request to say something
it already knows.

`t()` is a plain function, not a hook, so the tree is keyed on a language
version at the root and thrown away when the catalogue changes. Making every
call site a subscriber would turn extracting a string from "wrap it" into "wrap
it and add a hook", for an event that happens about once per account.

NotificationsSettings is extracted end to end as the reference -- it covers all
four shapes, being JSX text, translated attributes, a toast, and a sentence
with a value interpolated into it.

scripts/i18n-coverage.mjs counts what is left, because ~1,000 strings across 56
files is too many to eyeball in review or carry in anyone's head. It reports 20
wrapped and 925 remaining, and it deliberately does not count punctuation and
separators as untranslated -- a floor no amount of work could reach would make
the number useless. A progress report rather than a gate: --check exits
non-zero, for once the number is low enough for that to mean something.

ROADMAP.md said translations were "English-only for now" on a page whose stated
purpose is things the answer is "no" to. It now says what is actually happening,
carries the phase order, and says why Arabic, Hebrew and Persian are on neither
list: RTL is a layout and bidi problem rather than a longer catalogue, and
shipping it as though it were the same kind of work is how an RTL build ends up
unusable with nobody saying so.
2026-08-31 09:44:49 -07:00

3.0 KiB
Raw Blame History

Roadmap / not yet

Things ihasmail does not do, and why. An issue number here says where the entry came from, not that it is tracked elsewhere — a report can be closed because the bug in it was fixed while the larger thing it asked for stays on this page. What is genuinely open lives in the issue tracker; the rest is here because the answer is "no", not "not yet".

See KNOWN-ISSUES.md for what is built but worth knowing about.

  • Sharing a mail folder. Stalwart stores the share and never delivers it; see KNOWN-ISSUES.md. Withdrawn until the server does something with it. Sharing files, calendars and address books is unaffected and works.

  • Snooze (nothing in JMAP or Stalwart supports it, and ihasmail never stores a password, so nothing could act on a mailbox while you are away)

  • Translations. In progress, and the only entry here that is "not yet" rather than "no". The groundwork shipped in #145: an interface-language setting separate from the date-and-time locale, <html lang> served from it, and the structural work that keeps a browser's own translator from rewriting the page underneath React. What is left is the part that was always the hard part — extracting every user-facing string, and having each catalogue read by somebody who speaks the language. A language is offered in Settings only once its catalogue is complete, so a half-translated build shows English and nothing else; the picker is the gate, not the calendar.

    Planned order, and it is an order rather than a wish list: German, French, Dutch, Spanish, Portuguese (Brazil) first, then Russian, Ukrainian, Chinese (Simplified), Japanese. Arabic, Hebrew and Persian are deliberately not on either list. They are right-to-left, and that is a layout and bidi problem rather than a longer catalogue — shipping them as though they were the same kind of work is how an RTL build ends up unusable and nobody says so.

  • Two-factor sign-in. Today an account with 2FA must use an app password (see Quick start), and Settings Security offers no way to switch 2FA on — only off, for an account that already has it. Supporting a TOTP code directly means implementing OAuth: Stalwart offers the authorization-code and device flows and no password grant, so ihasmail would hand sign-in to Stalwart's own login and come back with a token. That is a better security posture than the sealed password it holds now — a refresh token rather than a credential — but it replaces ihasmail's own sign-in page for those users and may need an OAuth client registered. Came out of #75, which is closed: what was reported there was a sign-in refused with nothing but "Invalid credentials", and that was fixed by saying what is actually happening and pointing at app passwords. The OAuth work it uncovered is tracked here rather than as an open issue, so there is no ticket to watch for it.