Platforms

iOS

Keep localizing through the files Xcode already builds. anylocale reads the string catalog or the .lproj tree and hands back every language.

What you get

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

  • 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.

  • 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.

  • Updates over the air

    Publish the verified strings and let the running app fetch them, so a fixed translation reaches users without a store release.

    Over-the-air delivery
  • 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
  • One catalog, every platform

    The iOS app, the Android app and the web app share one project. Tag the keys only one of them uses and pull that subset alone.

  • 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

Set it up

From nothing to every language.

  1. Step 1: Keep writing SwiftUI as you do

    Every Text("key") with a comment lands in the string catalog on the next build. That catalog is what anylocale reads, so the source language stays in the code where it is written.

    ContentView.swift
    Text("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 source language

    From a string catalog or from the base .lproj. Descriptions in the file become the notes translators and the AI draft see.

    shell
    curl -fsS -X POST "https://anylocale.com/api/loc/push?project=my-app&format=xcstrings" \
        -H "Authorization: Bearer $ANYLOCALE_TOKEN" \
        --data-binary @Localizable.xcstrings
  4. Step 4: Pull every language into the target

    Which command depends on what the target reads. Add --platform ios to leave out keys tagged for Android.

    One file carries every locale, so one pull replaces the catalog in place.

    shell
    anylocale pull --project my-app --locale en --format xcstrings \
        --platform ios --out Localizable.xcstrings
  5. Step 5: Make it a build phase

    So every build carries the latest text. The Xcode build phase guide has the script for both layouts.

  6. Step 6: Add the Swift package

    Swift Package Manager, iOS 16 and macOS 13 upward, Xcode 16. In Xcode: File, Add Package Dependencies, enter the URL and add the Anylocale library to your target. Or declare it in Package.swift.

    Package.swift
    dependencies: [
        .package(url: "https://github.com/anylocale/anylocale-swift", from: "1.1.0")
    ],
    targets: [
        .target(name: "MyApp", dependencies: [
            .product(name: "Anylocale", package: "anylocale-swift")
        ])
    ]
  7. Step 7: Initialise once, fetch on launch

    Under Delivery, publish a distribution and create a key for the iOS SDK; its URL is the only configuration. initialize loads the cached strings synchronously, remoteFetch downloads the current language and answers a 304 when nothing changed.

    MyApp.swift
    import SwiftUI
    import Anylocale
    
    @main
    struct MyApp: App {
        init() {
            Anylocale.shared.initialize(
                cdn: URL(string: "https://anylocale.com/ota/v1/<distribution-key>")!)
        }
    
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .task { await Anylocale.shared.remoteFetch() }
            }
        }
    }
  8. Step 8: Read strings

    Two ways to get the downloaded text on screen. Both fall back to the strings compiled into the app for any key the distribution does not carry.

    Explicit calls, no runtime patching. AnylocaleText re-renders when a download lands, translate takes format arguments and a table name.

    ContentView.swift
    import SwiftUI
    import Anylocale
    
    struct ContentView: View {
        var body: some View {
            VStack {
                AnylocaleText("welcome_title")
                Text(Anylocale.shared.translate("items_count", 5))
                Text(Anylocale.shared.translate("hello", table: "common"))
            }
        }
    }

Good to know

  • Placeholders such as %@ and %lld are stored in a platform-neutral form and rendered back in Apple syntax.
  • Languages must exist in the Xcode project; a sync does not add a localization the target does not declare.
  • A SwiftUI Text resolves its string when it first renders, so a download shows on the next launch; AnylocaleText updates in place.

Questions

Do I need the SDK?
No. Without it, the text ships with each build through sync, and nothing changes in how the app reads strings. The SDK adds one thing: fetching the verified strings between releases, so a fix reaches users without a store update.Over-the-air delivery
Does the SDK work with SwiftUI?
Yes. AnylocaleText re-renders the moment a download lands. With swizzling on, a plain Text("key"), Label and NSLocalizedString resolve through the SDK, and a download shows on the next launch.
String catalog or .lproj folders?
Either. A string catalog is pushed and pulled whole; a legacy target syncs its .lproj tree with one .strings and, where there are plurals, one .stringsdict per table. Pull whichever your target reads.
Which iOS versions does the SDK support?
iOS 16 and macOS 13 upward, built with Xcode 16, as a Swift package. Older deployment targets keep using sync without the SDK.

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.