Skip to content

feat: add full Russian (ru-RU) localization - #605

Closed
nesquena-hermes wants to merge 4 commits into
masterfrom
add-full-russian-ru-RU-localization
Closed

feat: add full Russian (ru-RU) localization#605
nesquena-hermes wants to merge 4 commits into
masterfrom
add-full-russian-ru-RU-localization

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Full Russian (ru-RU) localization for Hermes WebUI.

All credit to @DrMaks22 for the original implementation — this PR contains their complete Russian locale work with fixes applied to keep source files in English.

What's included

  • static/i18n.js: Full ru: locale block with all keys translated to Russian (correct — locale data lives here)
  • api/routes.py: Russian login page locale strings (correct — server-side login page rendering)
  • tests/test_russian_locale.py: Full test suite for the Russian locale
  • tests/test_login_locale.py: Russian login page test
  • tests/test_provider_mismatch.py, tests/test_sprint36.py: Updated to count locales dynamically

Fixes applied during review

Source files were cleaned to remove hardcoded Russian strings that belonged in i18n.js:

  • static/sessions.js: Cyrillic JS variable names (startOfСегодняstartOfToday, startOfВчераstartOfYesterday) and all hardcoded Russian in session menus, project picker, pin/unpin, toast messages
  • static/boot.js: Workspace panel titles, composer placeholder, profile label
  • static/ui.js: Context window labels, update banner, model chip, refresh/update strings
  • static/messages.js: Context strings sent to agent (must stay in English)
  • static/panels.js: Error message
  • static/index.html: Restored to current master (branch was based on older version; Russian title/placeholder/label attributes reverted; data-i18n attribute additions preserved)
  • tests/test_sprint9.py + tests/test_onboarding_static.py: Updated path assertions from /static/ to static/ (PR feat: support subpath mount via reverse proxy (Tailscale Serve, nginx, etc) #588 made paths relative)
  • tests/test_russian_locale.py: Fixed login_title assertion to match actual translation

Tests

1214 passed, 0 failed.

LMA Studio and others added 4 commits April 15, 2026 02:17
…8n.js only

The locale translation belongs in static/i18n.js (ru: block) — that is correct
and untouched. Source files must stay in English so they work for all users
regardless of locale setting.

Files reverted to English originals:
- static/boot.js: workspace panel titles, composer placeholder, profile label
- static/sessions.js: Cyrillic variable names (startOfСегодня→startOfToday,
  startOfВчера→startOfYesterday), all hardcoded Russian strings in session
  actions, project picker, toast messages
- static/ui.js: context window labels, update banner, model chip fallback,
  refresh/update strings
- static/messages.js: context strings sent to agent (Workspace:, Model:, Files:)
- static/panels.js: error message
- static/index.html: restored to master — all Russian title/placeholder/label
  attributes reverted to English; Russian language option added correctly via
  the i18n.js _label field (no HTML change needed); legitimate data-i18n
  attribute additions preserved

Test fixes:
- tests/test_sprint9.py: update /static/ → static/ assertions (relative paths
  from PR #588 subpath mount support)
- tests/test_onboarding_static.py: same relative path fix
- tests/test_russian_locale.py: fix login_title assertion to match actual
  translation in i18n.js (Вход not Войти)
@nesquena

Copy link
Copy Markdown
Owner

Independent End-to-End Review — PR #605

First-pass review (no prior reviewer comments).

TL;DR

Code looks clean, tests pass — but the branch needs a rebase to resolve conflicts in 3 files before it can merge. Tagged hold so I'm assuming this is intentionally on pause; the review below covers what I see, but I haven't pushed any rebase commits.

Code overview

Substantial localization PR — adds a full Russian (ru-RU) locale to static/i18n.js covering:

  • Composer / button labels
  • Settings tabs (general, appearance, profiles, etc.)
  • Slash command descriptions and runtime messages
  • Approval / clarify card prompts
  • Error messages and toasts
  • Profile creation form

Review observations

Strengths:

  • Translations look idiomatic from a quick read; uses Cyrillic native characters rather than \u escapes (cleaner and more maintainable)
  • Locale entry follows the same shape as existing en/es/de/zh blocks
  • Cleanup commits removed hardcoded Russian from sessions.js and panels.js so the locale is the single source of truth
  • 4-commit progression shows iterative refinement (initial implementation → form localization → cleanup of stray Russian in source → final cleanup of pin/project actions)

Coordination concerns (3 file conflicts with current master):

These should be straightforward to resolve — the PR adds a new language entry to LOCALES, doesn't restructure existing entries, so most of the conflict resolution is just re-applying the new keys after the master changes settle.

Tests ✅

  • 1172 passed, 52 skipped, 0 failed (tests on the branch as-is, before rebase)
  • 43 i18n / russian / locale tests pass

Security audit ✅

Pure data addition to a JS object. No new endpoints, no user input handling, no XSS surface. The translation strings render through the existing t() lookup which is used in template literals already wrapped in esc() where needed.

Recommendation

Hold for rebase, then merge. The hold label suggests this is already in the queue. When ready to land:

  1. Rebase onto current master to resolve the 3 file conflicts
  2. Re-run CI
  3. Add CHANGELOG entry / version bump
  4. Merge

Happy to push a rebase commit if you want me to take a stab at it — let me know if that's helpful or if you'd rather coordinate with the other in-flight PRs first.

@DrMaks22

Copy link
Copy Markdown
Contributor

Thank you for the detailed review.

Understood regarding the 3 conflicting files. Since the PR is currently on hold and there are overlapping changes in master, I will wait for the coordination window to clear before doing a clean rebase.

I can rebase and push an updated branch once the related work lands, or earlier if you prefer.

@nesquena

Copy link
Copy Markdown
Owner

Second-pass Independent Review — PR #605

Thank you @DrMaks22 for the original Russian localization work, and thanks for the iteration cleaning up hardcoded strings. This is a substantial contribution — covering the translatable surface in one PR is a lot of painstaking work, and it's appreciated.

Verdict

Approved on content. Still blocked on rebase. Translation quality and i18n plumbing are solid — only outstanding item is the stale branch against current master (now at v0.50.90).

What's good

Translation quality (structural read):

  • Full key parity — 389/389 en keys present in ru (matches es exactly; de is behind at 182, which is pre-existing, not this PR's problem)
  • No duplicate keys
  • Zero missing translations — ru now leads all non-English locales on coverage
  • Cyrillic native characters throughout — easier to review and maintain than \u escapes

Pluralization done right:

  • session_time_minutes_ago, session_time_hours_ago, session_time_days_ago, profile_skill_count, active_conversation_meta — all correctly implement Slavic three-form plurals (минута / минуты / минут, час / часа / часов, etc.) keyed on mod10/mod100. The placeholder set diverges from English (${word} instead of ${n === 1 ? '' : 's'}) exactly as it should. These are the only keys whose template-literal placeholders differ from en, and every divergence is linguistically justified.

Locale registration — fully data-driven:

  • Auto-picked up by resolveLocale() / resolvePreferredLocale() in static/i18n.js (iterates Object.keys(LOCALES))
  • Auto-populated in the settings language picker in static/panels.js (iterates Object.entries(LOCALES) with _label)
  • _speech: 'ru-RU' set for speech recognition
  • Login page (api/routes.py _LOGIN_LOCALE["ru"]) covers all 7 required keys. Decoded translations read naturally: Войти / Введите пароль, чтобы продолжить / Пароль / Неверный пароль / Не удалось подключиться.

Test infrastructure upgrades are a nice lift:

  • tests/test_provider_mismatch.py and tests/test_sprint36.py now count locales dynamically via regex — future locale additions won't require test edits. Cleaner than hard-coding >= 5.
  • tests/test_russian_locale.py uses a proper brace-balanced parser with string-quote tracking to extract locale blocks — that's the right way to do this without false matches inside string values.

Source-file cleanliness:

  • static/index.html on the branch has zero Cyrillic in titles/placeholders — confirmed by scan. All UI strings flow through data-i18n, data-i18n-title, or data-i18n-placeholder.
  • New profile-form keys (profile_name_placeholder, profile_clone_label, profile_base_url_placeholder, profile_api_key_placeholder, profile_default_label) added to en, ru, es, zh — consistent coverage across locales.
  • panels.js swap (default) hardcode → t('profile_default_label') — clean extraction.

Security audit ✅

  • No <script>, <iframe>, javascript:, or event-handler injection in any of the 389 Russian translation values (grep-clean)
  • Translation values rendered through t() + esc() at call sites (e.g. panels.js ${esc(t('error_prefix'))}${esc(e.message)})
  • No new endpoints, no user-input plumbing — pure data addition + wiring
  • api/routes.py _LOGIN_LOCALE uses pre-escaped \u literals, renders through the existing login template path

Tests ✅

  • Local: 1172 passed, 52 skipped, 0 failed (branch-as-is, before rebase)
  • tests/test_russian_locale.py: 4 tests pass (existence, representative translations, key-parity, no duplicates)
  • tests/test_login_locale.py::test_login_page_uses_russian_for_ru: passes

Still blocked — rebase conflicts

Tried a rebase locally against current origin/master. Conflicts in 2 files (tests/test_onboarding_static.py and tests/test_sprint9.py flagged earlier now auto-merge cleanly):

  1. static/index.html — multiple hunks, all trivial. Master has moved on (base href for subpath mount, system theme auto-detect, workspacePanel open/closed dataset, clarify-card restructure, Excel/Word mime types, composerWorkspaceLabel/composerModelLabel empty on master, Prism theme id). The PR's actual change here is only data-i18n / data-i18n-title / data-i18n-placeholder attribute additions — everything else in the diff is master catching up. Resolution: take master's version and re-apply the data-i18n* attributes on top.

  2. static/ui.js — 1 conflict hunk around line 246. Master already removed the hardcoded Russian 'Пользовательский ID модели' fallback in favor of 'Custom model ID'. PR has a leftover dd.appendChild(_custSep); line that's now placed elsewhere in master. Resolution: take master's version verbatim — PR's change is already superseded upstream.

I did not push a rebase — given the back-and-forth above about waiting for the coordination window, and that this is tagged hold, I didn't want to step on an intentional queue position. If you'd like me to push the rebase, say the word and I'll take a pass at it; both conflicts are mechanical.

Next steps

  1. Rebase onto current master (v0.50.90) — 2 mechanical conflicts in index.html + ui.js
  2. Re-run CI
  3. Add CHANGELOG + version bump
  4. Merge — content is ready

Thank you again @DrMaks22 — once the rebase lands, Russian-speaking users will have a fully localized WebUI. Excellent work.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator Author

Superseded by PR #713 which is a clean rebase onto v0.50.92 with all locale key conflicts resolved and 1490 tests passing. Original translation work by @DrMaks22 fully preserved with Co-authored-by trailers.

nesquena-hermes added a commit that referenced this pull request Apr 19, 2026
Full Russian locale — 389/389 English keys, Slavic plural forms, native Cyrillic. Rebased from PR #605 with rebase artifacts fixed. Login page Russian added to api/routes.py. Credits: @DrMaks22 (translation), @renheqiang (PR #605 author).

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
@nesquena-hermes
nesquena-hermes deleted the add-full-russian-ru-RU-localization branch April 21, 2026 02:38
JKJameson pushed a commit to JKJameson/hermes-webui that referenced this pull request Apr 25, 2026
Full Russian locale — 389/389 English keys, Slavic plural forms, native Cyrillic. Rebased from PR nesquena#605 with rebase artifacts fixed. Login page Russian added to api/routes.py. Credits: @DrMaks22 (translation), @renheqiang (PR nesquena#605 author).

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
Full Russian locale — 389/389 English keys, Slavic plural forms, native Cyrillic. Rebased from PR nesquena#605 with rebase artifacts fixed. Login page Russian added to api/routes.py. Credits: @DrMaks22 (translation), @renheqiang (PR nesquena#605 author).

Co-authored-by: DrMaks22 <DrMaks22@users.noreply.github.com>
Co-authored-by: renheqiang <renheqiang@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants