I18n
I18n renders a translated message from the active locale bundle. Variables are passed as props, and translated slot placeholders such as <link/> are replaced with matching named XMLUI slots.
Use it for visible text that should react to locale changes, ICU message variables, plural rules, select branches, or translated inline markup.
Key features:
- Reactive locale switching: Re-renders automatically when
App.setLocale()changes the active locale. - ICU variables: Pass message variables as additional props, such as
name,count, orstatus. - Plural and select messages: Keep grammar and state-specific wording inside the locale bundle.
- Inline markup slots: Replace translated placeholders such as
<termsLink/>with named XMLUI children. - Safe missing-key fallback: Renders the translation key when no message is available.
Use I18n when translated text appears directly in markup. Use App.translate()
inside expressions, event handlers, or other script contexts.
<App
localeBundles="{{
en: {
'greeting': 'Hello!'
}
}}">
<I18n key="greeting" />
</App><App
localeBundles="{{
en: {
'greeting': 'Hello!'
}
}}">
<I18n key="greeting" />
</App><App
var.userName="Ada"
localeBundles="{{
en: {
'welcome': 'Welcome, {name}!'
},
de: {
'welcome': 'Willkommen, {name}!'
}
}}">
<VStack gap="$space-3">
<HStack>
<Button label="English" onClick="App.setLocale('en')" />
<Button label="Deutsch" onClick="App.setLocale('de')" />
</HStack>
<I18n key="welcome" name="{userName}" />
</VStack>
</App><App
var.userName="Ada"
localeBundles="{{
en: {
'welcome': 'Welcome, {name}!'
},
de: {
'welcome': 'Willkommen, {name}!'
}
}}">
<VStack gap="$space-3">
<HStack>
<Button label="English" onClick="App.setLocale('en')" />
<Button label="Deutsch" onClick="App.setLocale('de')" />
</HStack>
<I18n key="welcome" name="{userName}" />
</VStack>
</App><App
var.count="{1}"
var.status="ready"
localeBundles="{{
en: {
'cart.items': '{count, plural, one {# item} other {# items}} in cart',
'order.status': '{status, select, ready {Ready to ship} delayed ' +
'{Delayed by weather} other {Status unknown}}'
},
de: {
'cart.items': '{count, plural, one {# Artikel} other ' +
'{# Artikel}} im Warenkorb',
'order.status': '{status, select, ready {Versandbereit} delayed ' +
'{Durch Wetter verzogert} other {Status unbekannt}}'
}
}}">
<VStack gap="$space-3">
<HStack>
<Button label="One item" onClick="count = 1" />
<Button label="Five items" onClick="count = 5" />
<Button label="Ready" onClick="status = 'ready'" />
<Button label="Delayed" onClick="status = 'delayed'" />
<Button label="English" onClick="App.setLocale('en')" />
<Button label="Deutsch" onClick="App.setLocale('de')" />
</HStack>
<VStack gap="$space-2">
<HStack gap="$space-1">
<Text>Cart:</Text>
<I18n key="cart.items" count="{count}" />
</HStack>
<HStack gap="$space-1">
<Text>Status:</Text>
<I18n key="order.status" status="{status}" />
</HStack>
</VStack>
</VStack>
</App><App
var.count="{1}"
var.status="ready"
localeBundles="{{
en: {
'cart.items': '{count, plural, one {# item} other {# items}} in cart',
'order.status': '{status, select, ready {Ready to ship} delayed ' +
'{Delayed by weather} other {Status unknown}}'
},
de: {
'cart.items': '{count, plural, one {# Artikel} other ' +
'{# Artikel}} im Warenkorb',
'order.status': '{status, select, ready {Versandbereit} delayed ' +
'{Durch Wetter verzogert} other {Status unbekannt}}'
}
}}">
<VStack gap="$space-3">
<HStack>
<Button label="One item" onClick="count = 1" />
<Button label="Five items" onClick="count = 5" />
<Button label="Ready" onClick="status = 'ready'" />
<Button label="Delayed" onClick="status = 'delayed'" />
<Button label="English" onClick="App.setLocale('en')" />
<Button label="Deutsch" onClick="App.setLocale('de')" />
</HStack>
<VStack gap="$space-2">
<HStack gap="$space-1">
<Text>Cart:</Text>
<I18n key="cart.items" count="{count}" />
</HStack>
<HStack gap="$space-1">
<Text>Status:</Text>
<I18n key="order.status" status="{status}" />
</HStack>
</VStack>
</VStack>
</App><App
localeBundles="{{
en: {
'terms.intro': 'Read the <termsLink/> and <privacyLink/> before continuing.'
}
}}">
<I18n key="terms.intro">
<property name="termsLink">
<Link to="/terms">terms of service</Link>
</property>
<property name="privacyLink">
<Link to="/privacy">privacy policy</Link>
</property>
</I18n>
</App><App
localeBundles="{{
en: {
'terms.intro': 'Read the <termsLink/> and <privacyLink/> before continuing.'
}
}}">
<I18n key="terms.intro">
<property name="termsLink">
<Link to="/terms">terms of service</Link>
</property>
<property name="privacyLink">
<Link to="/privacy">privacy policy</Link>
</property>
</I18n>
</App>Behaviors
This component supports the following behaviors:
| Behavior | Properties |
|---|---|
| Animation | animation, animationOptions |
| Bookmark | bookmark, bookmarkLevel, bookmarkTitle, bookmarkOmitFromToc |
| Component Label | label, labelPosition, labelWidth, labelBreak, required, enabled, shrinkToLabel, style, readOnly |
| Tooltip | tooltip, tooltipMarkdown, tooltipOptions |
| Styling Variant | variant |
Properties
key
This property is required.
Translation key to resolve from the active locale bundle.
The key identifies the message in the active locale bundle. If the key is not
found, I18n renders the key itself so the UI remains visible and the missing
translation is easy to spot.
<App
localeBundles="{{
en: {
'profile.title': 'Profile'
}
}}">
<VStack gap="$space-2">
<I18n key="profile.title" />
<I18n key="profile.subtitle" />
</VStack>
</App><App
localeBundles="{{
en: {
'profile.title': 'Profile'
}
}}">
<VStack gap="$space-2">
<I18n key="profile.title" />
<I18n key="profile.subtitle" />
</VStack>
</App>Events
This component does not have any events.
Exposed Methods
This component does not expose any methods.
Styling
I18n renders its translated output in an inline wrapper so text fragments and inline
placeholders stay together as one sentence. Styling comes from the component that contains
it. Wrap separate translated messages in Text, HStack, VStack, or another layout
component when you need spacing, typography, or separate rows.