One place for every locale
No more file-by-file
Stop adding the same key to five files by hand. StringLane shows every locale side-by-side, fills the gaps with your own AI key, and saves straight to your source files — no cloud, no export step
IDE for localization files · Local-first · Works offline
14-day free trial · No email · No account
Sound familiar?
You already have a system. Here is why it keeps failing you.
Add one string. Now edit it five times.
A new key means opening every locale file and pasting the same key name into each one by hand — hoping you didn't typo it in de.arb or skip ja.arb entirely. The work grows with every language you support.
Comparing locales by hand is archaeology.
Open de.arb, memorize a key, switch to fr.arb, search for it, switch to ja.arb, lose your place, start over. At four locales you're not editing anymore — you're cross-referencing files in your head.
Your build passed. The translation didn't.
The key exists in every file, but the value is still in English. gen_l10n compiles clean, the tests pass, and a German user gets English UI — you find out from a 1-star review, not your toolchain.
One editor that keeps every locale in sync
Add, compare, and fill every language from a single view
{
"@@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}さん!"
}Add a key once — every locale, one view
StringLane opens your folder, groups keys by namespace in the sidebar, and renders every locale for the active key side-by-side. Create a key and it lands in all of them at once, ready to fill — no pasting the same key name into five files, no toggling between editor tabs.
"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
The localization IDE — built for locale files, 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.
Stop juggling locale files by hand
StringLane adds a key to every locale at once and shows them side-by-side — edits saved straight to your source files, no cloud, no export step. One purchase, no subscription, yours to keep.
Start free 14-day trialEarly-adopter price: $49 one-time. Full price is $79.