Files
iptvnator/tools/i18n/README.md
T
4grayandClaude Opus 5.5 afd7a5f331 fix(i18n): translate source cleanup, source health and diagnostics strings (#1884)
* fix(i18n): translate source cleanup, source health and diagnostics strings

Translate every English-identical SOURCE_CLEANUP, SOURCE_HEALTH and
PLAYBACK_DIAGNOSTICS value in all 18 locales, plus the external playback
dock statuses (the same strings as the diagnostic ones), the EPG source
list strings, and leftover English in hu, ko and el. The identical-English
baseline drops 973 entries (2106 -> 1133) and gains none.

Add tools/i18n/check-duplicates.mjs (pnpm run i18n:duplicates), a report
of English strings defined under several keys whose translations differ
per locale, and align drifted wordings: 101 diverging groups -> 65.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(i18n): call cleaned-up zh/zhtw sources invalid, not unactivated

The cleanup dialog lists expired and disabled accounts too, so 未激活 /
未啟用 ("not activated") misexplained why a source is offered for deletion.
Use 失效 ("no longer valid") in the title and the confirmed group heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(i18n): one zh/zhtw wording for copying the stream URL

The playback diagnostics button said 复制 URL / 複製 URL while the
channel details dialog (new on master) says 复制流 URL / 複製串流網址
for the same action; use the details dialog wording in both places.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:59:50 +02:00

106 lines
4.2 KiB
Markdown

# i18n fill-missing tool
One-shot helper for bringing every locale file in `apps/web/src/assets/i18n/`
to full coverage of `en.json`.
## How it works
`en.json` is the source of truth. For every other locale, the script:
1. Walks `en.json` in order (top-level + nested).
2. For each leaf key:
- Keeps the locale's existing translation if present.
- Otherwise pulls the translation from `tools/i18n/patches/<lang>.json`
(a flat object keyed by dotted path).
- If neither exists, leaves the key absent and exits non-zero.
3. Writes the merged tree back to the locale file with 4-space indent and a
trailing newline.
Existing translations are never overwritten.
## Workflow
```bash
# 1. Generate one missing-strings dump per locale (flat dotted-path → EN string)
node tools/i18n/fill-missing.mjs --emit-missing
# → tools/i18n/missing/<lang>.json
# 2. Translate each missing/<lang>.json into the equivalent patch file
# in tools/i18n/patches/<lang>.json (same shape: { dotted.path: translated }).
# 3. Merge patches into the locale files
node tools/i18n/fill-missing.mjs
# 4. Verify
node -e "
const fs=require('fs');
const en=JSON.parse(fs.readFileSync('apps/web/src/assets/i18n/en.json','utf8'));
const collect=(d,p='')=>Object.entries(d).flatMap(([k,v])=>v&&typeof v==='object'?collect(v,p?p+'.'+k:k):[p?p+'.'+k:k]);
const enKeys=new Set(collect(en));
for(const f of fs.readdirSync('apps/web/src/assets/i18n').filter(x=>x.endsWith('.json')&&x!=='en.json')){
const k=new Set(collect(JSON.parse(fs.readFileSync('apps/web/src/assets/i18n/'+f,'utf8'))));
console.log(f,'missing:',[...enKeys].filter(x=>!k.has(x)).length,'extra:',[...k].filter(x=>!enKeys.has(x)).length);
}"
```
For a non-mutating CI/agent check, run:
```bash
pnpm run i18n:check
```
The check fails on missing or extra keys against `en.json`, and on values that
are identical to English unless `identical-en-baseline.json` lists that exact
English text for the locale and key. Do not copy English into a locale as a
placeholder: translate it. The contract, including when an entry stops
covering a key, is in the
[validation map](../../docs/architecture/validation-map.md#i18n).
Baseline entries that are no longer identical are reported, not fatal. After
translating baselined values, or for a value that is legitimately the same in a
language (a brand, a technical name, a loanword), rewrite the baseline and
review every added entry in the diff:
```bash
pnpm run i18n:baseline:update
```
`pnpm run i18n:check` also runs `check-usage.mjs`, which fails on a key the
renderer uses but `en.json` lacks (ngx-translate would show the raw key). It
scans production `.ts` and `.html` files under `apps/web/src`,
`apps/remote-control-web/src` and `libs` for piped keys (including the
branches of a parenthesised operand), translate calls and
dotted literals in an `en.json` namespace; run it alone with
`pnpm run i18n:usage`. Keys built entirely at runtime are not checked.
The checkers' own tests run with `pnpm run i18n:test`; CI runs both through
`pnpm run i18n:validate`. For a full audit that ignores the baseline, run:
```bash
node tools/i18n/check-drift.mjs --fail-on-identical
```
`pnpm run i18n:duplicates` lists English strings that `en.json` defines under
several keys while a locale translates those keys differently, such as one
"Close" button saying "Afsluiten" and another "Sluiten". Add `--verbose` for
each locale's variants and keys, or `--max <n>` to fail above n groups. It is
a report, not a CI gate: a group can be legitimate when context needs another
grammatical form. When aligning, change the drifted values rather than
removing keys.
## Changing existing translations
`fill-missing.mjs` never overwrites, so rewording an existing value or paying
down baseline debt means editing the value in the locale file directly (keep
the key order, 4-space indent and trailing newline), then running
`pnpm run i18n:baseline:update` and checking that the baseline diff only
removes entries.
## Translation rules
- Preserve `{{interpolation}}` placeholders verbatim.
- Preserve any inline HTML tags.
- Match the locale's existing punctuation, casing, and terminology.
- Keep brand names (IPTVnator, Xtream, Stalker, M3U, EPG, MPV, VLC) untranslated.
- Short UI strings stay short — no length blow-up.