Skip to content

feat(i18n): complete the Russian translation for 0.10.1 - #317

Open
xJaroslav69 wants to merge 21 commits into
tonhowtf:mainfrom
xJaroslav69:ru-translate
Open

xJaroslav69 wants to merge 21 commits into
tonhowtf:mainfrom
xJaroslav69:ru-translate

Conversation

@xJaroslav69

@xJaroslav69 xJaroslav69 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What

Brings src/lib/i18n/ru.json to the current main (0.10.1): 9,260 / 9,260 keys — 0 missing, 0 extra, values-only, keys.ts untouched, the diff is one file.

  • Merged upstream/main (0.10.1) and resolved ru.json in favour of this PR's side, as you asked.
  • The 68 strings 0.10.1 added are translated: the workspace hub (Chat, Agents, Activity, Configure, Tasks, Routines, usage and performance, the limits-strip monitor), the appearance section (design.*), job filters, loop summaries, observatory states, skill actions, model-provider hints and the Help entry point.
  • Seven strings whose English source changed in 0.10.1 follow the new wording: home.hero_title → «Что хотите скачать?», llm.tab.roster / llm.roster.title ("Agent team") → «Команда агентов», llm.conv.empty_title / llm.conv.empty_body, llm.mcp.reg_install ("Configure") → «Настроить», llm.mcp.reg_reinstall ("Reconfigure") → «Перенастроить».
  • 97.9% of values are Cyrillic. The 178 that still match English are intentional: brands and products, formats and codecs, the Anki stock type names Basic/Cloze, protocol and field tokens (stdio, HTTP, URL, Id, LLM, CLI), the Lao endonym ລາວ, XP/FPS/CPU labels and two strings that are placeholders only.
  • The lo.json line stays gone — main has that key now.

Three stale strings fixed earlier in this branch

key en 0.9.2 en 0.10.0 ru now
study.music.albums_count {{count}} albums {{n}} album(s) Альбомов: {{n}}
study.read.page_of {{current}} of {{total}} p. {{cur}}/{{total}} стр. {{cur}}/{{total}}
study.library.roots Library roots Roots Корни

Terminology

Kept in step with the accepted translation and the interface: appearance is «внешний вид» (as in settings.appearance.title), the Routines entry of the workspace navigation reads «Циклы» — the name of the page it opens — and counters follow the repo convention («Род. падеж: {{count}}», _one keys keep the singular).

Checks

node scripts/generate-i18n-keys.js --strict → 9,260 keys, all 11 other locales in sync (exit 0)
node scripts/sync-locales.mjs --check       → ru.json: +0 missing, -0 stale
placeholder / HTML-tag parity lint          → 0 divergences (both brace styles)

Diff: src/lib/i18n/ru.json only — 5,253 insertions / 5,253 deletions.

Notes for maintainers

  • Unrelated heads-up from running the suite on Windows: with core.autocrlf=true, scripts/world-bench.mjs arrives with CRLF and vitest cannot transform src/lib/world/bench/compare.test.ts (29 tests never run), and src/lib/stores/limits-monitor.test.ts strips imports with /^import .*;\n/gm, which never matches \r\n, so the child process dies with "Identifier 'invoke' has already been declared". Both are one-line regex/eol fixes.
  • The same stale-value class still exists for zh, ja, fr, it, es, el, pt, zh-TW. Happy to send that cleanup as a separate PR.

@tonhowtf

Copy link
Copy Markdown
Owner

Verified rather than trusted, and every claim in the description holds up. This is the most carefully prepared locale PR this repo has received.

What I checked against main:

Claim Result
6,250 / 6,250 keys ✅ exactly, and 0 keys added, 0 removed — genuinely values-only
~97% Cyrillic ✅ 6,107 / 6,250 = 97.7% (was 3,836 before this PR)
Placeholders preserved 1:1 0 divergences across both {{double}} and {single} styles
keys.ts untouched ✅ the diff is one file
~126 intentionally left as-is ✅ 129 values still match English — brands, codecs, format names

