LiveLocalizationKit is designed so you can plug in your own translation backend without changing the UI layer or the shared localizer flow.
Custom providers conform to LocalizationProvider and translate a LocalizationRequest into a LocalizationResponse.
import LiveLocalizationCore
struct MyTranslationProvider: LocalizationProvider {
func translate(_ request: LocalizationRequest) async throws -> LocalizationResponse {
let localizedText = request.sourceText
return LocalizationResponse(localizedText: localizedText)
}
}Use it with the shared flow:
await LiveLocalization.configure(
provider: MyTranslationProvider(),
cacheStore: DiskLocalizationCacheStore(),
cachePolicy: LocalizationCachePolicy(
namespace: "preview",
providerIdentifier: "my-provider"
)
)
let localized = await "Settings".localize()Or create an explicit localizer:
let localizer = LiveLocalizer(provider: MyTranslationProvider())
let localized = await "Checkout".localize(using: localizer)LocalizationRequest contains:
sourceText: the original text to translatesourceLanguageIdentifier: optional source language hinttargetLanguageIdentifier: optional destination language hintcontext: optional domain or UI context
Example:
let localized = await "Checkout".localize(
sourceLanguageIdentifier: "en",
targetLanguageIdentifier: "ja",
context: "paywall.primary_cta"
)If your backend fails, the safest default is to fall back to request.sourceText.
struct SafeProvider: LocalizationProvider {
func translate(_ request: LocalizationRequest) async throws -> LocalizationResponse {
do {
let localizedText = try await callBackend(with: request)
return LocalizationResponse(localizedText: localizedText)
} catch {
return LocalizationResponse(localizedText: request.sourceText)
}
}
private func callBackend(with request: LocalizationRequest) async throws -> String {
request.sourceText
}
}Use SyncLocalizationProvider if your provider can answer immediately and does not require async work.
Good examples:
- pseudo-localization
- passthrough behavior
- deterministic mock translations
- local dictionary lookups
struct DictionaryProvider: SyncLocalizationProvider {
let values: [String: String]
func translateSynchronously(_ request: LocalizationRequest) throws -> LocalizationResponse {
let localizedText = values[request.sourceText] ?? request.sourceText
return LocalizationResponse(localizedText: localizedText)
}
}context is useful when the same source string can mean different things in different screens or product areas.
Examples:
paywall.primary_ctasettings.delete_buttoncheckout.summary_title
If your backend supports prompts, domains, or route selection, context is the right place to attach that information.
LiveLocalizer caches results using:
- source text
- source language identifier
- target language identifier
- context
This means the same source string can safely produce different cached values when the language or UI context changes.
You can attach a LocalizationLogger to observe cache and provider behavior at runtime.
let logger = ClosureLocalizationLogger { event in
print("Localization event:", event)
}
let localizer = LiveLocalizer(
provider: MyTranslationProvider(),
cacheStore: DiskLocalizationCacheStore(),
cachePolicy: LocalizationCachePolicy(providerIdentifier: "my-provider"),
logger: logger
)The runtime can emit events for:
- shared configuration
- cache warmup
- cache hit / miss / write / invalidation
- provider translation start / success / failure fallback
- Keep providers stateless when possible.
- Treat
targetLanguageIdentifieras the strongest routing signal. - Use
contextfor domain-specific prompts or glossary selection. - Return the source text when you need a predictable fallback.
- Keep auth, retry, and rate-limit handling inside the provider implementation rather than pushing that complexity into the UI layer.