An ARB editor that shows every locale at once
StringLane opens all of your app_*.arb files together, flags what is missing or broken while you type, and writes back to the same files your flutter gen-l10n already reads.
$49 once · No email · Full app for 14 days

The format
What StringLane opens
ARB (Application Resource Bundle) is JSON with a convention. Each translatable key sits beside an optional @key object holding its description, placeholder types and constraints, and Flutter reads the set of files to generate AppLocalizations. StringLane detects the format from the file extension, so there is nothing to configure.
- File layout
- One JSON file per locale, typically
lib/l10n/app_en.arb - Placeholders
{param}, compared between locales by name- Plurals
- ICU MessageFormat inline,
{count, plural, other{...}} - Metadata
@keyobjects:@description,x-max-length,x-guarded- Detected by
- Any
.arbfile in the project folder - Project chip
ARB
Verification
What gets flagged, and why it matters in ARB
Validation runs when the project loads and again after every edit. These are the checks that apply to ARB specifically.
Missing key
The key exists in your template locale but is absent or empty in another. Your build still succeeds and the string ships in English, so this is the failure mode that reaches users.
Placeholder mismatch
Placeholders are compared by name, parsed from the ICU syntax tree rather than matched with a regex. That distinction matters: in {count, plural, other{days} one{day}} a regex reads days and day as placeholders, so translating those two words would look like a mismatch. StringLane compares only the real argument, count.
Invalid ICU
Unmatched braces, a plural missing its other category, or a malformed select clause. gen_l10n parses ICU too and will fail the build on malformed syntax. StringLane shows it while you are still in the file.
Standalone #
Flutter has no pound token, so a bare # inside a plural branch renders literally in the UI instead of being replaced by the number. StringLane flags it and the AI fill converts it to a named variable.
Length over the limit
Set x-max-length in a key's @key metadata and any translation longer than that is flagged. German button labels are the usual reason this exists.
Same as base, and guarded words
A value identical to the template locale usually means it was copied and never translated. Terms you mark as guarded, such as a product name, are flagged the other way round when a locale changes them.
What it will not do. StringLane is a file editor, not a translation reviewer. It cannot tell you that a grammatically valid German sentence says the wrong thing. It can tell you the key is missing, the placeholder was dropped, the ICU is malformed and the plural is short a category, which is the part you can check without speaking the language.
Your files
Your files come back the way you left them
StringLane writes to your source files directly. There is no export step and no project database.
@keymetadata objects are read and written back untouched, including@description,x-max-lengthandx-guarded.- Key order is preserved exactly as it appears in the source file.
- ICU strings are stored as you wrote them, not normalised into a house style.
- Open the file in your editor before and after: the diff shows only the values you actually changed.
Your toolchain
Where it sits in a Flutter project
StringLane is a file editor, not a code generator and not a build tool. It edits the .arb files that gen_l10n and the FlutterIntl extension already consume. Your l10n.yaml, your flutter gen-l10n step and your CI all keep running unchanged.
That matters because the build is not the safety net people assume. gen_l10n reports a count of untranslated messages in the build log and does not fail the build, so a missing German string compiles clean and ships as English. A count also says nothing about a translation that exists but dropped its {placeholder}, and nothing at all about your other formats.
FAQ
Flutter ARB questions
Does StringLane replace gen_l10n or FlutterIntl?
.arb files; gen_l10n reads them and generates AppLocalizations exactly as before. Nothing in your l10n.yaml or your build changes.Will it reorder my keys or strip my @description annotations?
@key metadata objects are written back untouched, in every locale file rather than only the template. If you have lost annotations to an editor before, that is the behaviour worth testing first: open a translated locale, save it, and read the diff.Does it handle ICU plurals and select forms?
other category is caught before gen_l10n refuses to generate.What about a bare # inside a plural branch?
# renders literally in the UI. StringLane detects the standalone case and leaves deliberate literal usage such as rank #1 alone.Do I need an account or an internet connection?
Price
One purchase, no subscription
$49once
Early-adopter price. Full price after launch is $79.
- All five formats, not just this one
- Live validation while you edit
- AI fill with your own API key, or a local model
- macOS, Windows and Linux
- Updates within the major version
The comparison worth making is against a cloud TMS seat rather than against another editor. Lokalise lists $144 a month at entry and $999 a month on its Advanced plan, checked July 2026. That bill arrives every month. This one does not.
Docs
Read before you download
What is an ARB file?
The format, the @key convention, and how Flutter consumes it.
Working with ARB metadata
@description, x-max-length and x-guarded in practice.
ICU plurals and select forms
Writing and validating plural categories per locale.
Localize a Flutter app
Start to finish, from an empty l10n folder to a translated build.
ARB and i18next are the two ICU formats StringLane validates. If you ship a Flutter app and a React dashboard from one repo, both open in the same window.
i18next JSON editor