Your build passed.
Your translations didn't.
StringLane opens your project folder and shows every missing key across every locale — flagged before your localization pipeline breaks, before the App Store submission, before the 1-star review.
Saves directly to your files · No account · No cloud
14-day free trial · No email · No account · Works offline

Sound familiar?
You already have a system. Here is why it keeps failing you.
Your toolchain silently skips missing keys
gen_l10n silently skips missing translation keys — no error, no warning, no failed build. Your release pipeline looks like it worked. Your users find out otherwise.
Split panes work at 2 locales. They collapse at 4.
Four locales isn't inconvenient — it's a different problem entirely. You're not comparing files anymore, you're doing archaeology. And the worst failure mode doesn't look like a failure: the key exists, the value is still in English, the build passes, and a German speaker gets English UI — and you find out from a review.
Cloud TMS tools weren't built for you
Lokalise starts at $120/month. Both assume import steps, export steps, review queues, and a team to justify the overhead. You have one developer, source files on disk, and a release in two days.
Ship localization you can trust
Before it ships
{
"@@locale": "en",
"appTitle": "My Flutter App",
"@appTitle": {
"description": "The title of the application"
},
"welcomeMessage": "Welcome, {name}!",
"@welcomeMessage": {
"description": "Greeting shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"itemCount": "{count, plural, one {# item} other {# items}}",
"@itemCount": {
"description": "Number of items in the cart",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
},
"lastSeen": "Last seen {when}",
"@lastSeen": {
"description": "Relative timestamp shown in the activity feed",
"placeholders": {
"when": {
"type": "String",
"example": "2 hours ago"
}
}
},
"settingsTitle": "Settings",
"@settingsTitle": {
"description": "Settings screen title"
},
"saveButton": "Save",
"@saveButton": {
"description": "Primary save action button"
},
"cancelButton": "Cancel",
"@cancelButton": {
"description": "Cancel action button"
},
"deleteConfirm": "Are you sure you want to delete "{label}"?",
"@deleteConfirm": {
"description": "Confirmation prompt before deleting an item",
"placeholders": {
"label": {
"type": "String",
"example": "My note"
}
}
},
"errorGeneric": "Something went wrong. Please try again.",
"@errorGeneric": {
"description": "Fallback error message"
},
"notificationCount": "{count, plural, zero {No} notifications} one {# notification} other {# notifications}}",
"@notificationCount": {
"description": "Notification badge label",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
},
"signOut": "Sign out",
"@signOut": {
"description": "Sign out button label"
}
}{
"@@locale": "de",
"appTitle": "Meine Flutter-App",
"@appTitle": {
"description": "Der Titel der Anwendung"
},
"welcomeMessage": "Willkommen, {name}!",
"itemCount": "{count, plural, one {# Artikel} other {# Artikel}}",
"lastSeen": "Zuletzt gesehen {when}",
"settingsTitle": "Einstellungen",
"saveButton": "Speichern",
"cancelButton": "Abbrechen",
"deleteConfirm": "Möchten Sie "{label}" wirklich löschen?",
"errorGeneric": "",
"notificationCount": "{count, plural, zero {Keine Benachrichtigungen} one {# Benachrichtigung} other {# Benachrichtigungen}}",
"signOut": "Abmelden"
}{
"@@locale": "fr",
"appTitle": "Mon application Flutter",
"welcomeMessage": "Bienvenue, {name} !",
"itemCount": "{count, plural, one {# article} other {# articles}}",
"lastSeen": "Vu pour la dernière fois {when}",
"settingsTitle": "Paramètres",
"saveButton": "Enregistrer",
"cancelButton": "Annuler",
"deleteConfirm": "Voulez-vous vraiment supprimer « {label} » ?",
"errorGeneric": "Une erreur est survenue. Veuillez réessayer.",
"notificationCount": "{count, plural, zero {Aucune notification} one {# notification} other {# notifications}}",
"signOut": "Se déconnecter"
}
{
"@@locale": "uk",
"appTitle": "Мій додаток Flutter",
"cancelButton": "Скасувати",
"deleteConfirm": "Ви впевнені, що хочете видалити «{label}»?",
"errorGeneric": "Щось пішло не так. Будь ласка, спробуйте ще раз.",
"itemCount": "{count, plural, one {# елемент} other {# елементів}}",
"lastSeen": "Останній візит {when}",
"notificationCount": "{count, plural, zero {Немає} сповіщень} one {# сповіщення} other {# сповіщень}}",
"saveButton": "Зберегти",
"settingsTitle": "Налаштування",
"signOut": "Вийти",
"welcomeMessage": "Ласкаво просимо, {name}!"
}{
"@@locale": "ja",
"appTitle": "マイフラッターアプリ",
"cancelButton": "キャンセル",
"deleteConfirm": "「{label}」を削除してもよろしいですか?",
"errorGeneric": "問題が発生しました。もう一度お試しください。",
"itemCount": "{count, plural, one {# 件} other {# 件}}",
"lastSeen": "最終確認:{when}",
"notificationCount": "{count, plural, zero {通知はありません} 通知} one {# 通知} other {# 通知}}",
"saveButton": "保存",
"settingsTitle": "設定",
"signOut": "ログアウト",
"welcomeMessage": "ようこそ、{name}さん!"
}{
"@@locale": "en",
"appTitle": "My Flutter App",
"@appTitle": {
"description": "The title of the application"
},
"welcomeMessage": "Welcome, {name}!",
"@welcomeMessage": {
"description": "Greeting shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"itemCount": "{count, plural, one {# item} other {# items}}",
"@itemCount": {
"description": "Number of items in the cart",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
},
"lastSeen": "Last seen {when}",
"@lastSeen": {
"description": "Relative timestamp shown in the activity feed",
"placeholders": {
"when": {
"type": "String",
"example": "2 hours ago"
}
}
},
"settingsTitle": "Settings",
"@settingsTitle": {
"description": "Settings screen title"
},
"saveButton": "Save",
"@saveButton": {
"description": "Primary save action button"
},
"cancelButton": "Cancel",
"@cancelButton": {
"description": "Cancel action button"
},
"deleteConfirm": "Are you sure you want to delete "{label}"?",
"@deleteConfirm": {
"description": "Confirmation prompt before deleting an item",
"placeholders": {
"label": {
"type": "String",
"example": "My note"
}
}
},
"errorGeneric": "Something went wrong. Please try again.",
"@errorGeneric": {
"description": "Fallback error message"
},
"notificationCount": "{count, plural, zero {No} notifications} one {# notification} other {# notifications}}",
"@notificationCount": {
"description": "Notification badge label",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
},
"signOut": "Sign out",
"@signOut": {
"description": "Sign out button label"
}
}{
"@@locale": "de",
"appTitle": "Meine Flutter-App",
"@appTitle": {
"description": "Der Titel der Anwendung"
},
"welcomeMessage": "Willkommen, {name}!",
"itemCount": "{count, plural, one {# Artikel} other {# Artikel}}",
"lastSeen": "Zuletzt gesehen {when}",
"settingsTitle": "Einstellungen",
"saveButton": "Speichern",
"cancelButton": "Abbrechen",
"deleteConfirm": "Möchten Sie "{label}" wirklich löschen?",
"errorGeneric": "",
"notificationCount": "{count, plural, zero {Keine Benachrichtigungen} one {# Benachrichtigung} other {# Benachrichtigungen}}",
"signOut": "Abmelden"
}{
"@@locale": "fr",
"appTitle": "Mon application Flutter",
"welcomeMessage": "Bienvenue, {name} !",
"itemCount": "{count, plural, one {# article} other {# articles}}",
"lastSeen": "Vu pour la dernière fois {when}",
"settingsTitle": "Paramètres",
"saveButton": "Enregistrer",
"cancelButton": "Annuler",
"deleteConfirm": "Voulez-vous vraiment supprimer « {label} » ?",
"errorGeneric": "Une erreur est survenue. Veuillez réessayer.",
"notificationCount": "{count, plural, zero {Aucune notification} one {# notification} other {# notifications}}",
"signOut": "Se déconnecter"
}
{
"@@locale": "uk",
"appTitle": "Мій додаток Flutter",
"cancelButton": "Скасувати",
"deleteConfirm": "Ви впевнені, що хочете видалити «{label}»?",
"errorGeneric": "Щось пішло не так. Будь ласка, спробуйте ще раз.",
"itemCount": "{count, plural, one {# елемент} other {# елементів}}",
"lastSeen": "Останній візит {when}",
"notificationCount": "{count, plural, zero {Немає} сповіщень} one {# сповіщення} other {# сповіщень}}",
"saveButton": "Зберегти",
"settingsTitle": "Налаштування",
"signOut": "Вийти",
"welcomeMessage": "Ласкаво просимо, {name}!"
}{
"@@locale": "ja",
"appTitle": "マイフラッターアプリ",
"cancelButton": "キャンセル",
"deleteConfirm": "「{label}」を削除してもよろしいですか?",
"errorGeneric": "問題が発生しました。もう一度お試しください。",
"itemCount": "{count, plural, one {# 件} other {# 件}}",
"lastSeen": "最終確認:{when}",
"notificationCount": "{count, plural, zero {通知はありません} 通知} one {# 通知} other {# 通知}}",
"saveButton": "保存",
"settingsTitle": "設定",
"signOut": "ログアウト",
"welcomeMessage": "ようこそ、{name}さん!"
}{
"@@locale": "en",
"appTitle": "My Flutter App",
"@appTitle": {
"description": "The title of the application"
},
"welcomeMessage": "Welcome, {name}!",
"@welcomeMessage": {
"description": "Greeting shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"itemCount": "{count, plural, one {# item} other {# items}}",
"@itemCount": {
"description": "Number of items in the cart",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
},
"lastSeen": "Last seen {when}",
"@lastSeen": {
"description": "Relative timestamp shown in the activity feed",
"placeholders": {
"when": {
"type": "String",
"example": "2 hours ago"
}
}
},
"settingsTitle": "Settings",
"@settingsTitle": {
"description": "Settings screen title"
},
"saveButton": "Save",
"@saveButton": {
"description": "Primary save action button"
},
"cancelButton": "Cancel",
"@cancelButton": {
"description": "Cancel action button"
},
"deleteConfirm": "Are you sure you want to delete "{label}"?",
"@deleteConfirm": {
"description": "Confirmation prompt before deleting an item",
"placeholders": {
"label": {
"type": "String",
"example": "My note"
}
}
},
"errorGeneric": "Something went wrong. Please try again.",
"@errorGeneric": {
"description": "Fallback error message"
},
"notificationCount": "{count, plural, zero {No} notifications} one {# notification} other {# notifications}}",
"@notificationCount": {
"description": "Notification badge label",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
},
"signOut": "Sign out",
"@signOut": {
"description": "Sign out button label"
}
}{
"@@locale": "de",
"appTitle": "Meine Flutter-App",
"@appTitle": {
"description": "Der Titel der Anwendung"
},
"welcomeMessage": "Willkommen, {name}!",
"itemCount": "{count, plural, one {# Artikel} other {# Artikel}}",
"lastSeen": "Zuletzt gesehen {when}",
"settingsTitle": "Einstellungen",
"saveButton": "Speichern",
"cancelButton": "Abbrechen",
"deleteConfirm": "Möchten Sie "{label}" wirklich löschen?",
"errorGeneric": "",
"notificationCount": "{count, plural, zero {Keine Benachrichtigungen} one {# Benachrichtigung} other {# Benachrichtigungen}}",
"signOut": "Abmelden"
}{
"@@locale": "fr",
"appTitle": "Mon application Flutter",
"welcomeMessage": "Bienvenue, {name} !",
"itemCount": "{count, plural, one {# article} other {# articles}}",
"lastSeen": "Vu pour la dernière fois {when}",
"settingsTitle": "Paramètres",
"saveButton": "Enregistrer",
"cancelButton": "Annuler",
"deleteConfirm": "Voulez-vous vraiment supprimer « {label} » ?",
"errorGeneric": "Une erreur est survenue. Veuillez réessayer.",
"notificationCount": "{count, plural, zero {Aucune notification} one {# notification} other {# notifications}}",
"signOut": "Se déconnecter"
}
{
"@@locale": "uk",
"appTitle": "Мій додаток Flutter",
"cancelButton": "Скасувати",
"deleteConfirm": "Ви впевнені, що хочете видалити «{label}»?",
"errorGeneric": "Щось пішло не так. Будь ласка, спробуйте ще раз.",
"itemCount": "{count, plural, one {# елемент} other {# елементів}}",
"lastSeen": "Останній візит {when}",
"notificationCount": "{count, plural, zero {Немає} сповіщень} one {# сповіщення} other {# сповіщень}}",
"saveButton": "Зберегти",
"settingsTitle": "Налаштування",
"signOut": "Вийти",
"welcomeMessage": "Ласкаво просимо, {name}!"
}{
"@@locale": "ja",
"appTitle": "マイフラッターアプリ",
"cancelButton": "キャンセル",
"deleteConfirm": "「{label}」を削除してもよろしいですか?",
"errorGeneric": "問題が発生しました。もう一度お試しください。",
"itemCount": "{count, plural, one {# 件} other {# 件}}",
"lastSeen": "最終確認:{when}",
"notificationCount": "{count, plural, zero {通知はありません} 通知} one {# 通知} other {# 通知}}",
"saveButton": "保存",
"settingsTitle": "設定",
"signOut": "ログアウト",
"welcomeMessage": "ようこそ、{name}さん!"
}{
"@@locale": "en",
"appTitle": "My Flutter App",
"@appTitle": {
"description": "The title of the application"
},
"welcomeMessage": "Welcome, {name}!",
"@welcomeMessage": {
"description": "Greeting shown on the home screen",
"placeholders": {
"name": {
"type": "String",
"example": "Alice"
}
}
},
"itemCount": "{count, plural, one {# item} other {# items}}",
"@itemCount": {
"description": "Number of items in the cart",
"placeholders": {
"count": {
"type": "int",
"example": "3"
}
}
},
"lastSeen": "Last seen {when}",
"@lastSeen": {
"description": "Relative timestamp shown in the activity feed",
"placeholders": {
"when": {
"type": "String",
"example": "2 hours ago"
}
}
},
"settingsTitle": "Settings",
"@settingsTitle": {
"description": "Settings screen title"
},
"saveButton": "Save",
"@saveButton": {
"description": "Primary save action button"
},
"cancelButton": "Cancel",
"@cancelButton": {
"description": "Cancel action button"
},
"deleteConfirm": "Are you sure you want to delete "{label}"?",
"@deleteConfirm": {
"description": "Confirmation prompt before deleting an item",
"placeholders": {
"label": {
"type": "String",
"example": "My note"
}
}
},
"errorGeneric": "Something went wrong. Please try again.",
"@errorGeneric": {
"description": "Fallback error message"
},
"notificationCount": "{count, plural, zero {No} notifications} one {# notification} other {# notifications}}",
"@notificationCount": {
"description": "Notification badge label",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
},
"signOut": "Sign out",
"@signOut": {
"description": "Sign out button label"
}
}{
"@@locale": "de",
"appTitle": "Meine Flutter-App",
"@appTitle": {
"description": "Der Titel der Anwendung"
},
"welcomeMessage": "Willkommen, {name}!",
"itemCount": "{count, plural, one {# Artikel} other {# Artikel}}",
"lastSeen": "Zuletzt gesehen {when}",
"settingsTitle": "Einstellungen",
"saveButton": "Speichern",
"cancelButton": "Abbrechen",
"deleteConfirm": "Möchten Sie "{label}" wirklich löschen?",
"errorGeneric": "",
"notificationCount": "{count, plural, zero {Keine Benachrichtigungen} one {# Benachrichtigung} other {# Benachrichtigungen}}",
"signOut": "Abmelden"
}{
"@@locale": "fr",
"appTitle": "Mon application Flutter",
"welcomeMessage": "Bienvenue, {name} !",
"itemCount": "{count, plural, one {# article} other {# articles}}",
"lastSeen": "Vu pour la dernière fois {when}",
"settingsTitle": "Paramètres",
"saveButton": "Enregistrer",
"cancelButton": "Annuler",
"deleteConfirm": "Voulez-vous vraiment supprimer « {label} » ?",
"errorGeneric": "Une erreur est survenue. Veuillez réessayer.",
"notificationCount": "{count, plural, zero {Aucune notification} one {# notification} other {# notifications}}",
"signOut": "Se déconnecter"
}
{
"@@locale": "uk",
"appTitle": "Мій додаток Flutter",
"cancelButton": "Скасувати",
"deleteConfirm": "Ви впевнені, що хочете видалити «{label}»?",
"errorGeneric": "Щось пішло не так. Будь ласка, спробуйте ще раз.",
"itemCount": "{count, plural, one {# елемент} other {# елементів}}",
"lastSeen": "Останній візит {when}",
"notificationCount": "{count, plural, zero {Немає} сповіщень} one {# сповіщення} other {# сповіщень}}",
"saveButton": "Зберегти",
"settingsTitle": "Налаштування",
"signOut": "Вийти",
"welcomeMessage": "Ласкаво просимо, {name}!"
}{
"@@locale": "ja",
"appTitle": "マイフラッターアプリ",
"cancelButton": "キャンセル",
"deleteConfirm": "「{label}」を削除してもよろしいですか?",
"errorGeneric": "問題が発生しました。もう一度お試しください。",
"itemCount": "{count, plural, one {# 件} other {# 件}}",
"lastSeen": "最終確認:{when}",
"notificationCount": "{count, plural, zero {通知はありません} 通知} one {# 通知} other {# 通知}}",
"saveButton": "保存",
"settingsTitle": "設定",
"signOut": "ログアウト",
"welcomeMessage": "ようこそ、{name}さん!"
}See every locale gap at a glance
StringLane opens your folder, groups keys by namespace in the sidebar, and renders every locale for the active key side-by-side in the detail pane. No more toggling between files or holding three editor tabs in your head at once.
"notificationCount" in de — unclosed brace
"errorGeneric" exists in base but missing in de
Placeholder mismatch in "notificationCount" for uk
Know every gap before you submit
Gaps are flagged the moment they exist — not after a build fails, not after a 1-star review. You see what's wrong before you touch the build command.
On branch mainChanges not staged for commit:modified: lib/l10n/en.arbmodified: lib/l10n/de.arbmodified: lib/l10n/uk.arb[main 4a2f91c] l10n: add missing translations3 files changed, 24 insertions(+), 2 deletions(-)Edit, save, commit — nothing else
StringLane writes directly to your ARB, .strings, XML, or JSON files on every keystroke. Your git workflow stays exactly as it is. There is no export step, no sync button, no intermediate format to manage.
Fill all missing translations — with any AI model you already use, cloud or local
Bring your own API key and bulk-translate every missing string across every locale at once. OpenAI, Anthropic, Google Gemini, Groq, Mistral, DeepSeek, xAI — or run fully offline against any OpenAI-compatible local runner (Ollama, LM Studio, Jan, llama.cpp). The Issues Panel's "Fix all with AI" sweep covers ICU errors and placeholder mismatches in the same pass. No per-character credits, no subscription layered on top.
How it works
From open folder to ship-ready in minutes
Features
Purpose-built for localization — not bolted onto a generic editor
Pricing
One price. No subscription. Yours forever.
Free Trial
Full app, all features. No email, no account, works offline.
- All five formats: ARB, .strings, .xcstrings, XML, JSON
- Side-by-side locale editor
- Real-time ICU & placeholder validation
- AI translation — BYOK, no per-character fees
- Auto-save directly to source files
- 14 days · no credit card · works offline
Full License
Full price after launch: $79
One-time purchase. No annual renewal. No seat fee.
- Everything in Free Trial
- Lifetime license — pay once, use forever
- 3 machine activations
- All future updates within major version
- Priority email support
- Secured at early-adopter price ($79 after launch)
Instant download after purchase
Need more than 3 activations? Contact us and we'll sort it out.
Find out what's missing before your users do
StringLane shows every gap across all your locale files before you push a single commit. One purchase, no subscription, yours to keep.
Start free 14-day trialEarly-adopter price: $49 one-time. Full price is $79.