Apps SDK — iOS / macOS
Make your iOS or macOS app multilingual with one line at startup and a t()
call per string. UI strings are translated into 90 languages and cached on disk for instant,
offline subsequent launches. No public call ever throws into your app — every failure degrades to
the source text.
swift build + swift test), but
it has never been built on a real Mac or run on an Apple device — that needs Xcode
on a Mac, which the verification machine does not have. iOS / macOS-specific behavior is unverified
and the public API may still change. Not recommended for a shipping app yet —
we'd love your feedback.
Install
- Download the package: transable-ios-0.1.0-preview.1.zip
- Unzip it and add it as a local Swift package: in Xcode, File → Add Package Dependencies… → Add Local…, then check TransableSdk for your app target. (Once the repo is public you'll be able to add it by URL instead — see
PUBLISH.md.) - Requires Swift 5.7+, iOS 14 / macOS 12 or newer. Foundation-only — no third-party dependencies.
- Create an app project in the dashboard and copy your API key.
One-line start
Initialize once at app start (e.g. in your App init / AppDelegate). initialize never throws; it is idempotent — only the first call takes effect:
import TransableSdk
Transable.initialize(apiKey: "tk_live_your_key_here", options: {
var o = Transable.Options()
o.appName = "my-app"
// o.defaultLanguage = "de" // else the user's OS locale is used
return o
}())
Translate a string with one call. t() is synchronous and non-blocking: it returns the cached translation for the current language if known, otherwise your fallback while it queues the text for background translation:
label.text = Transable.t("save_button", "Save")
Re-apply your strings when background translations land:
NotificationCenter.default.addObserver(
forName: Transable.translationsDidUpdateNotification,
object: nil, queue: .main) { _ in
self.updateLabels() // re-read your t() strings and refresh the UI
}
Batch & language switching
A whole screen's strings at once — index-aligned with the input; skipped or failed entries come back as the source text, and it never throws:
let out = await Transable.translateBatch(["Save", "Cancel", "[email protected]"])
// "[email protected]" comes back unchanged — SafeMode filters it BEFORE the
// network, so it never leaves the device.
Switch language at runtime (persisted across launches by default) and react via notifications:
Transable.setLanguage("de") // no-op before init
let code = Transable.currentLanguage // active language code
let langs = Transable.availableLanguages // [LanguageInfo] for a picker
Safe mode & offline cache
Safe mode is on by default and runs before any network I/O, so filtered text never leaves the device: it skips text under 2 chars or with no letters, emails, URLs, file paths, GUIDs, version strings, timestamped log lines, and code-like identifiers (PascalCaseNames) — while single words like "Save" still translate. Never pass the contents of user input fields to t() / translateBatch — the SDK has no UI layer, so that is on you.
Every translation is cached under Application Support/Transable/v1/, per language, content-addressed (10 MB per-language cap), and loaded on the next launch — so the second launch is translated instantly, even with no network. Identity results (translation equals source) are never written to disk.
It never crashes your app
One rule: no public entry point throws, blocks the caller for I/O, or crashes the app. Missing key, no network, HTTP 4xx/5xx, quota exhaustion, a malformed response — every failure degrades to the source-language text, with at most a message on Options.logger. A 401/402/403 disables translation for the session; a monthly-quota 429 pauses it until the next UTC day.
Questions or something broke? Contact us — include your Swift/Xcode version and the package version. Other platforms: Android, Flutter, or any language via the REST API. Full platform list on Apps SDK.