Transifex iOS SDK is a collection of tools for easily localizing iOS applications using Transifex Native.

The SDK can fetch translations over the air (OTA), manage an internal cache of translations, and work seamlessly without requiring the developer to change the app's source code.

Both Objective-C and Swift projects are supported, and iOS 10+ is required.

The package is built using Swift 5.3, as it currently requires a bundled resource (introduced in version 5.3) to be present. An update that will require a lower Swift version is currently WIP.

SwiftUI is also supported as of v2.0.3.

Learn more about Transifex Native.

The full SDK documentation is available at https://transifex.github.io/transifex-swift/.

Minimum Requirements

SwiftXcodePlatforms
Swift 5.3Xcode 15.4iOS 12.0, watchOS 4.0, tvOS 12.0, macOS 10.13

Usage

The SDK allows you to keep using the same localization hooks that the iOS framework provides, such as NSLocalizedString, String.localizedStringWithFormat(format:...), etc., while taking advantage of the features that Transifex Native offers, such as OTA translations.

Below, you can find examples of the SDK initialization both in Swift and Objective-C for an app that uses the English language (en) as its source locale and it's localized both in Greek (el) and French (fr).

Keep in mind that in the sample codes below, you will have to replace the<transifex_token> and <transifex_secret> with the actual token and secret associated with your Transifex project and resource.

SDK configuration (Swift)

To complete the setup, you will need to add "Transifex" as a package dependency in Xcode by selecting your project, heading over to the 'Swift Packages' section, tapping on the '+' button, and entering the public repository URL in the search field.

Initialize the SDK in your application using the transifex_token provided by your Transifex Native project.

Here is a basic configuration example in Swift:

import Transifex

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        TXNative.initialize(
            locales: TXLocaleState(sourceLocale: "en",
                                   appLocales: ["en", "el", "fr"]),
            token: "<transifex_token>"
        )

        return true
    }
}

And a more complex one, defining a policy for handling missing translations and providing a secret for programmatically pushing strings.

import Transifex

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        TXNative.initialize(
            locales: TXLocaleState(sourceLocale: "en",
                                   appLocales: ["en", "el", "fr"]),
            token: "<transifex_token>",
            secret: "<transifex_secret>",
            missingPolicy: TXCompositePolicy(
                TXPseudoTranslationPolicy(),
                TXWrappedStringPolicy(start: "[", end: "]")
            )
        )

        /// Optional: Fetch translations on launch
        TXNative.fetchTranslations()
        return true
    }
}

For Swift projects, you will also need to copy the TXNativeExtensions.swift file in your project and include it in all of the targets that call any of the following Swift methods:

  • String.localizedStringWithFormat(format:...)
  • NSString.localizedStringWithFormat(format:...)

If none of your application targets call any of the above methods, then you don't need to
add this file to your project.

If you are also interested in setting up the SDK for your application extensions, you can look into the related section in the documentation. The documentation also covers special cases, such as providing a custom NSURLSession or configuring logging.

SDK configuration (Objective-C)

@import Transifex;

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    TXLocaleState *localeState = [[TXLocaleState alloc] initWithSourceLocale:@"en"
                                                                  appLocales:@[
                                                                    @"en",
                                                                    @"el",
                                                                    @"fr"
                                                                  ]
                                                       currentLocaleProvider:nil];
    TXPseudoTranslationPolicy *pseudoTranslationPolicy = [TXPseudoTranslationPolicy new];
    TXWrappedStringPolicy *wrappedStringPolicy = [[TXWrappedStringPolicy alloc] initWithStart:@"["
                                                                                          end:@"]"];
    TXCompositePolicy *compositePolicy = [[TXCompositePolicy alloc] init:@[
        pseudoTranslationPolicy,
        wrappedStringPolicy
    ]];

    [TXNative initializeWithLocales:localeState
                              token:@"<transifex_token>"
                             secret:@"<transifex_secret>"
                            cdsHost:nil
                            session:nil
                              cache:nil
                      missingPolicy:compositePolicy
                        errorPolicy:nil
                  renderingStrategy:TXRenderingStategyPlatform
                             logger:nil
                         filterTags:nil
                       filterStatus:nil];

    /// Optional: Fetch translations on launch
    [TXNative fetchTranslations:nil
                           tags:nil
                         status:nil
              completionHandler:nil];

    return YES;
}

Alternative initialization

If you want your application to make use of the default behavior, you can initialize the SDK using a more straightforward initialization method:

