Skip to content

feat(ui): translate the viewer with vue-i18n and let readers pick the language - #39

Merged
nekomeowww merged 3 commits into
mainfrom
nekomeowww/feat/viewer-i18n
Oct 21, 2026
Merged

nekomeowww merged 3 commits into
mainfrom
nekomeowww/feat/viewer-i18n

Conversation

@nekomeowww

Copy link
Copy Markdown
Contributor

Problem

The viewer was English only. Evals already label scores, metrics, and artifacts in several languages ({ 'en': …, 'zh-Hans': … }), but nothing let a reader pick a language.

Result

The viewer speaks English or Simplified Chinese. The reader picks one, or follows the browser. One locale drives everything on a page:

  • the UI text
  • the labels evals write
  • numbers, dates, and relative times

Comparison: d6a35fc (main) → 0bf23b4 (head revision of nekomeowww/feat/viewer-i18n). One commit.

How it works

Part What it does
vue-i18n 11 (client/src/i18n) createI18n({ legacy: false }). Messages are TypeScript modules per area: locales/{en,zh-Hans}/<area>.ts. A zh-Hans module is satisfies typeof en, so a missing key fails vue-tsc.
useLocale The vieval-viewer:locale preference is auto (the default) or a supported code. auto resolves navigator.languages with the Intl.Locale matching that localize() already used, now shared. Changing it sets <html lang>. public/scheme.js also sets <html lang> before the first paint.
Picker The Colors popover (HuePicker.vue) becomes Preferences (PreferencesMenu.vue). The language row is an option-trigger opening a DropdownMenuRadioGroup with Auto, English, and 简体中文, wrapped in AnimatedContent with floating / floating-arrow, like CompareMenu. The phone top bar keeps three buttons.
Formatting format.ts, labels.ts, caseMetrics.ts, InferencePanel, and JsonlTable format with currentLocale() instead of en-US or the browser default. localize() reads the picked locale first.
Translations Every page, panel, menu, tooltip, aria-label, and empty state, in about 650 keys. English counts are real plurals ("3 projects", not "3 project(s)"). The d3 charts redraw when the language changes. Report data (case, task, metric, and score names, ids, outputs) is not translated.

CLI changes

No CLI changes.

Report file and viewer API changes

No report file or viewer API changes. Only packages/ui/client changed: the server, @vieval/ui/contract, and the report files are untouched.

New dependency: vue-i18n ^11.4.9 (catalog; the same version airi uses).

Validation

At 0bf23b4:

  • pnpm typecheck passes, and pnpm exec eslint packages/ui is clean.
  • Client tests pass: 105 (vitest.client.config.ts, browser mode). They include resolveLocale, localize following the preference, and the language switch changing a label and <html lang>.
  • Server tests pass: 140 (vitest.server.config.ts).
  • The built viewer was checked in a browser with agent-browser: home, project, run, Scores tab, task, case, and compare pages, in zh-Hans and en, at 1440×900 and 390×844.

Follow-up

The next PR is stacked on this one. It covers:

  • a check that every t() key exists
  • an Appearance group in Preferences
  • a rounded CJK font (Resource Han Rounded SC) in place of Noto Sans SC

🤖 Generated with Claude Code

… language

The viewer was English only, while evals already label scores, metrics, and
artifacts in several languages (`{ 'en': …, 'zh-Hans': … }`).

- vue-i18n 11 with typed TypeScript messages per area (`client/src/i18n/locales/{en,zh-Hans}/<area>.ts`); a zh-Hans
  module that misses a key fails vue-tsc
- `useLocale` and the `vieval-viewer:locale` preference (`auto` or a code); `scheme.js` sets `<html lang>` before the
  first paint
- the Colors popover becomes Preferences, with a language dropdown (Auto, English, 简体中文) built on
  `option-trigger`, `AnimatedContent`, and `DropdownMenuRadioGroup`; phones keep three header buttons
- every page, panel, menu, tooltip, and empty state translated to zh-Hans, with real plurals in English
- `localize()`, numbers, dates, and relative times follow the picked language

Co-Authored-By: Claude Opus 5.5 <[email protected]>
nekomeowww and others added 2 commits October 21, 2026 15:10
Bring in #37 (phone layout and narrow cards), #38 (run header leads with the
score), and v0.10.2. Conflicts kept main's layout and behavior and applied the
translations on top; the strings #37 and #38 added (the ETA info button, Run
details) are translated in en and zh-Hans.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@github-actions

Copy link
Copy Markdown

Approval required for deploying to Cloudflare Workers (Preview) for docs.

Name Link
Waiting for approval For maintainers, approve here

@nekomeowww
nekomeowww merged commit 5d73f4a into main Oct 21, 2026
5 checks passed
@nekomeowww
nekomeowww deleted the nekomeowww/feat/viewer-i18n branch October 21, 2026 07:31
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.

1 participant