Add a display-language setting, so the extension can speak a language other than the browser's #11

Closed
opened 2026-09-30 15:19:46 +00:00 by tiagoagueda · 0 comments
Owner

The extension speaks the language the browser is set to, on purpose. The README's “Languages” section says “there is no setting for it”, and the header of src/lib/i18n.js says “There is nothing to pick and nothing stored.” Every sentence comes through t() from the browser's i18n.getMessage. This issue asks for a display-language setting that overrides the browser, which reverses that decision.

Why it is worth reversing

Besides preference (Postulo read in one language, the browser run in another), some shipped translations cannot be reached otherwise. Chrome on Windows, macOS and Linux has no interface in bs, cy, eu, gl, hy, is, ka, mk or sq, and none in ga, lb or mt on any platform (Chromium's build/config/locales.gni); its extension i18n documentation also leaves 13 of the 40 folders off its list: “If you use an unsupported locale, Google Chrome ignores it.” Their speakers get the browser's language instead. Unverified in a real browser, as is whether Chromium still serves a folder it ignores to runtime.getURL; see #10.

What the extension should do

The setting. One more storage.local key, empty by default, shown on the settings page as a list: “Follow the browser” first, then the shipped languages, each named in itself (“Deutsch”, “Gaeilge”, “српски”) with its own lang attribute for screen readers, as the core's picker does. On the 400 px settings page (src/style.css) it fits between the address-and-token form and “Testing and troubleshooting”, and, like the testing switch, takes effect when changed. A stored language no longer shipped is passed over and left as it is.

Loading the chosen language. No browser lets an extension set its own language; the API proposed in w3c/webextensions#258 is shipped nowhere. So with a language chosen, the extension loads _locales/<code>/messages.json through runtime.getURL and resolves messages by getMessage's rules: named $placeholder$ tokens from the message's placeholders, then $1 to $9 from the substitutions, $$ for a dollar sign, names regardless of case. lang and dir still come from the catalogue's languageTag and direction.

  • t() is synchronous and each page calls localise() first in main(), so the catalogue must be loaded before a page localises, without the HTML's English showing first.
  • The background (a service worker without a DOM in Chromium, an event page in Firefox) must load it too. It imports i18n.js through src/lib/local.js but composes no sentence today; the tooltip below, and any tooltip #4 sets, would be the first.
  • Open pages follow a change: the settings page at once, the list page on the storage change (src/captures.js already watches offline, instance and token), without losing an edit in progress.

Dates. The popup's “captured before” date uses toLocaleDateString() with no locale (src/popup.js); it should use t("languageTag"), as the list page's Intl.DateTimeFormat does.

What cannot follow. The browser fills the manifest's __MSG_ strings in its own language: the description, the tooltip (action.default_title) and the shortcut's description. The tooltip can follow through action.setTitle, set again at start-up (browsers forget it on restart) and on a change. Only Firefox desktop can re-word the shortcut's description, with commands.update.

Decisions for this repository

  • Postulo's sentences. src/lib/api.js sets no Accept-Language, so the 403 and 422 sentences Postulo writes, which the extension shows as they come, follow whatever the browser sends for that header, not necessarily the extension's language. Recommended: send the tag of the catalogue in use on every API call; the alternative sends it only when a language is chosen. The core words a capture's notification in the account's language, not this header's (postulo/postulo#223). Postulo should match en and fr to its en-gb and fr-fr; unchecked.
  • Where the names come from. The catalogues hold no language name. Either a key in every catalogue holding its own name, or a table written by the #10 script from the core's NATIVE_NAMES (recommended: one source, the names Postulo's picker shows).
  • Saying they are drafts. Every translation but English is an unreviewed draft, which the core's picker says by grouping (“Awaiting review by a speaker”). One hint under the picker is recommended.

Every new sentence (the label, “Follow the browser”, any hint) must be in all 40 _locales in the same commit with identical placeholders, or test/i18n.test.mjs fails: each costs 40 translations.

Needs from the server

Not filed on postulo/postulo. A “Same as my Postulo” option needs the account's interface language: Profile.language exists, but GET /me gives only name, owner, scopes, expires_at and last_used_at, and no other call returns it. Limiting the list to what one instance offers needs an endpoint listing its languages; there is none. What works today: the list offers the shipped catalogues, and the person picks the language they read Postulo in.

Done when

  • The settings page offers “Follow the browser” and every shipped language, each named in itself; choosing one switches the popup, the list, the settings page and the tooltip, and survives a restart.
  • With “Follow the browser”, nothing changes from today.
  • A Chrome user can choose Gaeilge, Malti or Lëtzebuergesch and read the extension in it.
  • The popup's dates follow the choice, and Postulo's refusals too if the decision above is taken.
  • The README's “Languages” section is rewritten and its “Layout” rows for src/options.* and src/lib/i18n.js updated. PRIVACY.md's “It keeps” list gains the setting, and “What it sends” says requests carry the language in Accept-Language, if they do.

From the list of extension improvements of 2026-09-30.

The extension speaks the language the browser is set to, on purpose. The README's “Languages” section says “there is no setting for it”, and the header of `src/lib/i18n.js` says “There is nothing to pick and nothing stored.” Every sentence comes through `t()` from the browser's `i18n.getMessage`. This issue asks for a display-language setting that overrides the browser, which reverses that decision. ## Why it is worth reversing Besides preference (Postulo read in one language, the browser run in another), some shipped translations cannot be reached otherwise. Chrome on Windows, macOS and Linux has no interface in `bs`, `cy`, `eu`, `gl`, `hy`, `is`, `ka`, `mk` or `sq`, and none in `ga`, `lb` or `mt` on any platform (Chromium's `build/config/locales.gni`); its extension i18n documentation also leaves 13 of the 40 folders off its list: “If you use an unsupported locale, Google Chrome ignores it.” Their speakers get the browser's language instead. Unverified in a real browser, as is whether Chromium still serves a folder it ignores to `runtime.getURL`; see #10. ## What the extension should do **The setting.** One more `storage.local` key, empty by default, shown on the settings page as a list: “Follow the browser” first, then the shipped languages, each named in itself (“Deutsch”, “Gaeilge”, “српски”) with its own `lang` attribute for screen readers, as the core's picker does. On the 400 px settings page (`src/style.css`) it fits between the address-and-token form and “Testing and troubleshooting”, and, like the testing switch, takes effect when changed. A stored language no longer shipped is passed over and left as it is. **Loading the chosen language.** No browser lets an extension set its own language; the API proposed in w3c/webextensions#258 is shipped nowhere. So with a language chosen, the extension loads `_locales/<code>/messages.json` through `runtime.getURL` and resolves messages by `getMessage`'s rules: named `$placeholder$` tokens from the message's `placeholders`, then `$1` to `$9` from the substitutions, `$$` for a dollar sign, names regardless of case. `lang` and `dir` still come from the catalogue's `languageTag` and `direction`. - `t()` is synchronous and each page calls `localise()` first in `main()`, so the catalogue must be loaded before a page localises, without the HTML's English showing first. - The background (a service worker without a DOM in Chromium, an event page in Firefox) must load it too. It imports `i18n.js` through `src/lib/local.js` but composes no sentence today; the tooltip below, and any tooltip #4 sets, would be the first. - Open pages follow a change: the settings page at once, the list page on the storage change (`src/captures.js` already watches `offline`, `instance` and `token`), without losing an edit in progress. **Dates.** The popup's “captured before” date uses `toLocaleDateString()` with no locale (`src/popup.js`); it should use `t("languageTag")`, as the list page's `Intl.DateTimeFormat` does. **What cannot follow.** The browser fills the manifest's `__MSG_` strings in its own language: the description, the tooltip (`action.default_title`) and the shortcut's description. The tooltip can follow through `action.setTitle`, set again at start-up (browsers forget it on restart) and on a change. Only Firefox desktop can re-word the shortcut's description, with `commands.update`. ## Decisions for this repository - **Postulo's sentences.** `src/lib/api.js` sets no `Accept-Language`, so the 403 and 422 sentences Postulo writes, which the extension shows as they come, follow whatever the browser sends for that header, not necessarily the extension's language. Recommended: send the tag of the catalogue in use on every API call; the alternative sends it only when a language is chosen. The core words a capture's notification in the account's language, not this header's (postulo/postulo#223). Postulo should match `en` and `fr` to its `en-gb` and `fr-fr`; unchecked. - **Where the names come from.** The catalogues hold no language name. Either a key in every catalogue holding its own name, or a table written by the #10 script from the core's `NATIVE_NAMES` (recommended: one source, the names Postulo's picker shows). - **Saying they are drafts.** Every translation but English is an unreviewed draft, which the core's picker says by grouping (“Awaiting review by a speaker”). One hint under the picker is recommended. Every new sentence (the label, “Follow the browser”, any hint) must be in all 40 `_locales` in the same commit with identical placeholders, or `test/i18n.test.mjs` fails: each costs 40 translations. ## Needs from the server Not filed on postulo/postulo. A “Same as my Postulo” option needs the account's interface language: `Profile.language` exists, but `GET /me` gives only `name`, `owner`, `scopes`, `expires_at` and `last_used_at`, and no other call returns it. Limiting the list to what one instance offers needs an endpoint listing its languages; there is none. What works today: the list offers the shipped catalogues, and the person picks the language they read Postulo in. ## Done when - The settings page offers “Follow the browser” and every shipped language, each named in itself; choosing one switches the popup, the list, the settings page and the tooltip, and survives a restart. - With “Follow the browser”, nothing changes from today. - A Chrome user can choose Gaeilge, Malti or Lëtzebuergesch and read the extension in it. - The popup's dates follow the choice, and Postulo's refusals too if the decision above is taken. - The README's “Languages” section is rewritten and its “Layout” rows for `src/options.*` and `src/lib/i18n.js` updated. PRIVACY.md's “It keeps” list gains the setting, and “What it sends” says requests carry the language in `Accept-Language`, if they do. From the list of extension improvements of 2026-09-30.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo-chromium#11
No description provided.