Swift

TXNative.initialize(
    locales: localeState,
    token: "<transifex_token>"
)

Objective-C

[TXNative initializeWithLocales:localeState
                          token:@"<transifex_token>"];

Fetching translations

As soon as fetchTranslations is called, the SDK will attempt to download the translations for all locales that are defined in the initialization of TXNative.

The fetchTranslations method in the above examples is called as soon as the application launches, but that's not required. Depending on the application, the developer might call that method whenever appropriate (for example, each time the application is brought to the foreground or when the internet connectivity is established).

💡

FAQS regarding FetchTranslations

What happens regarding limitations when Transifex receives multiple user requests for fetching translations?

The fetchTranslations method accepts a callback that reports any errors that may be generated, including those reported by the server, such as the HTTP error code 429 that indicates rate limiting as well as when the SDK logic has reached its limit of retries (20). So, it’s up to developers to decide how these errors will be handled.


Custom handling: is there any callback in fetchTranslations we could take advantage of to decide how to handle any updates on runtime? For example showing a banner to inform the users that they need to relaunch the app.

Yes. fetchTranslations accepts an optional callback that reports the downloaded translations and the generated errors during the pull logic. If you want to show a banner when new translations are available then you will have to cache and compare the translation structure reported back by the callback.

Apart from when the app is restarted, what level of customization do we have?

As soon as fetchTranslations() is called, the SDK will attempt to download both the source locale strings and the translations for the supported locales. If successful, it will update the cache.
The fetchTranslations() method in the SDK configuration example is called as soon as the application launches, but that’s not required. Depending on the application, the developer might choose to call that method whenever it is most appropriate (for example, each time the application is brought to the foreground or when the internet connectivity is established).

Pushing source content programmatically

To push the source translations to CDS, you will first need to prepare an array of TXSourceString objects that will hold all the necessary information required for CDS.

You can refer to the TXSourceString class for more information, or you can look at the
list below:

  • key (required): The key of the source string, generated via the public txGenerateKey()
    method.
  • sourceString (required): The actual source string.
  • developerComment (optional): An optional comment provided by the developer to
    assist the translators.
  • occurrencies (required): A list of relative paths where the source string is located in
    the project.
  • tags (optional): An optional list of tags that will appear alongside the source string in
    the Transifex dashboard.
  • characterLimit (required): The character limits the translations must stay under, provided as context for each string.
  • context (optional): An optional list of strings that provide more context.

After building an array of TXSourceString objects, use the pushTranslations method to push them to CDS. You can optionally set the purge argument to true (defaults to false) to replace the entire resource content. The completion handler can be used to notify asynchronously whether the request was successful.

Pushing source content using the CLI

Use the Transifex CLI-swift to collect all your app content and send it to Transifex for translation. To perform this action, you will need the transifex_secret token that you created in your Transifex Native project.

txios-cli push --token <transifex_token> --secret <transifex_secret> --project MyApp.xcodeproj

You may also use the --excluded-files option in the push command, providing a space-separated list of filenames to be excluded from processing.

Example:

txios-cli push ... --excluded-files ExcludedFile1.strings ExcludedFile2.strings

For more details and additional options, please refer to the documentation related to Transifex CLI-swift.

Pushing pluralizations

Pluralization rules both on the new String Catalogs (.xcstrings) and the old Strings Dictionary (.stringsdict) formats are generally supported.

When pushed to CDS, single, plural rules are converted to the ICU format. In contrast, more complex rules (device variations, substitutions, combinations) are converted to an intermediate XML format and back to the proper format when the SDK populates its cache during initialization.

The only variation-related limitation concerns the Width variants 1 2.

Display translated content

By default, the iOS Native SDK uses the current locale set on the iOS device and listens for changes to the current locale.

Developers can override this setting by providing a custom class that conforms to the TXCurrentLocaleProvider protocol and returns a specific locale code in the currentLocale() method.

This custom locale provider can then be provided during the initialization of the TXLocaleState object as its final argument (currentLocaleProvider):

Swift example:

class CustomLocaleProvider : TXCurrentLocaleProvider {
    func currentLocale() -> String {
        return "el"
    }
}

let locales = TXLocaleState(sourceLocale: "en",
                            appLocales: ["en", "el"],
                            currentLocaleProvider: CustomLocaleProvider())

TXNative.initialize(locales: locales,
                    token: "<token>")

Objective-C example:

@interface CustomLocaleProvider : NSObject <TXCurrentLocaleProvider>

