Localization
Generated projects ship with slang for type-safe, per-locale translations — English and Indonesian out of the box. There is no central translations package: shared/locale_core holds the app-wide strings, and each feature that needs its own copy (shell titles, form labels, validation messages, etc.) owns a second, feature-scoped translation package. auth_sdk is the reference implementation — copy its shape when you add translations to a new or existing feature.
TIP
Translations are generated via the standalone slang CLI (dart run slang), not build_runner. slang_build_runner is still listed as a dev dependency so melos run build and your editor's watch tooling stay in sync, but the actual generation command is dart run slang.
App-wide translations (shared/locale_core)
shared/locale_core is generated by the flutter_l10n brick and is the only package that defines the app's locale enum (AppLocale by default) and LocaleSettings. Never redeclare either per-feature.
It's namespaced by filename, since it aggregates several small JSON files instead of one flat one:
shared/locale_core/
l10n/
general/
general_en.i18n.json # l10n.general.*
general_id.i18n.json
feedback/
dialog_en.i18n.json # l10n.dialog.*
dialog_id.i18n.json
error_en.i18n.json # l10n.error.*
error_id.i18n.json
success_en.i18n.json # l10n.success.*
success_id.i18n.json
slang.yamlUse context.l10n.general.appName (or the bare l10n.general.appName getter outside of a BuildContext) anywhere in the app shell or shared widgets.
Adding translations to a feature
Follow these steps to add a feature-scoped translation package, using auth_sdk's auth_impl package as the reference:
1. Add dependencies
Add slang and slang_flutter as direct dependencies in the feature's pubspec.yaml — the generated code imports them directly, so very_good_analysis's depend_on_referenced_packages lint requires them listed even though shared/dependencies already re-exports both. Add slang_build_runner as a dev_dependency.
2. Create the translation files
Add l10n/en.i18n.json (base locale) and l10n/id.i18n.json at the feature package root — not under lib/. Nested JSON keys become nested translation getters (e.g. login.emailLabel). Keep validation messages in one flat top-level validation object shared across the feature's pages and forms, rather than nesting a separate validation block per page.
// features/auth/auth_impl/l10n/en.i18n.json
{
"login": {
"title": "Sign In",
"emailLabel": "Email",
"passwordLabel": "Password",
"button": "Sign In"
},
"validation": {
"emailRequired": "Email is required",
"passwordRequired": "Password is required"
}
}3. Add a per-feature slang.yaml
Add a slang.yaml at the feature package root with a feature-prefixed translate_var, enum_name, and class_name so multiple features' generated code can coexist without collisions:
# features/auth/auth_impl/slang.yaml
base_locale: en
fallback_strategy: base_locale
input_directory: l10n
input_file_pattern: .i18n.json
output_directory: lib/src/l10n
output_file_name: auth_translations.g.dart
locale_handling: true
flutter_integration: true
namespaces: false
translate_var: authL10n
enum_name: AuthLocale
class_name: AuthTranslations
translation_class_visibility: public
lazy: trueKeep namespaces: false with unprefixed l10n/en.i18n.json / l10n/id.i18n.json filenames for a single-file feature package. Setting namespaces: true only makes sense for a package with multiple sibling JSON files per locale, like shared/locale_core's general_en.i18n.json / dialog_en.i18n.json split — with a single file pair, it wraps every key under an extra namespace and breaks the flat authL10n.login.title access pattern.
4. Generate
Run dart run slang from inside the feature package directory. Commit the generated lib/src/l10n/*.g.dart output — generated translation files are committed to the repo, not gitignored.
5. Use translations in widgets and cubits/blocs
- Under a
TranslationProviderancestor (any widget in the feature's shell subtree), usecontext.{translate_var}— e.g.context.authL10n.login.title. It rebuilds automatically on locale change. - In places with no
BuildContext(cubits, blocs, use cases), use the bare top-level getter instead — e.g.authL10n.validation.emailRequired. It reads the current locale synchronously but does not itself trigger a rebuild.
6. Sync the feature's locale with the app-wide locale
Every feature package has its own LocaleSettings, separate from shared/locale_core's. Wire them together in the feature's shell page:
- On
initState, and again on every route build inside the "ready" branch, read the app-wideLocaleSettings.currentLocale(importlocale_corealiased, e.g.as app_l10n, to avoid colliding with the feature's own generatedLocaleSettings/locale enum). - Map it to the feature's own locale enum by
languageTag, and call the feature's own (unqualified)LocaleSettings.setLocale(...). - Wrap the "ready" branch in the feature's generated
TranslationProvidersocontext.{translate_var}reacts to the change.
// features/auth/auth_impl/lib/src/presentation/auth_shell_page.dart
import 'package:locale_core/locale_core.dart' as app_l10n;
Future<void> _syncLocale() async {
final appLocale = app_l10n.LocaleSettings.currentLocale;
final authLocale = AuthLocale.values.firstWhere(
(l) => l.languageTag == appLocale.languageTag,
orElse: () => AuthLocale.en,
);
if (_lastSyncedLocale == authLocale) return;
_lastSyncedLocale = authLocale;
await LocaleSettings.setLocale(authLocale);
}If your feature lets the user directly change the locale (e.g. a settings toggle), set both the feature-scoped LocaleSettings and propagate to app_l10n.LocaleSettings — nothing polls the app-wide locale for changes after the shell's initial sync.
Checklist
slang+slang_flutterasdependencies,slang_build_runnerasdev_dependencyl10n/en.i18n.json+l10n/id.i18n.jsonat the feature package root- Feature-prefixed
slang.yaml(translate_var,enum_name,class_name) dart run slangfrom the feature directorycontext.{translate_var}in widgets, bare getter in cubits/blocs- Locale sync in the feature's shell page, both directions if the feature can change locale itself
- Commit the generated
lib/src/l10n/*.g.dartfiles