-
Notifications
You must be signed in to change notification settings - Fork 1
Internationalization
The admin SSR UI supports English (en) and Korean (ko).
Default is English to match the public-facing documentation
(CLAUDE.md §5
"public docs are English"), with per-user override.
When rendering an SSR page the language is decided in this order:
-
Per-user setting —
admin_users.language(en/ko/null). Change at Settings → General → Language. -
Server default —
i18n.defaultLanguageinserver.yml, or override viaVIBECODER_DEFAULT_LANGUAGEenvironment variable. -
Fallback —
en(built-in default).
So with no preference and no env override the UI is English. An operator
who wants Korean by default can set VIBECODER_DEFAULT_LANGUAGE=ko in
docker/.env; the per-account override still wins.
- Sign in to the admin web (
http://localhost:17880/). - Go to Settings (top nav).
- Under Language, pick one of:
- Use server default (xx) — clears the per-user override.
- English
- 한국어 (Korean)
- Click Save language, then refresh the page.
The change is per-account and persists until you change it again. Logging
out preserves the choice (the value lives in admin_users.language,
not in the session cookie).
# in docker/.env
VIBECODER_DEFAULT_LANGUAGE=koThen docker compose up -d server — the env reaches the JVM at boot and
overrides the server.yml value. No server.yml change needed; this is
the preferred path for environment-specific defaults (dev vs. prod).
# config/server.yml (or $VIBECODER_CONFIG_DIR/server.yml)
i18n:
defaultLanguage: koRestart the server (the value is read once at boot). Saved through the Settings page or hand-edited.
The bundles live in
server/i18n/MessagesEn.kt
and
MessagesKo.kt.
To add Spanish (for example):
- Create
server/i18n/MessagesEs.ktwith the same key set as the English bundle (every missing key falls back to English at render time). - Add
"es" to MessagesEs.MAPto theBUNDLESmap inMessages.kt. - Add
"es"to theSUPPORTEDset inMessages.kt. - Add an
<option value="es">Español</option>to the language dropdown inAdminTemplates.settingsPage()and a matchingsettings.general.language.option.eskey in all bundles. - Allow the value in
ConfigLoader.applyEnvOverrides(theenv → defaultLanguagevalidator).
Missing keys at render time return the key string itself (e.g. nav.home)
— this makes them easy to spot during QA rather than crashing the page.
Dot notation with category prefix:
-
common.*— reusable single words (save,cancel,signOut) -
nav.*— top-level sidebar (nav.home,nav.projects) -
settings.*— Settings page (settings.title,settings.tab.general,settings.general.language.title) -
home.*,projects.*,env.*,claude.*,mcp.*,gitint.*,error.*— per-page
When adding a new key:
-
Add to both
MessagesEn.ktandMessagesKo.kt. Missing keys log a fallback at render time; the page does not break, but QA should catch the asymmetry. -
Use
%sfor parameter substitution (String.format-style):"home.greeting" to "Welcome to %s" // call site Messages.t(lang, "home.greeting", serverName)
- The bundles are plain Kotlin
object(no resource files) so they ship with the JAR and are accessible without I/O — no first-render penalty. -
WebSession.languageis computed once per request inrequireSessionOrRedirectand passed throughAdminTemplates.shell(lang = …). -
<html lang="…">is set so screen readers, browser auto-translate, and language-aware fonts work correctly. - The dropdown POSTs to
/settings/languagewhich callsAdminUserRepository.setLanguageand redirects back to/settingswith the "saved" flash message in the new language (the redirect URL encodes the message afterresolve(newLang, defaultLang)).
| Page | i18n? |
|---|---|
| Sidebar / top nav | ✅ |
| Settings tab bar | ✅ |
| Settings → General | ✅ |
| Settings → other tabs | partial — most labels still Korean (rolling cleanup) |
| Home / Projects / Env / Claude / MCP | partial — keys reserved, in progress |
| Console / Builds / Git / Files | not yet |
The hard infrastructure is complete; remaining work is mechanical (string extraction + key mapping), rolled up a batch of pages at a time.
Q. I changed my language but the page is still in the old one.
Hit refresh — the dropdown saves on submit but the response is a redirect
to /settings, which renders in the new language. If your browser cached
an earlier page, force-reload that tab.
Q. I set VIBECODER_DEFAULT_LANGUAGE=ko in .env but new users still
see English.
Make sure you restarted the container (docker compose up -d server).
Also confirm the value is lowercase (ko, not KO) — the loader is
case-insensitive but only accepts the lowercase form internally.
Q. A label shows up as nav.something literally.
That's a missing key — please file an issue with the page name. The
fallback chain is lang → en → key, so seeing the key means both
bundles are missing the entry.
Q. Can I have different defaults per user?
That's exactly what admin_users.language provides — each user picks
their own at /settings. The server default is only for new users
who haven't chosen yet.