Localising an app in Flutter seems straightforward: define your strings, provide translations, and you’re done, right? Not quite. Even with Flutter’s powerful internationalisation tools and ARB file structure, subtle mistakes can creep in and lead to broken user experiences and localisation headaches.
If you’ve ever struggled with missing translations, inconsistent pluralisation, or strings that don’t quite fit the target language, you’re not alone. This blog explores the most common traps developers fall into when localising Flutter apps and shares practical tips to ensure your app speaks every language fluently.
Flutter offers great built-in support for internationalisation. If you’re unfamiliar with it, Flutter uses ARB files (JSON-like files), with one file per supported language in the app. The main ARB file contains all key-value string pairs used by the app, while the other ARB files replicate the same keys with values translated into different languages:
intl_en.arb (default):
{
"@@locale": "en",
"homePageTitle": "Home page",
"buttonText": "Open next page"
}
intl_fi.arb:
{
"@@locale": "fi",
"homePageTitle": "Kotisivu",
"buttonText": "Avaa seuraava sivu"
}
While the process is straightforward, mistakes can still happen. From forgotten strings to assumptions about how all languages work, let’s dive into the most common localisation issues developers encounter and how to fix them.
01 Don’t forget to add strings to ARB files
It’s easy to overlook adding a string to the ARB file. This can result in an inconsistent user experience:
const Text('System status') // Can't be translated to other languages
02 Translate everything: avoid overlooking key texts
Some developers assume that text like unit names (e.g. “km” for kilometres or “MB” megabytes) don’t need localisation. However, unit names may differ in languages that don’t use Latin characters: For example:
- Greek: “km” → “χλμ”
- Bulgarian “km” → “км”
Text("$calculatedDistance km") // Can't be properly translated
03 Keep your strings updated: No room for errors
It’s human to forget. When adding a new string to the main localisation file, it’s easy to overlook providing the corresponding translations. During development or even before building a release app, there’s often no reminder to ensure up-to-date translations.
Another common issue arises when existing localisations are not updated after changes to a feature. For example, if a feature is modified and its strings are updated, developers might adjust the main translation strings to match the new design but neglect to update the translations. This results in inconsistent user experience and misleading translations.
Additionally, not all strings need to stay forever. If a string is no longer used, it should be removed to reduce translation workload and keep the localisation files clean.
04 English isn’t universal: adjust for language rules
Languages handle plurals in different ways, and assuming English pluralisation rules apply universally can lead to incorrect translations. For example:
final myString = cartItems.length > 1 ? “items” : “item”;
This approach may work in English but fails in other languages with more complex pluralisation rules. Just to show the issue: the code above doesn’t even work for the quantity of 0 in English. The solution is to always use plurals in ARB files, which are designed to handle such variations.
Additionally, programmatically constructed strings often overlook important linguistic features such as word order, grammar cases, punctuation, and pluralisation rules:
final myString = strings.question + items.length + "?";
Instead, use localisable strings that accommodate these nuances. For example:
"message": "Would you like to remove {count, plural, other{{count} items} one{{count} item}}?"
This structure enables accurate translations that respect the grammar and pluralisation rules of the target language, such as:
"message": "¿Desea eliminar {count, plural, other{{count} elementos} many{{count} elementos} one{{count} elemento}}?"
To better understand the pluralisation rules across different languages, unicode.org provides a detailed table of plural rules. These variations highlight why it’s often better to rely on Flutter’s built-in localisation support rather than attempting to build a custom solution.
05 Manage plurals properly: avoid missing or extra rules
Flutter supports plural strings for all supported languages, but the rules for plurals vary widely. For example, English requires just two options to handle all possible plural forms:
"message": "{count, plural, other{{count} items} one{{count} item}}"
In contrast, other languages may need more plural options to account for their specific grammar rules. . For instance:
- Vietnamese uses only one option.
- Irish requires five options.
- Arabic requires all six plural forms.
Here’s an example of Arabic pluralisation:
"message": "{count, plural, other{{count} ceann} one{{count} cheann} two{{count} cheann} few{{count} cinn} many{{count} gcinn}}"
It’s important to avoid translating plural options that are not used in a particular language. Including unnecessary options increases the effort required to translate and maintain ARB files without providing any benefit. For example, normally English localisation does not need values for “zero,” “two,” “few,” or “many”, however it uses “one”, “two”, “few” and “other” as ordinals (values like “1st” or “2nd”).
06 Understand “One” and other plural forms
Despite their names, plural forms like “one” and “two” can apply to more than just the quantities of 1 and 2 in certain languages. For example, these forms may also apply to quantities like 21, 22, or even other specific quantities. Assuming “one” or “two” refers only to the quantities of 1 and 2 can lead to incorrect results:
"durationHours": "{count, plural, one{1 sat}..."
durationHours(1) // Returns "1 sat" (correct)
durationHours(21) // Returns "1 sat" (incorrect)
The fix is easy: always use placeholders in plural strings to ensure the correct value is displayed:
"durationHours": "{count, plural, one{{count} sat}..."
This guarantees that the correct number is included in the output, respecting the pluralisation rules of the target language.
07 Define placeholder types for safer code
Specifying a placeholder type makes generated Dart code type-safe. For example, consider this string, which uses a placeholder but doesn’t define its type:
"key": "Hello, {username}!"
This will generate the following Flutter code:
String key(Object username) {
return Intl.message(
'Hello $username!',
name: 'key',
desc: '',
args: [username],
);
}
As you can see, the placeholder’s type is Object, which lacks type safety. To address this, you can explicitly define the placeholder type in the ARB file:
"key": "Hello, {username}!",
"@key": {
"placeholders": {
"username": {
"type": "String"
}
}
}
This results in type-safe Dart code, ensuring only the correct type can be passed to the function:
String key(String username) {
...
By specifying types for placeholders, you reduce the risk of runtime errors and improve code robustness.
08 Account for text length: accessibility matters
The impact of text length on an app may not always be obvious. For example, limiting the text length in a Text widget can negatively affect the app’s accessibility:
return TextButton(
onPressed: onPressed,
child: const Text(
'Add to cart',
maxLines: 1,
),
);
If a user increases the text size in their device settings or if the app’s locale changes, the button’s label could become truncated, making it difficult or even impossible to read. Read more about the accessibility of Flutter apps in one of our previous articles here.
09 Keep ARB files clean and organised
While developers often strive to keep their code clean, ARB files are frequently overlooked—even though they are a crucial part of the app and eventually converted into Dart code.
Here are some common issues that can make ARB files and the generated Dart code inconsistent:
- Key naming conventions: Inconsistent naming makes the file harder to maintain.
- Empty @-keys: Keys meant for metadata but left empty create unnecessary clutter.
- Empty strings: Untranslated or placeholder strings can cause runtime issues.
- Redundant or unused @-keys: These add noise and increase maintenance overhead.
- Duplicated keys: Repeated keys lead to conflicts and confusion in localisation.
By addressing these issues, ARB files can be kept clean, improving the maintainability and reliability of the app’s localisation.
Tools to simplify ARB file management
How can one identify, fix, and minimise issues in ARB files? While not all problems can be automatically detected and resolved, many common issues are easy to spot with tools provided by the Flutter community.
ARB Editor is an official VS Code extension from Google that checks ARB files’ validity. It’s a great extension that also provides a set of snippets for creating strings, plurals and selects.
Rebellion by Rebel App Studio is a command line lint tool for ARB files. It ensures that all strings are properly organised and that ARB files are free from errors. It can catch issues like missing translations, unused keys, incorrect pluralisation, and inconsistent naming conventions. For example:
> rebellion analyse ./lib/l10n/
l10n/intl_fi.arb: all caps string key "key2"
l10n/intl_fi.arb: no @@locale key found
l10n/intl_en.arb: key "@key4" is missing placeholders definition
l10n/intl_fi.arb key "key3" is missing a plural value "one"
l10n/intl_en.arb key "key3" contains a redundant plural value "zero"
l10n/intl_fi.arb: missing translation for key "key_5"
l10n/intl_fi.arb: @-key "@key" should only be present in the main file
l10n/intl_en.arb: key "key_5" does not match selected naming convention (camel case)
8 issues found
Adding Rebellion to your CI/CD pipeline ensures potential issues are caught before they’re merged into the main branch, saving you time and preventing errors from reaching production. We’re continuously improving Rebellion, with plans to integrate lint rules directly into the IDE for even smoother development workflows.
Let’s connect!
Rebellion is built by developers, for developers, and we’re always looking for feedback to make it even better. If you have questions about ARB file management, localisation best practices, or how to integrate Rebellion into your projects, feel free to reach out to us.
Contact Miika

Miika Toljamo
Business