Skip to content

feat(achievements): add i18n support with Chinese (zh-CN) translations - #23476

Open
kaiwer1 wants to merge 4 commits into
NousResearch:mainfrom
kaiwer1:feat/achievements-i18n
Open

feat(achievements): add i18n support with Chinese (zh-CN) translations#23476
kaiwer1 wants to merge 4 commits into
NousResearch:mainfrom
kaiwer1:feat/achievements-i18n

Conversation

@kaiwer1

@kaiwer1 kaiwer1 commented May 11, 2026

Copy link
Copy Markdown

Summary

Add full internationalization (i18n) support to the Hermes Achievements plugin. Includes:

  • Backend: Accept-Language header detection, locale-aware API routes that translate achievement names/descriptions/criteria while keeping tier/category/state in English (used for CSS class names)
  • Chinese translation (zh-CN.json): All 60 achievements translated + 80 UI string keys (category names, tier labels, filter labels, stat labels)
  • Frontend: _t(key, fallback) helper renders translated labels on cards, filters, tier legend, stats, and share buttons
  • No cache invalidation: Locale JSON loaded on every request, cache layer stores English only

Testing

  • Verified on Ubuntu 24.04 WSL2 / Windows 11 Chrome
  • All 60 achievements return Chinese when Accept-Language: zh-CN (0 English names)
  • English fallback is identical to original (no _ui field when en-US)
  • Category/tier/state fields remain English for CSS compatibility
  • Sidebar locale switcher works correctly

Add locale-based internationalization to the achievements plugin:
- New locales/zh-CN.json with 信达雅 translations for 60 achievements,
  5 tier names, ~30 metric labels, and common UI strings
- plugin_api.py: detect Accept-Language header and apply translations
  at the routing layer, keeping the cache layer in English
- Backward compatible: English requests hit the original code path
  with zero overhead, zh-CN requests apply translation overlay
- Fix unrelated bug: return clean -> return _build_pending_snapshot(now)
  (was referencing undefined variable at line ~1113)

Architecture: translations stored in JSON files under locales/, new
languages can be added without modifying plugin_api.py.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins labels May 11, 2026
…tx()

- Add _uiData module variable to store backend _ui translations
- Add PATH_TO_UI mapping table (all tx() paths -> flat _ui keys)
- Patch tx() to prioritize _uiData, fallback to SDK i18n, then en
- AchievementsPage.load()/refresh() save _uiData on API response
- Fixes PR conflict resolution that lost all client i18n code

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for carrying the dynamic achievement-content translation work forward. The remaining premise is valid: current main localizes static achievement UI through web/src/i18n/zh.ts:491, but the plugin still renders API-provided names, categories, descriptions, and criteria verbatim at plugins/hermes-achievements/dashboard/dist/index.js:523-542.

Problems

  • The PR replaces the request helper with direct fetch at plugins/hermes-achievements/dashboard/dist/index.js:164 (right side). Current main requires SDK.fetchJSON for both loopback token and gated-OAuth-cookie authentication; its inline contract says manual fetching returns 401 in gated mode at plugins/hermes-achievements/dashboard/dist/index.js:51-60.
  • The route-level localization changes have no regression coverage. Existing integration tests in tests/plugins/test_achievements_plugin.py cover scanning and cache behavior, not locale headers or localized endpoint payloads.

Suggested changes

  • Preserve SDK.fetchJSON and pass the locale header through its options.
  • Keep shared web/src/i18n for static chrome and restrict the overlay to dynamic achievement data.
  • Add English-fallback and zh-CN route tests, including stable CSS/state identifiers.

Automated hermes-sweeper review.

// Forward the plugin's locale to the backend so achievement names,
// descriptions, criteria, and tier labels are translated server-side.
if (_pluginLocale && _pluginLocale !== "en" && !headers.has("Accept-Language")) {
headers.set("Accept-Language", _pluginLocale);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please keep SDK.fetchJSON as the transport and pass the locale header through its options. Current main documents at this helper that manual fetch with window.__HERMES_SESSION_TOKEN__ 401s in gated OAuth mode because it omits the cookie-based authentication path.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 13, 2026
@teknium1 teknium1 added the area/i18n Localization, locales, translations label Jul 19, 2026

@GottZ GottZ left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This was generated by AI during triage.

Summary

Three PRs address Chinese localization for the achievements plugin: #18747 and #22595 implemented overlapping static UI translations and were superseded by merged #22914, while #23476 targets the remaining API-provided achievement names, descriptions, and criteria. #23476 does not actually localize API-provided categories because _apply_locale() copies only name and description, and it currently introduces authentication and English-output regressions.

Related pull requests

  • #18747 [closed] duplicate — (+354/-48) — superseded reference: Added inline zh-CN UI translations plus backend-generated Chinese names, descriptions, categories, and criteria. It remains relevant as a reference implementation, but contributor review confirms it was superseded by merged #22914, which incorporated its translation work into broader 16-language coverage.
  • #22595 [closed] related — (+1023/-259) — superseded reference: Moved achievements static UI strings into shared typed web i18n catalogs and also carried unrelated Kanban localization changes. It remains relevant for its shared-i18n approach, but contributor review confirms it was superseded by merged #22914 and should not be reopened.
  • #23476 related — (+752/-31) — keep open, not merge-ready: Adds locale JSON and server-side localization for API-provided achievement names, descriptions, and criteria, but not categories despite category translations being present. This aligns with the keep_open review on #23476, whose blockers remain: the diff replaces SDK.fetchJSON with direct fetch, breaking gated OAuth-cookie authentication; adds no locale endpoint regression tests; and regresses English threshold criteria by emitting Chinese punctuation/text (。等级阶梯) on the English path.

Duplicates

#18747 and the achievements portion of #22595 substantially overlap on Chinese static achievements UI localization and were both superseded by merged #22914; #23476 is not a full duplicate because it addresses the still-untranslated dynamic API content.

Suggested consolidation

Keep #23476 open as the consolidation target, but do not merge it yet. Preserve SDK.fetchJSON while forwarding Accept-Language, add endpoint-level locale and English-fallback tests, restore fully English punctuation and wording in English criteria, and either implement category localization consistently or remove that claim; #18747 and #22595 should remain closed as superseded by #22914.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    subgraph Dup18747 ["PRs duplicating each other"]
        P18747["PR #18747 (closed)"]
        P22595["PR #22595 (closed)"]
        P23476["PR #23476 (open)"]
    end
    class P18747 closed
    class P22595 closed
    class P23476 open
    class P23476 target
    click P18747 "https://github.com/NousResearch/hermes-agent/pull/18747"
    click P22595 "https://github.com/NousResearch/hermes-agent/pull/22595"
    click P23476 "https://github.com/NousResearch/hermes-agent/pull/23476"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed or no verify verdict yet (state tag in the node label).

Cross-PR triage: Reviewed 3 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 200 kB of PR diffs, 4 kB of issue/PR text, 3 kB of discussion (3 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

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

Labels

area/i18n Localization, locales, translations comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants