Formats

.strings

The legacy per-locale format. anylocale writes a full .lproj tree in one command, one .strings file per table.

What you get

Everything the editor does, through the files you already ship.

  • Every plural form a language has

    Each locale gets exactly the CLDR categories it uses, Arabic six and Japanese one, written in the format's own plural shape.

  • AI drafts, a person verifies

    Every language is drafted against your glossary and each key's length limit. A draft never ships until someone has verified it.

  • Scriptable from the CLI

    Push, pull and sync from a terminal, a build phase or a coding agent's shell, with a token in the environment.

    The CLI
  • Pulls on every build

    A Run Script phase above Compile Sources keeps the target on the latest verified text without anyone remembering to sync.

    Xcode build phase

Set it up

From nothing to every language.

  1. Step 1: Keep reading strings the way the target already does

    NSLocalizedString and String(localized:) look a key up in the current locale's Localizable.strings. The comment argument is what the translators and the AI draft see, so write it for them.

    WelcomeViewController.swift
    import UIKit
    
    final class WelcomeViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            titleLabel.text = NSLocalizedString("welcome_title",
                comment: "Headline on the first screen")
        }
    }
  2. Step 2: Install the CLI and log in

    Homebrew or the one-line installer, then a token issued per project under Settings, Integrations. The read scope pulls; push writes source files and values; manage creates and deletes keys.

    shell
    brew install anylocale/tap/anylocale
    anylocale login --project my-app
  3. Step 3: Push the base .lproj

    The source language's file, as the project keeps it. A block comment above an entry becomes that key's description.

    shell
    curl -fsS -X POST "https://anylocale.com/api/loc/push?project=my-app&format=strings" \
        -H "Authorization: Bearer $ANYLOCALE_TOKEN" \
        --data-binary @en.lproj/Localizable.strings
  4. Step 4: Sync every locale

    Downloads each locale as <locale>.lproj/<Table>.strings, plus a .stringsdict where a table has plurals. Each file lands complete or not at all.

    shell
    anylocale sync --project my-app --out-dir Sources/Resources
    de.lproj/Localizable.strings
    /* Headline on the first screen */
    "welcome_title" = "Willkommen";
    
    /* Explains why the app needs the location */
    "onboarding.location_permission" = "Standort & Wetter für %@ erlauben";
  5. Step 5: Or pull one table of one locale

    The file flag names the string table. It applies to .strings and .stringsdict only.

    shell
    anylocale pull --project my-app --locale de --format strings \
        --file Localizable --out de.lproj/Localizable.strings

Good to know

  • Add --no-comments to sync or pull to leave key descriptions out of the committed files.
  • A trailing \n before a closing quote is reported as a warning; it is almost always an accident.

Questions

Do the comments in the file survive?
Key descriptions are written as the comment above each entry, so a translator reading the file sees the same note the editor shows. Add --no-comments to leave them out of committed files.
Where do the plurals go?
Into a .stringsdict beside the .strings file, written by the same sync for every table that has at least one plural key. A table without plurals gets no .stringsdict.
Do I get unverified text when I pull?
Yes. A pull carries the current text of every key, verified or not, so a build always has something to show. Only over-the-air delivery is limited to verified strings.

Start with the file your app already ships.

After checkout, import one strings file and read the first drafts within minutes.

Nothing to connect and nothing to install. The CLI is optional.