Check your ARB files right in your browser with a free tool, without signup or upload.
How ARB files are organized
With flutter gen-l10n, each language has its own file (app_en.arb, app_fr.arb…). The template file, set by template-arb-file in l10n.yaml, is the reference: it holds every key and its @key metadata (description, placeholders).
{
"cartCount": "{count, plural, =0{Empty cart} =1{1 item} other{{count} items}}",
"@cartCount": {
"placeholders": { "count": { "type": "int" } }
}
}
1. Let Flutter list untranslated messages
Add the untranslated-messages-file option to l10n.yaml:
arb-dir: lib/l10n
template-arb-file: app_en.arb
untranslated-messages-file: untranslated.json
On every generation, Flutter writes the template keys missing from each language into that file. It is the simplest check to set up; make sure the file is empty (or only contains {}) before you ship.
2. Compare keys with a small Dart script
For a CI check that fails clearly, a script is enough:
import 'dart:convert';
import 'dart:io';
void main() {
final dir = Directory('lib/l10n');
final template = _keys(File('lib/l10n/app_en.arb'));
var failed = false;
for (final file in dir.listSync().whereType<File>()) {
if (!file.path.endsWith('.arb')) continue;
final missing = template.difference(_keys(file));
if (missing.isNotEmpty) {
failed = true;
print('${file.path}: ${missing.join(', ')}');
}
}
if (failed) exit(1);
}
Set<String> _keys(File f) => (jsonDecode(f.readAsStringSync()) as Map<String, dynamic>)
.keys
.where((k) => !k.startsWith('@'))
.toSet();
Keys starting with @ are metadata, so they are excluded from the comparison.
3. Check placeholders
A key present everywhere can still break: if the French translation uses {nombre} while the template declares {count}, gen-l10n adds nombre as an extra parameter in every locale, and existing call sites stop compiling. Conversely, a translation that simply drops a placeholder goes through without any warning (details in the ICU plurals and placeholders guide). For each key, compare the names in braces with those declared in @key.placeholders. Watch out for ICU messages (plural, select): keywords such as other are not placeholders.
4. Watch for empty values too
Some translation pipelines create the key with an empty value until it is translated. It does not show up as "missing", so add a check that flags empty strings in every language except the template.
Pre-release checklist
- The untranslated messages file is empty.
- Every language has the same keys as the template.
- Placeholders match, key by key.
- No empty values outside the template.
- Long messages have been checked on screen in the wordiest languages (German, for example).