Internationalization Integration Guide
RTC Agent builds a complete internationalization system on @lit/localize, supporting runtime language switching without page reloads.
Architecture Overview
Section titled “Architecture Overview”Initialization
Section titled “Initialization”Language Priority
Section titled “Language Priority”The component determines the initial language at startup according to the following priority:
- HTML attribute: The
langattribute set on<rtc-agent lang="en-US">(highest priority) - localStorage: The user’s choice saved under the key
rtc-agent-locale - Browser language:
navigator.language, used if it’s in the supported list - Default: Falls back to the source language
zh-CN
initLocale()
Section titled “initLocale()”Call initLocale() when the component starts to initialize the locale:
import { initLocale } from '@rtc-agent/component';
// Call at application startupawait initLocale();Initialization logic:
- Check if there’s a saved language in
localStorage(key:rtc-agent-locale) - Check if browser language
navigator.languageis in the supported list - Fall back to source language
zh-CN - Sync
document.documentElement.langattribute (screen reader pronunciation rules)
Configuration
Section titled “Configuration”lit-localize.json defines language configuration:
{ "sourceLocale": "zh-CN", "targetLocales": ["en-US"], "tsConfig": "tsconfig.json", "output": { "mode": "runtime", "localeCodesModule": "src/.generated/locale-codes.ts" }}Switching Languages
Section titled “Switching Languages”switchLocale()
Section titled “switchLocale()”Switch languages at runtime:
import { switchLocale, type SupportedLocale } from '@rtc-agent/component';
// Switch to Englishawait switchLocale('en-US');
// Switch back to Chineseawait switchLocale('zh-CN');switchLocale() behavior:
| Step | Operation |
|---|---|
| 1 | Call @lit/localize’s setLocale() to load language pack |
| 2 | Save to localStorage (key: rtc-agent-locale) |
| 3 | Update document.documentElement.lang attribute |
| 4 | All @localized() components automatically re-render |
getLocale()
Section titled “getLocale()”Get the currently active locale (synchronous):
import { getLocale } from '@rtc-agent/component';
const current = getLocale(); // 'zh-CN' | 'en-US'persistLocale()
Section titled “persistLocale()”Persist locale preference to localStorage without triggering a locale switch. Useful for saving user choice before the component is mounted:
import { persistLocale } from '@rtc-agent/component';
persistLocale('en-US');Supported Languages
Section titled “Supported Languages”| Language Code | Language | Type |
|---|---|---|
zh-CN | Simplified Chinese | Source language |
en-US | English | Target language |
// Type-safetype SupportedLocale = 'zh-CN' | 'en-US';
// Type guardimport { isValidLocale } from '@rtc-agent/component';if (isValidLocale(userInput)) { await switchLocale(userInput);}Agent-Facing Messages
Section titled “Agent-Facing Messages”It is important to note that the component contains two categories of text: UI text (controlled by the i18n system, switches with switchLocale()) and Agent-Facing messages (always in English, unaffected by the UI language).
Agent-Facing messages are text written to the virtual file system for the LLM to read, for example:
- Parameter validation error messages (error prompts referenced in
/functions/INDEX.md) - Auto-generated function documentation (
AGENT.md,/functions/*.md) - Scenario index (
/scenarios/INDEX.md)
These messages are uniformly in English because Agent prompts and tool call protocols are in English, and mixing in other languages would cause inconsistent LLM parsing. Host applications do not need to translate these messages during integration.
Using in Components
Section titled “Using in Components”@localized() Decorator
Section titled “@localized() Decorator”Use the @localized() decorator to mark components that need to respond to language changes:
import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import { localized, msg } from '@lit/localize';import { consume } from '@lit/context';import { localeContext, type LocaleContextValue } from '@rtc-agent/component';
@localized()@customElement('my-component')export class MyComponent extends LitElement { @consume({ context: localeContext, subscribe: true }) @state() private _localeCtx: LocaleContextValue = { /* ... */ };
render() { // Reference locale to ensure re-render on language switch void this._localeCtx.locale;
return html` <div>${msg('Welcome to RTC Agent')}</div> <p>${msg('This is an internationalized component')}</p> `; }}msg() Function
Section titled “msg() Function”msg() marks text that needs translation:
import { msg, str } from '@lit/localize';
// Simple textmsg('Hello World')
// Text with variablesconst name = 'User';msg(str`Hello, ${name}!`)
// Text with HTML (needs escaping)msg(html`Click <a href="/help">${msg('Help')}</a>`)localeContext
Section titled “localeContext”Get the locale context via @lit/context:
import { consume } from '@lit/context';import { localeContext, type LocaleContextValue } from '@rtc-agent/component';
@consume({ context: localeContext, subscribe: true })@state()private _localeCtx: LocaleContextValue = { locale: 'zh-CN', setLocale: async () => {}, locales: ['zh-CN', 'en-US'],};Adding Translations
Section titled “Adding Translations”Translation Workflow
Section titled “Translation Workflow”Step 1: Mark Text
Section titled “Step 1: Mark Text”Wrap text that needs translation with msg() in components:
render() { return html` <h1>${msg('Settings')}</h1> <p>${msg('Choose interface language')}</p> `;}Step 2: Extract Translations
Section titled “Step 2: Extract Translations”Run the extract command to generate XLIFF files:
npm run localize:extractThis generates translation entries in xliff/en-US.xlf:
<trans-unit id="s8ef06a6080e58381"> <source>Settings</source> <target state="new">Settings</target> <note from="lit-localize">Settings panel title</note></trans-unit>Step 3: Translate Text
Section titled “Step 3: Translate Text”Edit xliff/en-US.xlf, replacing <target> with the translated text:
<trans-unit id="s8ef06a6080e58381"> <source>Settings</source> <target>Settings</target></trans-unit>Step 4: Build Language Packs
Section titled “Step 4: Build Language Packs”Run the build command to generate compiled language packs:
npm run localize:buildThis generates optimized translation mappings in src/locales/en-US.ts.
Font Size System
Section titled “Font Size System”RTC Agent provides a proportional scaling font size system based on --rtc-font-size-user.
Base Font Size
Section titled “Base Font Size”rtc-agent { /* Set base font size (12-24px) */ --rtc-font-size-user: 16px;}Font Size Scale
Section titled “Font Size Scale”All font size tokens scale proportionally to the base font size:
| Token | Formula | 12px | 14px (default) | 16px | 18px | 20px |
|---|---|---|---|---|---|---|
--rtc-font-size-xs | base * 0.857 | 10px | 12px | 14px | 15px | 17px |
--rtc-font-size-sm | base * 0.929 | 11px | 13px | 15px | 17px | 19px |
--rtc-font-size-base | base | 12px | 14px | 16px | 18px | 20px |
--rtc-font-size-md | base * 1.143 | 14px | 16px | 18px | 21px | 23px |
--rtc-font-size-lg | base * 1.286 | 15px | 18px | 21px | 23px | 26px |
--rtc-font-size-xl | base * 1.429 | 17px | 20px | 23px | 26px | 29px |
--rtc-font-size-2xl | base * 1.714 | 21px | 24px | 27px | 31px | 34px |
Settings Panel Integration
Section titled “Settings Panel Integration”The settings panel provides a font size adjustment control:
// In rtc-settings-layoutprivate _renderAppearance() { return html` <div class="setting-row"> <label for="font-size-input">${msg('Font Size')}</label> <input type="number" id="font-size-input" min="12" max="24" step="1" .value=${String(this._settingsCtx.state.appearance.fontSize)} @change=${(e: Event) => { const size = Number((e.target as HTMLInputElement).value); this._updateSetting('appearance', { fontSize: size }); }} /> <span>px</span> </div> `;}DOM Side Effects
Section titled “DOM Side Effects”SettingsController automatically applies the font size to the document root:
// SettingsController._applyFontSize()document.documentElement.style.setProperty( '--rtc-font-size-user', `${clamped}px`);This ensures all components in Shadow DOM can inherit the font size setting.
Best Practices
Section titled “Best Practices”| Practice | Description |
|---|---|
✅ Always use msg() | All user-visible text should be wrapped in msg() |
✅ Reference locale | Reference this._localeCtx.locale in render() to ensure re-rendering |
✅ Use @localized() | Components that need to respond to language changes must add this decorator |
| ✅ Type safety | Use SupportedLocale type and isValidLocale() guard |
| ❌ Don’t hardcode | Avoid hardcoding user-visible text outside msg() |
❌ Don’t call _setLocale directly | Use switchLocale() to ensure persistence and DOM sync |
Next Steps
Section titled “Next Steps”- Component API — Learn about the full component API
- Settings System — Learn about the global settings system