The interface's own translations break outside English: plurals chosen in JavaScript, English password advice, fixed date formats, sentences in pieces #225

Closed
opened 2026-09-15 21:23:25 +00:00 by tiagoagueda · 0 comments
Owner

These will get worse with every language #70–#72 adds. Found in the 2026-09-15 code audit.

1. "N rows ticked" chooses its plural by hand

partials/table/bulk.html:65-66 supplies only "One row ticked." and "{count} rows ticked.", and static/js/app.js:495 picks with ticked === 1 ? one : many. That is wrong for Polish, Czech, Slovak, Slovene, Croatian, Serbian, Bosnian, Ukrainian, Lithuanian, Latvian, Irish, Maltese and Welsh.

Fix: render the ngettext result for n = 1 up to the page size into a JSON data attribute and index it from the script. The page size is bounded, so this is exact without CLDR rules in JavaScript.

2. Password-strength advice is always English

account/partials/password_meter.html:6-8 loads only zxcvbn's language-en, and app.js:283,370-371 appends its warning/suggestions to the translated strength word, inside a polite live region. A French user hears "Fort · Add another word or two…" on every keystroke.

Fix: show the advice only when the interface is English, or map zxcvbn's warning keys to translated strings through data attributes. Announce only when the strength word changes.

3. 73 fixed date formats, against docs/TRANSLATING.md:192

  • 73 literal |date:"…" formats across 33 templates (22 × "j M Y", 14 × "j M Y, H:i", …); only 7 use DATE_FORMAT/DATETIME_FORMAT.
  • Everything is forced into day-month-year order with a 24-hour clock, which is wrong for Hungarian and Lithuanian now and for the 0.4.0 Asian locales.
  • Percentages are fixed as {{ share }}% (widgets/funnel.html:12, widgets/insight_counters.html:21), while French writes "42 %" and Turkish "%42".

Fix: named formats, with FORMAT_MODULE_PATH entries for a short month-name date; a test_template_lint.py rule that rejects literal date formats; a translatable %(n)s%%.

4. Sentences assembled from fragments

  • api/token_list.html:67-77: "created" + date · "expires"/"expired" + date · "last used" + date and time.
  • applications/partials/board.html:10-15: a blocktranslate ending in ";", then a separate "see them in the table", then a literal ".".
  • settings/appearance.html:52: a standalone "days" after a number field, with no plural.

Fix: whole sentences with placeholders (Created %(date)s), the <a> inside the blocktranslate with its URL as a variable, and ngettext for units.

These will get worse with every language #70–#72 adds. Found in the 2026-09-15 code audit. ## 1. "N rows ticked" chooses its plural by hand `partials/table/bulk.html:65-66` supplies only "One row ticked." and "{count} rows ticked.", and `static/js/app.js:495` picks with `ticked === 1 ? one : many`. That is wrong for Polish, Czech, Slovak, Slovene, Croatian, Serbian, Bosnian, Ukrainian, Lithuanian, Latvian, Irish, Maltese and Welsh. **Fix:** render the `ngettext` result for n = 1 up to the page size into a JSON data attribute and index it from the script. The page size is bounded, so this is exact without CLDR rules in JavaScript. ## 2. Password-strength advice is always English `account/partials/password_meter.html:6-8` loads only zxcvbn's `language-en`, and `app.js:283,370-371` appends its `warning`/`suggestions` to the translated strength word, inside a polite live region. A French user hears "Fort · Add another word or two…" on every keystroke. **Fix:** show the advice only when the interface is English, or map zxcvbn's warning keys to translated strings through data attributes. Announce only when the strength word changes. ## 3. 73 fixed date formats, against `docs/TRANSLATING.md:192` - 73 literal `|date:"…"` formats across 33 templates (22 × `"j M Y"`, 14 × `"j M Y, H:i"`, …); only 7 use `DATE_FORMAT`/`DATETIME_FORMAT`. - Everything is forced into day-month-year order with a 24-hour clock, which is wrong for Hungarian and Lithuanian now and for the 0.4.0 Asian locales. - Percentages are fixed as `{{ share }}%` (`widgets/funnel.html:12`, `widgets/insight_counters.html:21`), while French writes "42 %" and Turkish "%42". **Fix:** named formats, with `FORMAT_MODULE_PATH` entries for a short month-name date; a `test_template_lint.py` rule that rejects literal date formats; a translatable `%(n)s%%`. ## 4. Sentences assembled from fragments - `api/token_list.html:67-77`: "created" + date · "expires"/"expired" + date · "last used" + date and time. - `applications/partials/board.html:10-15`: a `blocktranslate` ending in ";", then a separate "see them in the table", then a literal ".". - `settings/appearance.html:52`: a standalone "days" after a number field, with no plural. **Fix:** whole sentences with placeholders (`Created %(date)s`), the `<a>` inside the `blocktranslate` with its URL as a variable, and `ngettext` for units.
tiagoagueda added this to the 0.4.0 milestone 2026-09-15 21:33:24 +00:00
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#225
No description provided.