Internationalization
i18n is built in. Mark the text you want translated, register dictionaries, and switch language at runtime — every marked binding re-renders.
Dictionaries
const lips = new Lips()
lips.i18n.setDictionary('en', {
'Welcome': 'Welcome',
'Submit': 'Submit'
})
lips.i18n.setDictionary('fr', {
'Welcome': 'Bienvenue',
'Submit': 'Soumettre'
})
lips.i18n.setDictionary('es', {
'Welcome': 'Bienvenido',
'Submit': 'Enviar'
})
Dictionary ids are the language part of a locale: fr covers fr, fr-FR and fr-CA.
The default language is the browser’s navigator.language.
Switching language
lips.setLanguage('fr')
lips.setLanguage('en-GB')
lips.getLanguage() // → 'en-GB'
Every binding marked for translation re-runs on change — there is nothing to refresh manually.
Marking content
Add i18n to an element to translate its text:
<h1 i18n>Welcome</h1>
<p i18n>Hello, {state.name}</p>
<button i18n>Submit</button>
In French that renders:
<h1>Bienvenue</h1>
<p>Bonjour, Ada</p>
<button>Soumettre</button>
Exclude a subtree with no-translate:
<div no-translate>Lips</div>
Regional variants
A dictionary entry can be an object keyed by region, with * as the fallback:
lips.i18n.setDictionary('en', {
'color': {
'*': 'color', // default
'GB': 'colour',
'CA': 'colour'
}
})
lips.setLanguage('en-GB') // → "colour"
Formats
For anything with variables, define a format entry and reference it with @format.
Variable
lips.i18n.setDictionary('en', {
'welcome_user': {
type: 'variable',
value: 'Welcome back, {name}!'
}
})
<p @format="welcome_user, { name: state.user.name }"/>
Plural
Keyed by count, with * as the fallback:
lips.i18n.setDictionary('en', {
'items_count': {
type: 'plural',
value: {
'*': '{count} items',
'1': '{count} item',
'0': 'No items'
}
}
})
<p @format="items_count, { count: state.items.length }"/>
The parameter must be named count, and a * fallback is required.
Condition
Keyed by an expression over the parameters; the first matching branch wins:
lips.i18n.setDictionary('en', {
'age_check': {
type: 'condition',
value: {
'age >= 18': 'Welcome in.',
'age < 18': '{age}-year-olds are not allowed'
}
}
})
<p @format="age_check, { age: input.user.age }"/>
Reacting to language changes
useTranslator runs a callback when the language changes — useful for reloading data from a
localized API. It returns an unsubscribe function.
const stop = lips.useTranslator([ 'fr', 'es' ], lang => {
reloadArticles( lang )
})
// '*' to react to every language
lips.useTranslator('*', lang => document.documentElement.lang = lang )
Translating from code
The i18n API lives on the Lips instance, so reach it through the instance you created — a
component’s this does not carry it.
// app.js
export const lips = new Lips()
import { lips } from './app.js'
export const handler = {
label( text ){
return lips.i18n.translate( text ).text
},
message( key, params ){
return lips.i18n.format( key, params )
}
}
translate() returns { text, lang }; format() returns the formatted string.
Prefer the i18n attribute and @format over calling the API from handlers. Only the
template forms subscribe to the language signal, so only they re-render automatically when the
language changes.