iOS .STRINGS AND .STRINGSDICT · macOS, WINDOWS, LINUX
A Localizable.strings editor for every .lproj at once
StringLane collects every .lproj folder in your project and shows them side by side, checks format specifiers across languages, and writes plural forms to the companion .stringsdict on save.
For macOS, Windows and Linux14-day trial, no email · $49 once

Ukrainian few and many, listed as missing
THE FORMAT
What StringLane opens
The classic Apple layout splits localization two ways. Values live in one Localizable.strings per language, as "key" = "value"; pairs, and plurals live in a separate .stringsdict plist because the .strings format cannot express them. StringLane reads both and treats them as one project, so plural keys sit in the same table as everything else.
- File layout
- One folder per language,
en.lproj/Localizable.strings - Plurals
- Companion
.stringsdictplist, written alongside on save - Placeholders
%@,%d,%ld, and positional forms such as%1$@- Comments
/* ... */blocks above each key, passed through intact- Detected by
.stringsand.stringsdictfiles under.lprojfolders- Project chip
STR
WHAT GETS FLAGGED
What gets flagged in a .strings project
Validation runs when the project loads and again after every edit. These are the checks that apply to .strings and its .stringsdict companion. The same checks run from the terminal with stringlane check, and over MCP for a coding agent.
Missing or empty per language
The key exists in your base language and is absent or blank in another
.lproj. Nothing in the Xcode build stops you shipping that, which is why it is the first column of the table.missing_keyFormat specifier mismatch
Specifiers are compared by count and by the type each argument is read as. A base string with
%@and%ldagainst a translation carrying one specifier is flagged as an error, and so is%d itemsagainst%@ Elemente, which is undefined behaviour on iOS.%ldand%lldread the same type;%dand%lddo not. Positional forms like%1$@and%2$@are recognised.format_placeholder_mismatchPlural coverage against CLDR
Every
.stringsdictplural is checked against the categories its language actually requires. Ukrainian needsone,few,manyandother. English needs two. A Ukrainian entry carrying only the English pair is flagged, and you do not have to read Ukrainian to act on it. A language that holds its own plural forms in.stringsdictfor a key your base language keeps as one.stringsstring is checked form by form, because Foundation reads the.stringsdictentry first.missing_keyWidth and device variants
Width variants (
NSStringVariableWidthRuleType) and device variants (NSStringDeviceSpecificRuleType) written directly under the key, as Apple documents, are read and each one is checked against the base string for the same width or device. A width entry framed under a format variable such as%#@w@is flagged, because Foundation does not resolve that layout: the app shows%#@w@, and it can crash. An entry with both width and device variants is kept exactly as it is and left for Xcode to edit.framed_width_variantSame as base, and guarded words
A value identical to the base language usually means it was copied rather than translated. Terms you mark as guarded are flagged in the opposite direction, when a language changes something that should have stayed fixed.
untranslatedguarded_word_violation
What it will not do
ICU MessageFormat validation does not apply to this format and is switched off for it. Apple .strings values are not ICU messages, and plurals live in the .stringsdict structure rather than inline. Plural coverage is still checked, through CLDR.
YOUR FILES
Your files come back the way you left them
StringLane writes to your source files directly, in place, with no export step.
/* comment */blocks above each key are passed through untouched.- Plural keys are written to the companion
.stringsdictalongside the.stringsfile, generated only when the file actually has plurals. - In a multi-target workspace, each key is saved back to the file it came from rather than merged into one.
- Quoting style and file structure are preserved, so the diff shows only the values you changed.
en.lproj/Localizable.strings
/* Personalised greeting on the home tab. */
"home.welcomeBack" = "Welcome back, %@!";
/* Running total at the bottom of the cart. */
"cart.total" = "Total: %@";
/* Positional: translators may reorder the two arguments. */
"transfer.summary" = "Sent %1$@ to %2$@";
"cart.checkout" = "Check out";YOUR TOOLCHAIN
Where it sits in an Xcode project
StringLane never opens your .xcodeproj or .xcworkspace. It reads the .lproj folders on disk and writes back to them, so Xcode picks the changes up the way it would if you had typed them yourself.
Multi-target workspaces load as one project. A main app, a shared module and a watch extension each with their own .lproj cluster are collected together, with keys prefixed by their origin folder so namespaces stay separate. MyApp/Core/Assets/en.lproj/Localizable.strings shows up as MyApp.Core.Assets.<key>. Non-standard filenames inside .lproj, such as SharedLocalizable.strings, are picked up too.
Let your coding agent check the same files.
The free CLI reads the same .lproj folders and .stringsdict files.
Read https://stringlane.app/docs/cli/agent-setup.md and set up StringLane for this project.Pay once for the app. The CLI is free.
Desktop app
$49once
- 14-day trial, no email, no card
- macOS, Windows and Linux
- 3 machines, updates in the major version
- Your own AI key, or a local model
Early-adopter price, $79 after launch.
CLI & agents
$0free
- Terminal, CI and MCP
- Claude Code, Cursor, Codex and 5 more
- No key, no activation
- Separate from the app licence
Lokalise starts at $144 a month (checked July 2026). StringLane sells no AI credits.
iOS .strings questions
Does it handle .stringsdict plural files?
.stringsdict next to the .strings file. The companion is only written when the file actually has plural keys, so a project without plurals does not gain a file it never had. Width and device variants in Apple’s documented layout are read and edited too. A .stringsdict damaged by a git conflict or a cut-off write is reported as a load error naming the file, and StringLane does not write over it.Will it rewrite my comments or reformat the file?
/* comment */ blocks above each key are passed through intact. StringLane writes back the values you edited and leaves the rest of the file structure alone.I have several targets, each with its own .lproj folders. Does that load?
.lproj cluster, and prefixes each key with its origin folder so two targets can use the same key name without colliding. Saves go back to each origin file separately.Should I be using String Catalogs instead?
.xcstrings page covers what changes.Do I need a Mac?
.strings files are plain text. You will still need a Mac to build the app. The Windows build is not code-signed yet, so SmartScreen shows a warning on first run.Read before you download
Opening an Xcode project
How
.lprojclusters and multi-target workspaces load.Format-specific editing
What changes per format, including iOS
.strings.Fixing placeholder mismatches
Reading the badge tooltip and correcting a dropped specifier.
Reading validation badges
What each badge means and how to clear it.
Key descriptions and limits
Editing the
/* */above an entry from inside the editor.
If your project has moved to .xcstrings, that page covers the single-file catalog and the per-string translation state model Apple added with it.
Check your locales tonight.
For macOS, Windows and Linux14-day trial, no email · The agent setup is free