.xcstrings in Xcode 15

My name is Daniil Khrapovitsky, I’m an iOS developer at CleverPumpkin studio, and today we’ll talk about xcstrings in xcode 15.

One of the most frustrating aspects of iOS development is string localization and pluralization. Not only are they split into different files: strings And stringsdict, so also working with these files for a novice developer may not be very obvious. “What’s happened %#@⁠VARIABLE@?”, “How to add multiple plurals in one string?”, “How to use plurals in localized strings?”, “How to add different translations for different devices?” – All these questions sooner or later arise from the developer. After receiving answers to them, everyone asks the question: “Why is everything so bad?”

Things are better with our green brothers – they have one file for localization. With us, everything is so difficult, because we inherited the data format from Objective‑C, where some kind of rigor and protection from fools were not entirely necessary (have you ever seen Objective‑C?). Therefore, earlier in the localization files it was possible to:

– calmly skip the semicolon, the place in the code of which Xcode will bashfully keep silent;

– miss the point that the name NSStringLocalizedFormatKey must match the key of the line for which we provide pluralization (when you know about it, it’s hard to forget, but at first everyone made such mistakes);

– generally forget to add a translation for one of the keys, after which the application will display the key instead of the normal string.

However, everything changed with the advent of Xcode 15, where the localization and pluralization of strings were significantly improved. Now there is one file xcstrings. This is a directory that stores all the keys and strings for translations into other languages, as well as translations for the plural. It is important to note that all this can be backported to older versions of iOS by splitting xcstrings on .strings and .stringsdict. That is, the old format is still used under the hood, but we, as developers, are already working with an interface that is convenient for us.

When creating a new xcstrings-catalog interface will look like this:

If we select a language, then we will see that the directory is empty:

Let’s try adding a line and see what happens. A string has been created for which we can set a key, a localized string and a comment. The status will automatically change to display information about the status of the translation and the relevance of this entry:

Let’s change the line:

Now let’s add a translation into Russian:

It will show that there is one newline:

Let’s translate it. After adding the translation, the status will change to OK.

Let’s add the ability to pass numbers there. This is done as follows:

However, now we need to add pluralization to the string to make it look natural. This is done as follows:

Status changed to Needs review. This indicates that the line needs our attention.

After localizing the two variables, the following is obtained:

Returning to the Russian language, we will see that the lines need to be updated.

By updating, we get the following:

This is how localization and pluralization happen. In code, this can be called either through the good old NSLocalizedString:

String(format: NSLocalizedString("File instead of Files", comment: ""), 1, 2)

Or through a newfangled structure LocalizedStringResourcebut for this you need to change the key a little – add argument format specifiers to it (%d, %lld, %@ and so on) that are used in the string.

However, if we call the string in this way, then things will not work as we expected:

String(localized: "\(2) File instead of \(1) Files")

// 2 File instead of 1 Files

And all because %d in a localized string is Int16. And no matter how good Swift is at literal initialization, it cannot know that by specifying 2 or 1We mean Int16but not Int. Therefore, it cannot match the passed string with the key.

After that, another nuance of working with the string catalog comes into play. By default, for each row that has not been mapped to an existing key, Xcode will create a new key. It works like new String(localized:)and for already seen life NSLocalizedString. This can be handy if you don’t want to work directly with xcstrings file, creating new keys and writing arguments to them (although you still have to write string localization):

However, in the case of %d but if we explicitly specify the type, then everything will work as expected:

String(localized: "\(Int16(2)) File instead of \(Int16(1)) Files")

// 2 Files instead of 1 File

To avoid this, you need to specify the type %lldwhich will parse normally into Int.

In addition, it is possible to create translation strings for a specific type of device. For example, you can set different translations for iPhone and other devices:

Also of interest, it is worth noting that now files instead of the usual xml have json format, which is very unexpected for Apple. And since there is only one file now, services like POEditor will be able to generate strings for iOS projects normally.

All this beauty is supported on all versions of iOS, but, of course, compiled using Xcode 15. The magic is that when compiling, everything is parsed into the already familiar strings And stringsdict files, and they will be stored in the bundle.

In conclusion, I would like to say that Apple is taking the right steps in terms of localization. Starting with Xcode 15, translating strings has become much more convenient, simpler and clearer without duplication across multiple files.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *