Evolution of Localization in Swift: From Strings to String Catalogs

cover
12 Jun 2024

With the world being a global marketplace these days, the capacity of your mobile applications to be in different languages and cultures is not just an option but it is a necessity. Localization enchances the user experience, extends its reach and may have a great impact on the success of software on the international level.

For the iOS developers, Swift offers powerful tools to easily incorporate localization to the applications. This guide explains the transition in localization in Swift, from the early techniques to the latest tool, to help developers fully take advantage of these capabilities.

Early Challenges in Localization

Initially, when Swift was first introduced, it inherited the localization infrastructure from Objective-C, which was built around the Localizable.strings file and the NSLocalizedString function. In this way, developers had to deal with editing the localized strings manually and synchronize them across different language files, a process that is both prone to errors and time-consuming.

The Basics of Localizable.strings

The Localizable file is a simple key-value pair file where the strings are stored in a "key" = "value" format, where "key" is the code identifier and "value" is the localized string that the user will see. This file needs to be duplicated and translated to each supported language and put into the proper corresponding ‘.lproj’ directories, such as ‘en.lproj’ for English language and ‘de.lproj’ for German language.

Example of a Localizable.strings Entry:

"hello_world_key" = "Hello, World!";

In the Swift code, this string could be retrieved as:

let greeting = NSLocalizedString("hello_world_key", comment: "The default greeting")

The NSLocalizedString function makes it easier to access localized content by using keys from Localizable.strings.

This function takes a key, provides a fallback value, and puts a comment for translators. Offering a comment is crucial as it gives translators the subtext to improve the accuracy and suitability of the translations.

Example: Greeting a user based on the time of day:

func greetingBasedOnTime() -> String {

   let hour = Calendar.current.component(.hour, from: Date()) let key = hour < 12 ? "good_morning_key" : "good_evening_key"          return NSLocalizedString(key, comment: "Greeting based on time of day")

}

With corresponding Localizable.strings, entries looking like:

"good_morning_key" = "Good morning!";

"good_evening_key" = "Good evening!";

Here are some challenges with Localizable. strings that you might face:
1) Scalability Issues.

As the number of strings grows, it becomes tough to handle them. Managing multiple .strings files over time becomes cumbersome.

2) Duplication.

Each language required a separate file, leading to potential inconsistencies and duplicated efforts in managing strings. With one language to support, it might not be a problem but when you need to manage multiple languages, it sometimes becomes too troublesome.

3) Lack of Context.

However, the comments could be added in NSLocalizedString – even if they do not always help the translator to understand the context fully, that could result in inaccuracies in translation. Developers are often too lazy to fill them properly.

Transitioning to stringsdict for Pluralization

The introduction of stringsdict files brought a significant improvement for Swift localization, which primarily addressed the problems of pluralization. The main cause is that plurals of languages are formed differently, and what might be effective for English might not be for other languages such as Russian or Arabic.

Before the stringsdict introduction, it was very difficult to manage them. stringsdict enables programmers to determine rules for the categories like “one”, “few”, “many” and “other”, which ensure the same level of correctness in translation across languages.

Understanding stringsdict files

Stringsdictfile is an XML property list, which is used to create strings that are localized depending on numeric values. This functionality is intended to correctly process plural forms because different languages have their own differing rules for processing plural forms.

Structure of a stringsdict file:

A sample of stringsdict file includes entries of all strings that need to be pluralized. Each entry comprises of a key and a dictionary in which the user defines one or more subkeys representing different plural categories.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

    <key>numberOfSongs</key>

    <dict>

       <key>NSStringLocalizedFormatKey</key>

        <string>%#@songs@</string>

       <key>songs</key>

        <dict>

           <key>NSStringFormatSpecTypeKey</key>

           <string>NSStringPluralRuleType</string>

           <key>one</key>

           <string>One song</string>

           <key>other</key>

           <string>%d songs</string>

       </dict>

   </dict>

</dict> 

</plist>

Example of using stringsdict for pluralization

Let’s suppose we have an application that shows the number of articles that have been read by a user. On the screen, it should show a message presenting the correct plural form of the number of articles read.

Localizable.stringsdict for English and Polish:

<!-- English -->

<key>article_count</key>

<dict>

    <key>NSStringLocalizedFormatKey</key>

    <string>%#@articles@</string>

    <key>articles</key>

    <dict>

        <key>NSStringFormatSpecTypeKey</key>

        <string>NSStringPluralRuleType</string>

        <key>one</key>

        <string>One article</string>

        <key>other</key>

        <string>%d articles</string>

     </dict>

</dict>


<!-- Polish -->

<key>article_count</key>

