When I started my first Flutter app, I did what every tutorial told me to do: set up flutter_localizations with .arb files and let the official intl-based codegen handle translations. It worked. It also made me miserable.
Here's why I moved to slang (with slang_flutter) instead, and why beginners especially might want to skip the "official" route.
The problem with ARB + intl
The built-in Flutter localization setup isn't bad, exactly. It's just clunky for small and mid-size projects:
-
.arbfiles are verbose JSON with metadata blocks for every single string. - You need
flutter: generate: true, anl10n.yamlfile, and a rebuild step that sometimes silently fails. - Accessing strings means
AppLocalizations.of(context)!.someKey, and that!is doing a lot of quiet lying to you. - Typos in keys aren't caught until runtime, which for a beginner means a confusing "LateInitializationError" instead of a helpful compile error.
What changed with Slang
Slang flips the experience. You write plain JSON (or YAML/CSV if you prefer) files per locale:
// en.i18n.json
{
"hello": "Hello $name",
"login": {
"success": "Logged in successfully"
}
}
Run the generator, and you get fully typed Dart code. Usage looks like this:
import 'package:my_app/i18n/strings.g.dart';
String greeting = t.hello(name: 'Tom');
Text(t.login.success);
No context lookups, no nullable bang operator, and (this is the part that sold me) if you typo a key, your code doesn't compile. As a beginner, that single change removed an entire category of "why is my text blank" bugs.
Where Slang beat easy_localization for me
I'd also tried easy_localization before landing on Slang. It's a solid package, but it resolves keys as strings at runtime ('login.success'.tr()), which means the same typo problem as ARB, just with a friendlier API. Slang's compile-time generation is the real differentiator: your IDE autocompletes every translation key, and refactors (renaming a key) are safe, because you're renaming a real Dart property, not a magic string.
Setup, quickly
dependencies:
slang: ^4.18.0
slang_flutter: ^4.18.0
dev_dependencies:
build_runner: ^2.15.1
Drop your JSON files in lib/i18n/, run:
dart run slang
Wire it into MaterialApp:
MaterialApp(
locale: TranslationProvider.of(context).flutterLocale,
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
child: MyApp(),
)
That's genuinely most of it. Pluralization, rich text, and namespaces are all supported when you're ready to grow into them, but you don't need to touch a single YAML config file to get started, which is rare for an i18n package.
Bottom line
If you're new to Flutter and dreading localization setup, skip the ARB detour. Slang gets you type-safe, typo-proof translations with less boilerplate and a much gentler learning curve. My only regret is not switching sooner.
Have you tried Slang or another i18n package in Flutter? I'd love to hear what's worked for you in the comments.
Top comments (1)
"It worked. It also made me miserable" is the entire .arb + intl experience in two sentences. The silent rebuild failures were what got me too.
slang fixes the ergonomics, but curious how you handle the other half: actually filling the JSONs for each locale when copy changes. That was the part that stayed manual for me even after fixing the tooling, so I built i1n (i1n.ai), one command AI-translates missing keys against the source locale and a check catches drift in CI. Works on plain JSON so it composes with slang. For apps with six-figure downloads the languages compound fast. Free tier if you want to poke at it.