@end

@implementation CustomLocaleProvider

- (NSString *)currentLocale {
    return @"el";
}

@end

/// ...

TXLocaleState *locales = [[TXLocaleState alloc] initWithSourceLocale:@"en"
                                                          appLocales:@[@"en", @"el"]
                                               currentLocaleProvider:customLocale];

[TXNative initializeWithLocales:locales
                          token:@"<token>"];

It is worth noting that the iOS SDK manages an internal cache of translations in the file system of the translations fetched over the air.

You can find more about caching in the documentation.

Standard Cache

The default cache strategy used by the SDK, if the developer provides no other cache, is the TXStandardCache.getCache(). The standard cache operates by using the publicly exposed classes and protocols from the cache.swift file of the SDK, so it's easy to construct another cache strategy if desired.

The standard cache is initialized with a memory cache (TXMemoryCache) that manages all cached entries in memory. After the memory cache gets initialized, it tries to look up if there are any already stored cache files in the file system using the TXDiskCacheProvider class:

  • The first cache provider is the bundle cache provider, which looks for an already created cache file in the app's main application bundle that the developer may have offered.
  • The second cache provider looks for a cache file in the application sandbox directory (using the optional app group identifier argument if provided) in case the app has already downloaded the translations from the server during a previous launch.

Those two providers are used to initialize the memory cache using an update policy (TXCacheUpdatePolicy) which is optionally provided by the developer and defaults to the replaceAll value. After the cached entries have updated the memory cache, the cache is ready to be used.

Whenever new translations are fetched from the server using the fetchTranslations() method, the standard cache is updated, and those translations are stored as-is in the file system in the same cache file used by the aforementioned second cache provider so that they are available on the next app launch.

Alternative cache strategy

You might want to update the internal memory cache when the newly downloaded translations are available. Always update all entries so that the updated policy can also be omitted.

To achieve that, you can create a new TXDecoratorCache subclass or create a method that returns a TXCache instance, just like in the TXStandardCache.getCache()case.

func getCustomCache() -> TXCache {
    return TXFileOutputCacheDecorator(
        fileURL: ...,
        internalCache: ...
    )
}

This way, whenever the cache is updated with the new translations from the fetchTranslations() method, the update() call will propagate to the internal TXMemoryCache and update all of its entries.

Application Extensions

To add the SDK to an application extension target, be sure to include the Transifex library in the 'Frameworks and Libraries' section of the General settings of the application extension you are working on.

Furthermore, in case Xcode produces a "No such module 'Transifex'" error on the import Transifex statements of the extension files, be sure to add the $(SRCROOT) path in the 'Framework Search Paths' setting under the Build Settings of the application extension target.

To make the Transifex SDK cache file visible by both the extension and the main application targets, you would need to enable the App Groups capability in both the main and extension targets and use an existing or create a new app group identifier. Then, you would need to initialize the Transifex SDK with the TXStandardCache passing that app group identifier as the groupIdentifier argument.

URL Session

By default, a temporary URLSession object with no cache is used for all requests made to the CDS service.

For more control over the networking layer, an optional session parameter is exposed in the initialize() method of the TXNative cache, so developers can offer their own session object if that's desirable (e.g., for more fine-grained cache control, certificate pinning, etc.).

Logging

By default, warning and error messages produced by the SDK are logged in the console using the print() method. Developers can offer a class that conforms to the TXLogger protocol so that they can control the logging mechanism of the SDK or make use of the public TXStandardLogHandler class to control the log level printed to the console.

Limitations

Special cases

Localized strings that are being managed by the OS are not supported by the Transifex SDK:

  • Localized entries found in the Info.plist file (e.g., Bundle Display Name and Usage Description strings) that are included in the InfoPList.strings file.
  • Localized entries found in the Root.plist of the Settings.bundle of an app that exposes its Settings to the iOS Settings app that are included in the Root.strings file.

ICU support

Currently, SDK supports only the platform rendering strategy, so translations will trigger the error policy if the ICU rendering strategy is passed during the initialization.

Internet connectivity

If the device cannot access the Internet when fetchTranslations() method is called the internal logic of the SDK, which doesn't retry or wait for a connection to preserve resources. Developers can detect when internet connectivity is regained to re-call that method.

Sample applications

You can find two sample applications that use the Transifex iOS SDK in Swift and Objective-C.

Video resources

For a quick overview of iOS-supported Native features, check out the Transifex Native Feature Matrix.