Lightweight i18n solution for Flutter
fast_i18n
Lightweight i18n solution. Use JSON files to create typesafe translations.
Getting Started
Step 1: Add dependencies
dependencies:
fast_i18n: ^3.0.2
dev_dependencies:
build_runner: any
Step 2: Create JSON files
Create these files inside your lib
directory. Preferably in one common package like lib/i18n
.
Only files having the .i18n.json
file extension will be detected. You can configure it.
strings.i18n.json (default, fallback)
{
"hello": "Hello $name",
"save": "Save",
"login": {
"success": "Logged in successfully",
"fail": "Logged in failed"
}
}
strings_de.i18n.json
{
"hello": "Hallo $name",
"save": "Speichern",
"login": {
"success": "Login erfolgreich",
"fail": "Login fehlgeschlagen"
}
}
Step 3: Generate the dart code
flutter pub run build_runner build
Step 4: Initialize
a) use device locale
void main() {
WidgetsFlutterBinding.ensureInitialized(); // add this
LocaleSettings.useDeviceLocale(); // and this
runApp(MyApp());
}
b) use specific locale
@override
void initState() {
super.initState();
String storedLocale = loadFromStorage(); // your logic here
LocaleSettings.setLocale(storedLocale);
}
Step 4a: Override 'supportedLocales'
This is optional but recommended.
Standard flutter controls (e.g. back button's tooltip) will also pick the right locale.
MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: LocaleSettings.supportedLocales, // <---
)
Step 4b: iOS configuration
File: ios/Runner/Info.plist
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>de</string>
</array>
Step 5: Use your translations
Text(t.login.success); // plain
Text(t.hello(name: 'Tom')); // with argument
Text(t.step[3]); // with index (for arrays)
Text(t.type['WARNING']); // with key (for maps)
// advanced
TranslationProvider(child: MyApp()); // wrap your app with TranslationProvider
// [...]
final t = Translations.of(context); // forces a rebuild on locale change
String translateAdvanced = t.hello(name: 'Tom');
API
When the dart code has been generated, you will see some useful classes and functions
t
- the translate variable for simple translations
Translations.of(context)
- translations which reacts to locale changes
TranslationProvider
- App wrapper, used for Translations.of(context)
LocaleSettings.useDeviceLocale()
- use the locale of the device
LocaleSettings.setLocale('de')
- change the locale
LocaleSettings.setLocaleTyped(AppLocale.en)
- change the locale (typed version)
LocaleSettings.currentLocale
- get the current locale
LocaleSettings.currentLocaleTyped
- get the current locale (typed version)
LocaleSettings.locales
- get the supported locales
LocaleSettings.supportedLocales
- see step 4a
Configuration
All settings can be set in the build.yaml
file. Place it in the root directory.
targets:
$default:
builders:
fast_i18n:i18nBuilder:
options:
base_locale: en
input_directory: lib/i18n
input_file_pattern: .i18n.json
output_directory: lib/i18n
output_file_pattern: .g.dart
translate_var: t
enum_name: AppLocale
key_case: snake
maps:
- a
- b
- c.d
Key | Type | Usage | Default |
---|---|---|---|
base_locale | String |
locale of default json | en |
input_directory | String |
path to input directory | null |
input_file_pattern | String |
input file pattern | .i18n.json |
output_directory | String |
path to output directory | null |
output_file_pattern | String |
output file pattern | .g.dart |
translate_var | String |
translate variable name | t |
enum_name | String |
enum name | AppLocale |
key_case | camel , pascal , snake |
transform keys (optional) | null |
maps | List<String> |
entries which should be accessed via keys | [] |
FAQ
How do I add arguments?
Use the $
prefix.
In edge cases you can also wrap it with ${...}
.
{
"greeting": "Hello $name",
"distance": "${distance}m"
}
t.greeting(name: 'Tom'); // Hello Tom
t.distance(distance: 4.5); // 4.5m
How can I access translations using maps?
Define the maps in your build.yaml
.
Keep in mind that all nice features like autocompletion are gone.
strings.i18n.json
{
"welcome": "Welcome",
"thisIsAMap": {
"hello world": "hello"
},
"classicClass": {
"hello": "hello",
"aMapInClass": {
"hi": "hi"
}
}
}
build.yaml
targets:
$default:
builders:
fast_i18n:i18nBuilder:
options:
maps:
- thisIsAMap
- classicClass.aMapInClass
Now you can access the translations via keys:
String a = t.thisIsAMap['hello world'];
String b = t.classicClass.hello; // the "classical" way
String c = t.classicClass.aMapInClass['hi']; // nested
Can I use lists?
Lists are fully supported. You can also put lists or maps inside lists!
{
"niceList": [
"hello",
"nice",
[
"first item in nested list",
"second item in nested list"
],
{
"wow": "WOW!",
"ok": "OK!"
},
{
"a map entry": "access via key",
"another entry": "access via second key"
}
]
}
String a = t.niceList[1]; // "nice"
String b = t.niceList[2][0]; // "first item in nested list"
String c = t.niceList[3].ok; // "OK!"
String d = t.niceList[4]['a map entry']; // "access via key"