<dict>

    <key>NSStringLocalizedFormatKey</key>

    <string>%#@articles@</string>

    <key>articles</key>

    <dict>

        <key>NSStringFormatSpecTypeKey</key>

        <string>NSStringPluralRuleType</string>

        <key>one</key>

        <string>Jeden artykuł</string>

        <key>few</key>

        <string>%d artykuły</string>

        <key>many</key>

        <string>%d artykułów</string>

        <key>other</key>

        <string>%d artykułu</string>

     </dict>

</dict>

Here, the Polish localization clearly demonstrates the complexity of plural forms in some languages, where multiple plural categories (one, few, many, other) are needed to be used to correctly represent different numeric contexts.

Swift Implementation:

The stringsdict entries can be utilized in the code by simply referring to the keys defined in the file when calling NSLocalizedString.

let count = getArticleCount()
let formatString = NSLocalizedString("article_count", comment: "Count of articles read by a user")
let message = String(format: formatString, count)

This approach allows you to adaptively modify the output string to match the grammatic for the supported languages; all of this only in one line of code.

Benefits of stringsdict

  • Efficiency: This helps to save time and effort which otherwise would have been spent on managing and modifying the localization files.
  • Accuracy: Helps to enhance the accuracy of word translations where more context is given directly within the development environment.
  • Flexibility: Supports real-time and dynamic adjustments to localized content.

Modern Localization Techniques With String Catalogs

One of the new features that were introduced with Xcode 15 is String Catalogs which are supposed to help developers make iOS and macOS applications localize even easier than before. While Localizable.strings and stringsdict have been helpful resources for storing and organizing strings, the String Catalogs are far more cohesive and more UI friendlier to manage localized strings.

These are the main aspects which should be highlighted in the string catalogs:

  • Visual Editor: In contrast with traditional simple plain text files, String Catalogs are edited with the help of a visual editor in Xcode, making it way easier to add, modify, or delete localized strings.

  • In-line Documentation: Developers can directly provide the descriptions and context within the String Catalog, which significantly improves the translation’s clarity and correctness.

  • Dynamic Localization: Strings Catalog supports dynamic localization, so the developers are allowed to change localized strings without exiting the application, which is ideal for testing and iterative development.

  • Advanced Pluralization and Adaptation: It also overcomes some of the shortcomings of stringsdict by offering finer control of plural forms, and text with variations depending on UI elements and device types.

Swift Implementation:

Let’s suppose, that in a messaging application, you have to inform users about the number of messages that are unread by them. The notification has to be dynamic by showing the number of received messages and should adjust accordingly to the content type.

Step-by-Step Setup:

  1. Create a String Catalog: Add a new String Catalog to your project. This will generate a .stringcatalog file.

  2. Add Entries: Use the editor to add entries for each string that needs to be localized. Then specify keys, default values, and any plural rules or adaptive parameters for each entry.

  3. Localize Content: Provide translations for each supported language, specifying different forms for plurals, adaptations for device types, etc.

  4. Implement in Code: Reference these strings using localized identifiers in your Swift code.

<!-- String Catalog Entry -->
<key>unread_messages_count</key>
<dict>
    <key>NSStringLocalizedFormatKey</key>
    <string>%#@messages@</string>
    <key>messages</key>
    <dict>
        <key>NSStringFormatSpecTypeKey</key>
        <string>NSStringPluralRuleType</string>
        <key>one</key>
        <string>You have one unread message.</string>
        <key>other</key>
        <string>You have %d unread messages.</string>
    </dict>
</dict>

You could access this string like so:

let unreadCount = fetchUnreadMessagesCount()
let message = NSLocalizedString("unread_messages_count", value: "You have \(unreadCount) unread messages.", comment: "Notify about unread messages")
print(message)

Benefits of String Catalogs

  • Efficiency: Saves time and effort needed for managing localization files.

  • Accuracy: Improves accuracy by providing more additional direct context right in the development environment.

  • Flexibility: Supports real-time and even dynamic updates to the localized content, which is particularly useful in a rapid development cycle.

Challenges

While String Catalogs offer significant improvements, they also come with challenges. First, it is a learning curve; it takes time to get accustomed to a new system or way of creating something. Another potential challenge that developers could face is linked to the time and adjustments needed to integrate String Catalogs into existing projects where traditional localization files were already created and in use.

Conclusion

String Catalogs in Swift are the most recent further improvement of localization technology providing tools that are not only powerful but also flexible and user-friendly. By adding the technology as a part of the development lifecycle, the teams will have drastically improved efficiency and quality in the localization process; hence, better global products will be created.

I will be constantly updating this article with the latest developments in Swift localization tools. If you feel like I left anything out or you have thoughts about what I said, you can share them in the comments box below. Make sure to follow me for more interesting information and latest news!