Localization: Change Languages in Our iOS PDF Viewer

PSPDFKit for iOS comes with many built-in languages:

  • Arabic (ar) (including R2L interface support)

  • Chinese Simplified / Chinese Traditional (zh-Hans/zh-Hant)

  • Croatian (hr)

  • Czech (cs)

  • Danish (da)

  • Dutch (nl)

  • English (en)

  • English United Kingdom (en-GB)

  • Finnish (fi)

  • French (fr)

  • French Canada (fr-CA)

  • German (de)

  • Greek (el)

  • Hebrew (he)

  • Indonesian (id)

  • Italian (it)

  • Japanese (ja)

  • Korean (ko)

  • Malay (ms)

  • Norwegian Bokmål (nb-NO)

  • Polish (pl)

  • Portuguese Brazil / Portugal (pt-BR/pt-PT)

  • Russian (ru)

  • Slovak (sk)

  • Slovenian (sl)

  • Spanish (es)

  • Swedish (sv)

  • Thai (th)

  • Turkish (tr)

  • Ukrainian (uk)

  • Welsh (cy)

These languages are used automatically depending on the preferred language. To enable a language, your application must also be localized in that language — otherwise, PSPDFKit will fall back to English (the reason being that you would otherwise end up with a partially translated app, which isn’t a good user experience).

To translate your app, follow Apple’s localization guidelines and provide your .strings file in all the languages you want to support.

You can also use PSPDFKit’s setLocalizationDictionary(_:) or setLocalizationClosure(_:) to customize the localization.

ℹ️ Note: iTunes Connect doesn’t support all of these languages. However, iOS devices can still be set to these languages. Just be aware that there’s no localized storefront. See here for a detailed list of Xcode language codes.

Adding Additional Localization to PSPDFKit

You can add additional localization by adding the corresponding folders or setting a new localization programmatically.

To see all strings that should be localized, look inside PSPDFKit/PSPDFKit.bundle/en.lproj/PSPDFKit.strings. PSPDFKit uses NSLocale.preferredLanguages to determine a language and falls back to English if this method doesn’t return anything or the language isn’t available either in the PSPDFKit.bundle or the language dictionary. PSPDFKit won’t look into your default project strings file. To get all strings translated (including OS-level default buttons like Edit or Done), you need to add a Localizable.strings file of your target language to your project:

setLocalizationDictionary([
    "en": [
        "Go to %@": "Browse %@",
        "%d of %d": "Page %d of %d"
    ]
])
PSPDFSetLocalizationDictionary(@{
    @"en": @{
        @"Go to %@": @"Browse %@",
        @"%d of %d": @"Page %d of %d"
    }
});

You can also use setLocalizationClosure(_:) to register a block that’s called every time a string is translated. In this way, you can easily use your default bundle or a custom bundle with NSLocalizedStringFromTableInBundle:

setLocalizationClosure { stringToLocalize in
    guard let stringToLocalize = stringToLocalize else { return nil }

    // This will look up strings in `language/PSPDFKit.strings` inside your resource folder.
    return NSLocalizedString(stringToLocalize, tableName: "PSPDFKit", comment: "")

    // You can also route translation through your default localization:
    return NSLocalizedString(stringToLocalize, comment: "")

    // Or use a custom variant, for example, to test if everything is localized.
    // Please keep in mind that several strings, like Edit, are provided by the system and are automatically translated, so long as you have the correct localization resources set.
    return String(format: "_____%@_____", arguments: [stringToLocalize])
}
PSPDFSetLocalizationBlock(^NSString *(NSString *stringToLocalize) {
    // This will look up strings in `language/PSPDFKit.strings` inside your resource folder.
    return NSLocalizedStringFromTable(stringToLocalize, @"PSPDFKit", nil);

    // You can also route translation through your default localization:
    return NSLocalizedString(stringToLocalize, nil);

    // Or use a custom variant, for example, to test if everything is localized.
    // Please keep in mind that several strings, like Edit, are provided by the system and are automatically translated, so long as you have the correct localization resources set.
    return [NSString stringWithFormat:@"_____%@_____", stringToLocalize];
});

Pluralization Considerations

PSPDFKit uses both the PSPDFKit.strings file and the PSPDFKit.stringsdict file. A .stringsdict file is used to define language pluralization rules, as some languages have more pluralization variants besides one and many; for example, see the pluralization rule for Croatian:

<key>%tu match(es) found</key>
<dict>
    <key>NSStringLocalizedFormatKey</key>
    <string>%#@tu_matches_found@</string>
    <key>tu_matches_found</key>
    <dict>
        <key>NSStringFormatSpecTypeKey</key>
        <string>NSStringPluralRuleType</string>
        <key>NSStringFormatValueTypeKey</key>
        <string>tu</string>
        <key>zero</key>
        <string>Nema pronađenih rezultata</string>
        <key>one</key>
        <string>%tu pronađen rezultat</string>
        <key>two</key>
        <string>%tu pronađena rezultata</string>
        <key>other</key>
        <string>%tu pronađenih rezultata</string>
    </dict>
</dict>

PSPDFLocalize() automatically looks in both the stringsdict and the classical strings files to look up localized strings.

Forcing a Specific Language

You can manually override system defaults and force a specific language. We generally don’t recommend this, as it’s against Apple HIG. If you have a use case that requires this, recreate all UI elements manually after updating the setting or — ideally — simply restart the application to update the localization. You can achieve this by either setting setLocalizationClosure(_:) and returning a custom language, or (better) changing this systemwide via setting the AppleLanguages in the user defaults. This needs to happen early, before your app initializes. You can place this in the main.m/Main.swift file before UIApplicationMain or simply use a function with a constructor attribute in an Objective-C file:

__attribute__((constructor)) static void PSCForceCustomLanguage(void) {
    [NSUserDefaults.standardUserDefaults setObject:@[@"de"] forKey:@"AppleLanguages"];
}

See how this can be done in Swift here.

You can learn more about AppleLanguages in Apple’s QA1391 article. If your app requires updating localization at runtime without restarting the app, you can do more advanced tricks like swizzling NSBundle’s localizedString(forKey:value:table:).