Internationalization (i18n)¶
The console supports English (en) and Chinese (zh) via next-intl v4. URLs do not include a locale prefix (localePrefix: "never").
Configuration¶
Routing (i18n/routing.ts)¶
export const routing = defineRouting({
locales: ["zh", "en"],
defaultLocale: "zh",
localePrefix: "never",
});
Locale resolved by middleware / proxy.ts from cookie or Accept-Language.
Request config (i18n/request.ts)¶
Loads messages at request time:
- Read
messages/${locale}.jsonviareadFileSync(avoids Turbopack dynamic import hang) - Merge all
messages/fragments/*.${locale}.jsonwithdeepMerge - Return
{ locale, messages }toNextIntlClientProvider
Message file layout¶
frontend/messages/
├── en.json # Shared / shell strings
├── zh.json
└── fragments/
├── qa.en.json # namespace: "qa"
├── qa.zh.json
├── ingest.en.json
├── kb.en.json
├── health.en.json
└── …
Fragment convention¶
Each fragment file is namespaced at the top level:
deepMerge recurses into existing keys — fragments extend base messages without clobbering siblings.
Adding a new page namespace¶
- Create
messages/fragments/mypage.en.json+.zh.json - No change to
request.ts— auto-discovered by suffix scan - Use
useTranslations("mypage")in components
Usage in components¶
Client components¶
import { useTranslations } from "next-intl";
function Composer() {
const t = useTranslations("qa");
return <span>{t("composer.placeholder")}</span>;
}
Nested namespaces¶
Server components¶
Locale in App Router¶
app/[locale]/layout.tsx:
generateStaticParams()→ both locales pre-renderedsetRequestLocale(locale)in layout for static optimization- Invalid locale →
notFound()
<html lang={locale}> set for accessibility and SEO.
Fragment inventory (Q&A example)¶
messages/fragments/qa.en.json covers:
| Namespace segment | UI area |
|---|---|
qa.composer |
Prompt, modes, attachments |
qa.sources |
Evidence rail |
qa.rail |
Structure / preview tabs |
qa.history |
Session drawer |
qa.scope |
Scope filter drawer |
qa.error |
Toast messages |
Parallel qa.zh.json must keep key parity — missing keys fall back to key path in dev.
Date / number formatting¶
Use next-intl formatters when displaying locale-sensitive values:
import { useFormatter } from "next-intl";
const format = useFormatter();
format.dateTime(new Date(session.updated_at));
History grouping (history-utils.ts) uses calendar math in UTC — UI labels come from qa.history.* keys.
API content language¶
Backend LLM answers follow model defaults (Chinese/English mixed possible). UI strings are fully localized; citation content comes from corpus language, not UI locale.
Testing locales¶
Switch locale via:
- User preference store (
prefsStore) if wired - Cookie set by middleware
- Browser
Accept-Languageon first visit
Related documentation¶
- App structure —
[locale]segment - Q&A module — primary fragment consumer
- Frontend index — stack table