I also diffed the HTML tags inside values: 0 divergences. That is the failure mode that quietly breaks rendering, and it is clean.

The contrast worth naming: a locale PR was closed here recently because the file was ~95% English under a translated label. Yours is the inverse — 2,271 strings actually moved into Russian, the plural forms follow the genitive convention rather than the «2 инструмента» calque, and you left brands and codecs alone instead of transliterating them. That is the judgement part, and it is the part a script cannot check.

One thing before I merge, and it is on my side, not yours. A batch of new keys for the next release is currently in flight, which means ru.json is about to grow past the 6,250 this PR targets. I checked whether that collides with your work: it does not — the incoming keys and the keys you translated are disjoint sets, zero overlap. Your translations are not at risk of being overwritten by a value someone else chose.

But the merge order matters, because both rewrite the same file. I want to sequence this so that your 2,271 strings cannot be lost to a bad conflict resolution. I will handle that and merge — nothing for you to do, and please do not rebase preemptively, since that would just move the problem.

The Weblate link in the README being dead is a real catch, thank you. Leave the README to me; it was rewritten for 0.9.1 and I would rather keep it in one hand.

Comment thread src/lib/i18n/ru.json Outdated
"scope_channel": "Этот канал",
"scope_guild": "Весь сервер",
"run": "Найти",
"filters_hint": "Фильтры: от:имя · содержит:файл|изображение|ссылка · до:2026-01-31 · после:2026-01-01",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Localized filters silently fail

The hint now tells Russian users to enter от:, содержит:, до:, and после:, but the search parser only recognizes from:, has:, before:, and after: with the English values file, image, and link. Following the localized hint therefore treats the filters as ordinary search words, so the requested filtering silently fails.

Suggested change
"filters_hint": "Фильтры: от:имя · содержит:файл|изображение|ссылка · до:2026-01-31 · после:2026-01-01",
"filters_hint": "Фильтры: from:name · has:file|image|link · before:2026-01-31 · after:2026-01-01",
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/i18n/ru.json
Line: 6521

Comment:
**Localized filters silently fail**

The hint now tells Russian users to enter `от:`, `содержит:`, `до:`, and `после:`, but the search parser only recognizes `from:`, `has:`, `before:`, and `after:` with the English values `file`, `image`, and `link`. Following the localized hint therefore treats the filters as ordinary search words, so the requested filtering silently fails.

