UI, Assets & L10N
How to add type-safe localization to a Flutter app with Slang
Free Reference for flutter_l10n: every variable, option and package it generates. Command: archipelago generate flutter_l10n
Generate the shared localization package, add translation keys and regenerate, change language so it survives a restart, and scope translations per feature.
What you'll learn
- Generating the shared localization package with Slang
- Adding translation keys and regenerating
- Changing the language so the choice survives a restart
- Scoping translations per feature
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
New projects already include this brick.
Step 1: Generate the Localization Package
archipelago generate flutter_l10nThe prompts are:
- appName: your app name, for example
MyApp - prefix: the prefix for the locale enum, such as
AppLocale - isForMonorepo: leave this as
true
Or use a config file:
{
"appName": "MyApp",
"prefix": "App",
"isForMonorepo": true
}archipelago generate flutter_l10n --config l10n_config.jsonStep 2: Understand the Generated Structure
shared/locale_core/
├── l10n/ # Translation sources
│ ├── general/
│ │ ├── general_en.i18n.json
│ │ └── general_id.i18n.json
│ └── feedback/ # dialog, error and success namespaces
├── lib/src/
│ ├── persistence/ # AppLocaleController, LocaleStore
│ ├── translations/translations.g.dart # Generated by slang
│ └── utils/ # Formatting and the Indonesian plural resolver
├── slang.yaml
└── build.yamlNamespaces come from the folders under l10n/.
Step 3: Add Translation Keys
Add keys to a namespace file in each language:
// l10n/general/general_en.i18n.json
{
"appName": "My App",
"welcome": "Welcome, {name}!"
}// l10n/general/general_id.i18n.json
{
"appName": "Aplikasi Saya",
"welcome": "Selamat datang, {name}!"
}Regenerate:
dart run slangRun slang after build_runner, not before. The packages here have no direct build_runner dependency, so melos run build alone does not generate them.
Step 4: Use Translations
Text(context.l10n.general.appName);
Text(context.l10n.general.welcome(name: user.name));Step 5: Change the Language and Keep It
slang's LocaleSettings lives in memory only. A language switch that uses it alone reverts on the next cold start. Route the change through AppLocaleController:
await AppLocaleController.setLocale(AppLocale.id); // sets and savesbootstrap.dart awaits AppLocaleController.restore() before runApp(). AppLocaleController.clear() forgets the choice. To store it somewhere else, set AppLocaleController.store = MyLocaleStore() before restore() runs.
Never call LocaleSettings.setLocale of this package from UI. It looks right and saves nothing.
Step 6: Scope Translations per Feature
A feature has its own slang.yaml and its own locale enum, for example AuthLocale and context.authL10n. A feature that offers a language switch changes both:
LocaleSettings.setLocale(HomeLocale.id); // the feature rebuild
unawaited(AppLocaleController.setLocale(AppLocale.id)); // app-wide and savedThe feature shell page copies the app locale into the feature locale when it first builds.
Key Customization Points
| Customization | Where to Change |
|---|---|
| Add a language | Add *_<code>.i18n.json files per namespace, then run dart run slang |
| Interpolation | {variable} in the JSON value |
| Plurals | Slang plural syntax. Indonesian rules are in utils/resolver.dart |
| Base language | base_locale in slang.yaml |
| Locale storage | A custom LocaleStore |
Next Steps
- Generate a UI Kit with localized component labels
- Scaffold a new app