Skip to content
Back to archipelago.banuacoder.com

UI, Assets & L10N

flutter_l10n Free ​

Tier
Freeversion 2.1.7
Command
archipelago generate flutter_l10n
Generates
  • shared/locale_core

A Mason brick for generating a Flutter localization package using Slang v4, with type-safe translations, per-feature l10n support, and Indonesian pluralization.

Variables ​

VariableTypeDefaultDescription
appNamestringMyAppApplication name for package naming
prefixstringAppPrefix for generated locale enum (e.g. AppLocale)
isForMonorepobooleantrueWhether this package will be part of a monorepo structure

Install ​

Interactive ​

bash
archipelago generate flutter_l10n

Non-interactive (CI) ​

bash
archipelago generate flutter_l10n --config my_config.json

Config Template ​

json
{
  "@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 ​

text
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.md

Public API ​

dart
// 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 via slang_build_runner:legacy with build_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.yaml with scoped translations (e.g. context.authL10n)
  • Persisted locale — AppLocaleController writes 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:

ConcernOwner
Writing the chosen locale to diskflutter_l10n — AppLocaleController.setLocale()
Restoring it before the first frameflutter_l10n — AppLocaleController.restore(), awaited in bootstrap.dart (flutter_modular_monorepo / app_scaffold)
Where the value is storedflutter_l10n — LocaleStore, default SharedPreferencesLocaleStore
Applying it to a feature-scoped slang instancethe 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(...):

dart
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:

dart
AppLocaleController.store = MyEncryptedLocaleStore();

Slang Configuration ​

The slang.yaml configures how translations are generated:

yaml
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: true

Per-Feature Translations ​

Feature modules can have their own translations. Each feature gets its own slang.yaml:

yaml
# features/auth/auth_impl/slang.yaml
translate_var: authL10n           # context.authL10n
enum_name: AuthLocale
class_name: AuthTranslations

Shell pages sync the global locale to feature-scoped translations:

dart
final authLocale = AuthLocale.values.firstWhere(
  (l) => l.languageTag == locale.languageTag,
  orElse: () => AuthLocale.en,
);
await LocaleSettings.setLocale(authLocale);

Dependencies ​

  • slang ^4.18.0 — Core translation engine
  • slang_flutter ^4.18.0 — Flutter integration (TranslationProvider, LocaleSettings)
  • shared_preferences ^2.5.5 — Backs the default LocaleStore (already resolved by every generated project via shared/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:

yaml
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/translations

Why 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.dart is not generated by this brick — it ships as an empty placeholder (lib/src/translations/.gitkeep) until you run dart run slang or build_runner yourself.
  • LocaleStore's default persistence is SharedPreferencesLocaleStore; assign AppLocaleController.store before restore() 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.