```suggestion
      "filters_hint": "Фильтры: from:name · has:file|image|link · before:2026-01-31 · after:2026-01-01",
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

The hint told Russian users to type `от:`, `содержит:`, `до:` and `после:`, but SearchPanel only parses `from:`, `has:`, `before:` and `after:` (with the values `file`, `image`, `link`). The localized operators were pushed into the free-text query instead, so the filters silently did nothing while the hint said they worked.

Keep the operators in English and translate only the "Filters:" prefix, matching every other locale, which left this hint untranslated.
@xJaroslav69

Copy link
Copy Markdown
Contributor Author

Thanks for the review — the filter finding was a real bug, and a nasty one because it failed silently.

What was wrong. SearchPanel.parse() only recognises from:, has:, before: and after: (with file / image / link as the values for has). Any other token falls through to the free-text query. The Russian hint told users to type от:имя, содержит:файл, до: and после: — so following the hint pushed those tokens into the search terms and the filter did nothing at all, with no error shown.

Fixed in 6a272f9 by keeping the operators in English and translating only the Filters: prefix, which is what every other locale already does — Russian was the only one that had translated the operators themselves:

"filters_hint": "Фильтры: from:имя · has:file|image|link · before:2026-01-31 · after:2026-01-01"

Verified: check:i18n --strict → 6255 keys, all 11 locales in sync; diff is one line in ru.json, keys.ts untouched.

@tonhowtf tonhowtf left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked key by key against main: this is not a blind rewrite — only 6 already-translated strings change (the stale ones you list), 3,844 translated keys are untouched, and 6a272f9a keeps the OmniDisc search operators in English. Good work.

It conflicts with main because 0.9.2 added 957 keys (48 new tools) that this branch doesn't have. Please rebase, run node scripts/sync-locales.mjs, and either translate the new keys or leave them as the English fallback the script fills in — both are fine. Then it merges.

@xJaroslav69

Copy link
Copy Markdown
Contributor Author

Updated to 0.9.2 — thanks for the pointer.

What changed

  • Merged upstream/main into the branch. The ru.json conflict was resolved in favour of this PR's translations, so the 2,277 strings it already carried are untouched.
  • node scripts/sync-locales.mjs reported ru.json: +959 missing (filled with English). All 959 are now translated: 957 are the new tools.* strings (48 new tools + their categories), plus settings.appearance.lang_lo and settings.download.download_attachments_desc.
  • ru.json now holds every key of en.json: 7,209 / 7,209 — 0 missing, 0 extra, with 0 placeholder divergences (both {{double}} and {single} styles) and 0 HTML-tag divergences. 97.7% of values are Cyrillic; 152 stay identical to English (brands, codecs, format names). keys.ts is untouched — the diff is still one file.

Two notes:

  • settings.appearance.lang_lo keeps the native «ລາວ», matching «Русский» / 「日本語」 / «Português» in the same list.
  • I merged upstream/main rather than rebasing, to avoid rewriting the branch history; happy to rebase if you would rather have a linear history.

Unrelated heads-up

main is currently one key out of sync: lo.json is missing settings.download.download_attachments_desc, so node scripts/generate-i18n-keys.js --strict fails with [lo.json] missing: 1 on main as well. I left it alone to keep this PR to a single file — say the word if you would like it as a separate one-line PR.

node scripts/sync-locales.mjs --check   → ru.json: +0 missing -0 stale
node scripts/generate-i18n-keys.js --strict → only [lo.json] missing: 1 (pre-existing on main)

`main` ships `lo.json` without `settings.download.download_attachments_desc`,
so `pnpm check:i18n` aborts with `[lo.json] missing: 1` on every branch, this
one included — that is what the three red checks were (frontend and both
smoke jobs).

`sync-locales.mjs` fills a missing key with the English value, which is what
it does here: one key, English fallback, no behaviour change. Translating it
properly is left to a Lao speaker.

Not strictly part of the Russian translation, but the locale gate cannot pass
for this PR — or for `main` — until that key exists.
@xJaroslav69

Copy link
Copy Markdown
Contributor Author

One extra file, and here is why.

main currently ships lo.json without settings.download.download_attachments_desc, so pnpm check:i18n aborts with [lo.json] missing: 1 on every branch — this one included. That is exactly what the three red checks were: frontend (the "Locales in sync with en.json" step) and both smoke jobs, which build the frontend and hit the same gate.

sync-locales.mjs filled that key with the English value, the same way it does for any locale. That is the whole change: one key, English fallback, no behaviour change — a Lao speaker can translate it properly later.

I know this PR was supposed to stay on ru.json. Happy to move the line into its own PR if you would rather keep this one to a single file — but until the key exists somewhere on main, that check stays red for everyone, not just here.

@xJaroslav69 xJaroslav69 changed the title feat(i18n): complete the Russian translation for 0.9.1 feat(i18n): complete the Russian translation for 0.9.2 Sep 16, 2026
@tonhowtf

Copy link
Copy Markdown
Owner

Re-checked against main at 0.10.0, key by key:

  • 7,209 keys in your ru.json, 0 extra, 0 placeholder divergences, 0 HTML-tag divergences, 152 values identical to English (brands, codecs, formats) — the numbers in your last comment hold;
  • main has not changed the value of any Russian key since your merge base, so nothing of yours is superseded.

The review point is resolved and this is approved in substance. It conflicts again for the same reason as last time, and that one is on me: 0.10.0 shipped while this was open and added 829 new keys (the agents/LLM section). Sorry for making you chase main twice.

To finish: merge main, resolve ru.json in favour of your side, run node scripts/sync-locales.mjs (it fills the 829 new keys with English). Translating them is welcome but not required for the merge — I'd rather land what you have now and take the new strings as a follow-up PR than have this go stale a third time. The lo.json line can be dropped; main has that key now.

Please let #318 land first since both touch ru.json; I'll merge this one right behind it.

upstream 0.10.0 changed the English source of three keys the Russian file
had already translated, so the translations still carried the old tokens:

- study.music.albums_count: {{count}} -> {{n}}
- study.read.page_of: {{current}} -> {{cur}}
- study.library.roots: "Library roots" -> "Roots"

Without this the UI would render a literal {{count}} / {{current}}.
Verified: 0 placeholder divergences, 0 HTML-tag divergences against en.json.
Completes ru.json for 0.10.0: 9,192 / 9,192 keys, 0 missing, 0 extra,
0 placeholder divergences and 0 HTML-tag divergences (both `{{double}}`
and `{single}` styles), 97.9% of values Cyrillic.

Covers the sections the release introduced: agents/LLM (accounts, jobs,
loops, prune, rules, workspace, models, MCP, skills, observatory, limits
strip, wire probe, quota), the World (house, yard, activity, settings,
HUD), pet/omni, profile, onboarding, and the new Anki, notes, music,
player, read and Telegram surfaces.

Terminology follows the accepted 0.9.x translation and the repo's plural
convention: counters read "Род. падеж: {{count}}" («Инструментов: {{count}}»),
`_one` keys keep the singular form.

Verified:
  node scripts/generate-i18n-keys.js --strict -> 9,192 keys, all 11 locales in sync
  node scripts/sync-locales.mjs --check       -> ru.json: +0 missing, -0 stale
  placeholder / HTML-tag parity lint          -> 0 divergences
@xJaroslav69 xJaroslav69 changed the title feat(i18n): complete the Russian translation for 0.9.2 feat(i18n): complete the Russian translation for 0.10.0 Sep 20, 2026
@xJaroslav69

Copy link
Copy Markdown
Contributor Author

Updated to 0.10.0 — CI is 8/8 green and the PR is mergeable again. This one can go right behind #318, as you suggested.

  • Merged main, resolved ru.json in favour of this PR's side, then translated all 1,980 strings 0.10.0 added (agents/LLM, the World, pet/omni, profile, onboarding, the new Anki/notes/music/player/read/Telegram surfaces).
  • ru.json is now 9,192 / 9,192 keys, 0 missing, 0 extra, 97.9% Cyrillic, keys.ts untouched — the diff is still one file.
  • The three strings whose English source changed in 0.10.0 are fixed (study.music.albums_count {{count}}{{n}}, study.read.page_of {{current}}{{cur}}, study.library.roots shortened), so nothing renders a literal placeholder.
  • The lo.json line is gone — main has that key now.
node scripts/generate-i18n-keys.js --strict → 9,192 keys, all 11 other locales in sync (exit 0)
node scripts/sync-locales.mjs --check       → ru.json: +0 missing, -0 stale
placeholder / HTML-tag parity lint          → 0 divergences (both brace styles)

One unrelated heads-up, since it costs every Windows checkout a test file: with core.autocrlf=true, scripts/world-bench.mjs arrives with CRLF and vitest cannot transform src/lib/world/bench/compare.test.ts (SyntaxError: Invalid or unexpected token, its 29 tests never run). Converting that one file to LF makes the suite green; a *.mjs text eol=lf line in .gitattributes would fix it for good.

…tale ones

Brings ru.json to 0.10.1: 9,260 / 9,260 keys, 0 missing, 0 extra,
0 placeholder divergences, 0 HTML-tag divergences, 97.9% Cyrillic.

New in 0.10.1 and translated here: the workspace hub (Chat, Agents,
Activity, Configure, Tasks, Routines, usage and performance, the limits
strip monitor), the appearance section (design.*), job filters, loop
summaries, observatory states, skill actions, model-provider hints and
the Help entry point.

Seven strings whose English source changed in 0.10.1 follow the new
wording: home.hero_title, llm.tab.roster / llm.roster.title ("Agent
team" -> «Команда агентов»), llm.conv.empty_title, llm.conv.empty_body,
llm.mcp.reg_install ("Configure" -> «Настроить») and
llm.mcp.reg_reinstall ("Reconfigure" -> «Перенастроить»).

Terminology kept in step with the accepted translation: appearance is
«внешний вид» (as in settings.appearance.title), and the Routines entry
of the workspace navigation reads «Циклы», the name of the page it opens.

Verified:
  node scripts/generate-i18n-keys.js --strict -> 9,260 keys, all 11 locales in sync
  node scripts/sync-locales.mjs --check       -> ru.json: +0 missing, -0 stale
  placeholder / HTML-tag parity lint          -> 0 divergences
@xJaroslav69

Copy link
Copy Markdown
Contributor Author

Updated to 0.10.1 — CI is green and the PR is mergeable again. This one can go right behind #318.

  • Merged main, resolved ru.json in favour of this PR's side, then translated all 68 strings 0.10.1 added (the workspace hub, the appearance section, job filters, loop summaries, observatory states, skill actions, model-provider hints, the Help entry point).
  • Seven strings whose English source changed in 0.10.1 follow the new wording: home.hero_title → «Что хотите скачать?», llm.tab.roster / llm.roster.title ("Agent team") → «Команда агентов», llm.conv.empty_title, llm.conv.empty_body, llm.mcp.reg_install ("Configure") → «Настроить», llm.mcp.reg_reinstall ("Reconfigure") → «Перенастроить».
  • ru.json is now 9,260 / 9,260 keys, 0 missing, 0 extra, 97.9% Cyrillic, keys.ts untouched — the diff is still one file.
node scripts/generate-i18n-keys.js --strict → 9,260 keys, all 11 other locales in sync (exit 0)
node scripts/sync-locales.mjs --check       → ru.json: +0 missing, -0 stale
placeholder / HTML-tag parity lint          → 0 divergences (both brace styles)

Two small things you may want to pick up while this sits in the queue:

  1. The MCP tool count. README.md and llms.txt say "49 tools" for the MCP server, but omniget-core/src/core/llm/tool_table.rs builds 56 and its own test asserts 56 (mcp.rs::tools() returns the whole table). The Russian README I am preparing says 56; the English one is still off by seven.
  2. Windows CRLF tests. With core.autocrlf=true, scripts/world-bench.mjs arrives with CRLF and vitest cannot transform src/lib/world/bench/compare.test.ts (29 tests never run), and src/lib/stores/limits-monitor.test.ts strips imports with /^import .*;\n/gm, which never matches a CRLF checkout, so the child process fails with "Identifier 'invoke' has already been declared". Both are one-line fixes (\r?\n, or a *.mjs text eol=lf line).

@xJaroslav69 xJaroslav69 changed the title feat(i18n): complete the Russian translation for 0.10.0 feat(i18n): complete the Russian translation for 0.10.1 Sep 21, 2026
"от:", "содержит:", "до:" and "после:" are not words to translate: the
search parser only knows `from:`, `has:`, `before:` and `after:` with the
English values `file`, `image` and `link`. A Russian user following the
localized hint therefore gets the filters treated as ordinary search words
and the filtering silently does nothing. Every other locale keeps those
tokens in English for the same reason.
@tonhowtf

Copy link
Copy Markdown
Owner

#318 is merged, so this one is next — and, as predicted, it conflicts again, but only in one hunk: the last object of ru.json, where #318 appended the tray block right after the design.* keys you translated. Everything else merges clean.

Resolution: merge main, keep your side of the design.* values (Внешний вид рабочей области …), and add the tray block after them, with the Russian strings from #318:

  "tray": {
    "quit": "Выход",
    "downloads_none": "Нет активных загрузок",
    "downloads_active": "Активных загрузок: {{count}}",
    "tooltip_active": "OmniGet — активных: {{count}}",
    "tooltip_speed": "OmniGet — активных: {{count}} · {{speed}}"
  }

Then node scripts/sync-locales.mjs --check and pnpm check:i18n (main is at 9,267 keys after #345 and #309 added settings.download.remove_saved_dir and the downloads.status_bar.* block — the script fills those with English, translate them if you like, not required). Push and I merge right away. Sorry for the third round; this is the last one, nothing else touching ru.json is queued.

ru.json was the only conflict, in two positional hunks:

- downloads.* — kept this branch's Russian values and took main's new
  downloads.status_bar.* block (already Russian on main);
- design.* — kept this branch's Russian values and appended main's Russian
  tray block that landed with tonhowtf#318.

The other 17 keys main added since 0.10.1 (status_bar, tray, telegram,
remove_saved_dir) came in already translated.
Found while re-reading the whole file after the merge:

- onboarding.agents_desc held an older English sentence verbatim — it now
  translates the current source, including the three starter agents and the
  LLM > Roster breadcrumb;
- tools.common.file was "File" although it shows up as a tab label, a row
  label and a section heading;
- downloads.history_empty and settings.advanced.concurrent_fragments_desc
  kept only the first half of the English text.

check:i18n --strict: 9,279 keys, 11 locales in sync; 0 placeholder and
0 HTML-tag divergences; keys.ts untouched.
1k / 5k / 10k rather than 1000 / 5000 / 10000: the badges and the progress
list are narrow, and a four-digit number is more likely to wrap. Matches the
English source, which already writes "1k XP" and "10k XP".
@xJaroslav69

Copy link
Copy Markdown
Contributor Author

Updated — everything you asked for is in, and CI is green on the head commit.

The conflict, resolved exactly as you described: kept this branch's side for the design.* values and appended the Russian tray block from #318. The 19 keys main added since our merge base came in already translated, except settings.download.remove_saved_dir, which was still "Remove" — it is «Убрать из списка» now (the ✕ only drops the shortcut from the picker, it does not delete the folder).

Four values were still in English — I found them re-reading the file after the merge, and they are fixed in a separate commit so the resolution stays readable:

  • onboarding.agents_desc held an older English sentence verbatim, so it also lost the part about the three starter agents and the roster path;
  • tools.common.file was "File" although it shows up as a tab label, a row label and a section heading;
  • downloads.history_empty and settings.advanced.concurrent_fragments_desc kept only the first half of the English text.

XP thresholds stay shortened: 1k / 5k / 10k rather than 1000 / 5000 / 10000 — the badges and the progress list are narrow, and a four-digit number is the first thing to wrap.

node scripts/generate-i18n-keys.js --strict → 9,279 keys, all 11 other locales in sync (exit 0)
node scripts/sync-locales.mjs --check       → ru.json: +0 missing, -0 stale
placeholder / HTML-tag parity lint          → 0 divergences (both brace styles)

ru.json is 9,279 / 9,279 keys, 97.9% Cyrillic, 178 values identical to English (brands, codecs, formats, language names). keys.ts is still untouched — the diff is one file. CI is 8/8 and GitHub reports the branch mergeable with no conflicts, so this is ready whenever you are. Thanks for the exact resolution recipe on the third round — it was the right call.

Two follow-ups, both as separate PRs — nothing for you to do now:

  1. Keys that future releases add to en.json I will send as their own PR instead of letting this branch go stale again, as you suggested.
  2. One more pocket of untranslated UI, and this one is not a locale-file issue: src/components/llm/surface-copy.ts and src/components/llm/accounts/ConnectionWizard.svelte carry hardcoded en / pt dictionaries — derived(locale, v => v?.startsWith("pt") ? pt : en) and an inline copy(pt, en) in the markup — so every locale except Portuguese reads English there: "How it works", the task and routine hints, the chat suggestions, and the whole "Connect my AI" wizard. I have a draft that moves those 93 strings into en.json / pt.json (your Portuguese values move across unchanged) plus ru.json, and replaces the hardcoded reads with $t. I would send it as its own PR, starting with surface-copy.ts since that is the single file behind thirteen views. Say the word if you would rather take that one yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants