I18n
GPUI Component ships component strings in crates/component/locales/ui.yml, including en, zh-HK, zh-TW, and it entries. Coverage varies by key. Your application owns its own copy, locale policy, and formatting; it can add or override component translations without copying that file.
This feature requires rust-i18n 4.2 or later.
#Add the dependency
Add rust-i18n to the application crate that owns your locale files:
[dependencies]
gpui-kit = "{{gpui_kit_version}}"
rust-i18n = "4.2"
#Create an application locale file
Create locales/ui.yml in your application crate. Put component translations under the gpui_component namespace:
_version: 2
app:
save:
en: Save
fr: Enregistrer
gpui_component:
Calendar:
week.0:
fr: Di
month.January:
fr: Janvier
DatePicker:
placeholder:
fr: Sélectionner une date
Keep your own keys outside gpui_component. That namespace must match the Rust crate name gpui-component with hyphens converted to underscores. Copy key names exactly from GPUI Component’s built-in locale file; for example, month.January is one nested key under Calendar.
#Register the extension
Initialize the application’s locales at the crate root:
rust_i18n::i18n!("locales", fallback = "en");
Register them before initializing GPUI Component:
app.run(move |cx| {
use gpui_kit::component as gpui_component;
rust_i18n::extend!(gpui_component);
gpui_kit::init(cx);
// Open windows and initialize the rest of the application.
});
Call extend! only once during application startup. The local story app uses this alias and ordering in crates/story/src/lib.rs.
#Lookup priority
The application’s translations take priority over the component’s built-in translations:
Application locales (locales/ui.yml)
│
│ key not found
▼
GPUI Component built-in locales
This deep-merge behavior means that:
- A new locale, such as
fr, can contain only the keys your application needs. - A translation with the same locale and key overrides the built-in value.
- Keys not supplied by the application continue to use the built-in value.
- Future GPUI Component translations remain available without being copied into the application.
For example, defining only gpui_component.Calendar.month.January.en changes January’s English label while all other English calendar labels still come from GPUI Component.
#The namespace applies to component lookups only
extend! changes how GPUI Component resolves its keys. It does not give the application’s own t! calls access to the built-in translations:
// Inside a GPUI Component component: application value first, built-in second.
t!("Calendar.month.February") // -> "February"
// In application code: reads the application's locale files only.
t!("gpui_component.Calendar.month.February") // -> the key, unless you defined it
Read component strings by rendering the component, not by looking the key up yourself.
For application copy, call rust_i18n::t!("app.save") from the application crate. Keep the value in a complete message key so each language can choose its own word order and punctuation. A component’s translated placeholder does not translate the surrounding application title, label, or menu item.
#Select a locale
gpui-component re-exports the locale accessors, so an application does not have to reach for rust-i18n to switch languages:
use gpui_kit::component::{locale, set_locale};
set_locale("fr");
let current = locale();
Components then resolve their translated labels using the selected locale and the priority described above.
The active locale is global state outside GPUI’s change tracking. Changing it alone does not schedule a repaint. If a view handles its own language picker, notify that view after switching:
use gpui_kit::component::set_locale;
set_locale("fr");
cx.notify();
See Context for how cx.notify() schedules a View update.
If an application-wide action changes the locale from &mut App, refresh the windows that show translations:
use gpui_kit::{App, component::set_locale};
fn select_locale(language: &str, cx: &mut App) {
set_locale(language);
cx.refresh_windows();
}
Rebuild other cached presentation explicitly. For example, the story app’s SelectLocale handler rebuilds its native and in-window menus after changing the language; their checked state is not a live translation lookup. The story’s menu labels themselves are currently fixed strings, so the language picker demonstrates component translations and selection state, not a fully localized gallery.
#Text and layout across scripts
Translation changes the string; the text system then resolves fonts, shapes glyphs, and measures the result. CJK or mixed Latin/CJK text needs a font with the actual glyphs. A font family that loads can still lack them, and a fallback face can change line breaks or control height. See Fonts and TextSystem. Check long translations, narrow buttons, input placeholders, and text at application scale on each target platform. Measure custom text with shaped layout rather than character counts.
GPUI Kit does not expose an application-wide RTL layout switch through this locale API. Setting an Arabic or Hebrew locale will select strings, but it does not automatically mirror navigation, icon direction, alignment, focus order, or pointer and keyboard behavior. Review those parts of each screen for the target language, and test bidirectional text and editing on the target platform. Treat text shaping and full interface mirroring as separate checks.
#Format data in the application
rust-i18n selects message strings here; set_locale does not format dates, numbers, currencies, units, or plurals for your domain data. Choose a locale-aware formatter appropriate to your application and pass its finished output into your views. Keep variable placement in a whole translated message so a translator can reorder it. Do not assemble a sentence from separately translated fragments or assume format!("{value}") applies locale-specific separators. A date picker can translate its own month names while a date shown elsewhere in the app still needs your formatting policy.
#Verify the integration
The repository’s story crate provides a working extension and French overrides. From the repository root, run cargo run -p gpui-component-story, open the Language menu, and select Français. Open a calendar or date picker story: its month names and date placeholder use the fr values in crates/story/locales/ui.yml. Switch back to English and check the same controls. The menu checkmark should follow the choice. You can also run cargo test -p gpui-component-story --features test-support extends_component_translations_with_story_locales to exercise the story’s translation layering test.
#Common mistakes
| Symptom | Check |
|---|---|
| Component still shows an old language | Call set_locale, then notify the owning view or refresh windows; rebuild cached menus or labels. |
| Override does not appear | Put the key under gpui_component, match the built-in spelling and locale code, and register extend! before gpui_kit::init. |
Application t! returns a key | Define that key in your application locale file; component built-ins are available to component lookups, not application lookups. |
| CJK characters are boxes or layout changes | Check font glyph coverage and fallback on the target platform; see Fonts. |
| RTL string appears but the screen is awkward | Review layout and interactions explicitly; the locale setter does not mirror the interface. |