UI, Assets & L10N
flutter_l10n Free
- Tier
- Freeversion 2.1.7
- Command
archipelago generate flutter_l10n- Generates
shared/locale_core
- Used by
- app_scaffold, auth_sdk, home_sdk, paywall_sdk, push_notification_sdk, webview_sdk_advanced and 1 more
A Mason brick for generating a Flutter localization package using Slang v4, with type-safe translations, per-feature l10n support, and Indonesian pluralization.
Variables
| Variable | Type | Default | Description |
|---|---|---|---|
| appName | string | MyApp | Application name for package naming |
| prefix | string | App | Prefix for generated locale enum (e.g. AppLocale) |
| isForMonorepo | boolean | true | Whether this package will be part of a monorepo structure |
Install
Interactive
archipelago generate flutter_l10nNon-interactive (CI)
archipelago generate flutter_l10n --config my_config.jsonConfig Template
{
"@appName": "Application name for package naming",
"appName": "MyApp",
"@prefix": "Prefix for generated locale enum",
"prefix": "App",
"@isForMonorepo": "Whether this package will be part of a monorepo structure",
"isForMonorepo": true
}Generated Structure
shared/
└── locale_core/
├── l10n/ # Translation source files
│ ├── general/
│ │ ├── general_en.i18n.json # English translations
│ │ └── general_id.i18n.json # Indonesian translations
│ └── feedback/
│ ├── dialog_{en,id}.i18n.json
│ ├── error_{en,id}.i18n.json
│ └── success_{en,id}.i18n.json
├── lib/
│ ├── locale_core.dart # Barrel (re-exports slang_flutter + translations)
│ └── src/
│ ├── persistence/
│ │ ├── app_locale_controller.dart # Persist + restore locale
│ │ ├── locale_store.dart # Storage seam
│ │ ├── shared_preferences_locale_store.dart # Default store
│ │ └── persistence.dart # Barrel export
│ ├── translations/
│ │ └── translations.g.dart # Generated by slang
│ └── utils/
│ ├── extensions.dart # Locale-aware formatting
│ ├── resolver.dart # Indonesian pluralization rules
│ └── utils.dart # Barrel export
├── slang.yaml # Slang configuration
├── build.yaml # build_runner config (sources: l10n/**)
├── pubspec.yaml
└── README.mdPublic API
// Read/change the durable, app-wide locale (static, no GetIt registration)
final current = AppLocaleController.localeForLanguageTag('id');
await AppLocaleController.setLocale(AppLocale.id); // sets + persists
await AppLocaleController.restore(); // called in bootstrap.dart
await AppLocaleController.clear(); // forget the preference
// Access translations in a widget (namespaced by directory under l10n/)
Text(context.l10n.general.appName);Key Features
- Slang v4 — Type-safe, compile-time checked translations with namespace support
- Dual generation —
dart run slang(standalone) or viaslang_build_runner:legacywithbuild_runner - Namespaces — Organize translations by domain (general, feedback, etc.)
- Indonesian support — Custom pluralization resolver for Bahasa Indonesia
- Per-feature l10n — Feature modules (auth, home) can have their own
slang.yamlwith scoped translations (e.g.context.authL10n) - Persisted locale —
AppLocaleControllerwrites the chosen language to disk and restores it before the first frame
Locale Persistence Contract
slang's LocaleSettings is in-memory only — it mutates a plain in-process singleton with no disk-backed storage. Without the layer this brick adds, every generated language switcher silently reverts to the base locale on each cold start. flutter_l10n owns both halves of the contract, so no other brick has to re-derive it:
| Concern | Owner |
|---|---|
| Writing the chosen locale to disk | flutter_l10n — AppLocaleController.setLocale() |
| Restoring it before the first frame | flutter_l10n — AppLocaleController.restore(), awaited in bootstrap.dart (flutter_modular_monorepo / app_scaffold) |
| Where the value is stored | flutter_l10n — LocaleStore, default SharedPreferencesLocaleStore |
| Applying it to a feature-scoped slang instance | the feature brick's shell page |
Any UI that changes the language must call its own feature-scoped LocaleSettings.setLocale(<Feature>Locale.x) for the immediate rebuild and route the app-wide change through app_l10n.AppLocaleController.setLocale(...):
LocaleSettings.setLocale(HomeLocale.id); // feature-scoped rebuild
unawaited(
app_l10n.AppLocaleController.setLocale(app_l10n.AppLocale.id), // app-wide + durable
);Calling app_l10n.LocaleSettings.setLocale(...) directly looks identical in the running app but persists nothing.
To store the preference somewhere else (encrypted storage, a server-synced profile setting), assign a LocaleStore before restore() runs:
AppLocaleController.store = MyEncryptedLocaleStore();Slang Configuration
The slang.yaml configures how translations are generated:
base_locale: en
enum_name: AppLocale # Generated from prefix variable
class_name: Translations
translate_var: l10n # Access via context.l10n
input_directory: l10n
output_directory: lib/src/translations
namespaces: true
flutter_integration: truePer-Feature Translations
Feature modules can have their own translations. Each feature gets its own slang.yaml:
# features/auth/auth_impl/slang.yaml
translate_var: authL10n # context.authL10n
enum_name: AuthLocale
class_name: AuthTranslationsShell pages sync the global locale to feature-scoped translations:
final authLocale = AuthLocale.values.firstWhere(
(l) => l.languageTag == locale.languageTag,
orElse: () => AuthLocale.en,
);
await LocaleSettings.setLocale(authLocale);Dependencies
slang^4.18.0 — Core translation engineslang_flutter^4.18.0 — Flutter integration (TranslationProvider,LocaleSettings)shared_preferences^2.5.5 — Backs the defaultLocaleStore(already resolved by every generated project viashared/consent_impl)slang_build_runner^4.18.0 (dev) — build_runner integration
Build Configuration
The build.yaml must include sources: ["l10n/**"] so build_runner can find translation files outside lib/. It also turns off slang's default builder and turns on slang_build_runner:legacy:
targets:
$default:
sources:
- "$package$"
- "l10n/**"
- "lib/**"
builders:
slang_build_runner:
enabled: false
slang_build_runner:legacy:
enabled: true
options:
input_directory: l10n
output_directory: lib/src/translationsWhy the legacy builder. The default slang_build_runner writes its .g.dart files through a post-process builder. build_runner 2.15 and later stops with InvalidOutputException: ... Asset already exists (and writes 0 outputs) when such a file is already on disk but is not in build_runner's asset graph. That happens on a normal checkout after .dart_tool/build is deleted, or after it is restored from a CI cache. The flag --delete-conflicting-outputs no longer exists to override it. The legacy builder writes the same files directly and overwrites them, so build_runner build works with or without existing output. One limit: in build_runner watch the legacy builder generates once per process, so restart watch (or run dart run slang) after you edit a translation file.
melos run build also runs dart run slang in every package with a slang.yaml (l10n:generate:all). That covers shared/locale_core, which build:select skips because it has no direct build_runner dependency.
DI & Wiring
None — AppLocaleController and slang's LocaleSettings/TranslationProvider are static/singleton APIs, not registered in GetIt. restore() must be awaited in bootstrap.dart before runApp; that wiring is provided by flutter_modular_monorepo/app_scaffold, not by this brick's own post_gen.dart (which only adds shared/locale_core to the workspace).
Platform Setup
None required.
Limitations
translations.g.dartis not generated by this brick — it ships as an empty placeholder (lib/src/translations/.gitkeep) until you rundart run slangorbuild_runneryourself.LocaleStore's default persistence isSharedPreferencesLocaleStore; assignAppLocaleController.storebeforerestore()runs to use a different backend.
Changelog
Run archipelago brick info flutter_l10n to see the latest published version. Enterprise customers can read the full CHANGELOG.md in the brick source repository.