App Localization Tips With Swift
Mobile localization is a very important topic. It helps us to reach as many users as possible providing our Apps with different languages. Let’s find out some tips to improve our localization handling.
Introduction
If we want to reach as many users as possible in the AppStore, we should localize our Apps with different languages. It sometimes might increase the complexity of our code. For this reason, I want to share some tips to improve our localization handling.
Happy Reading!
Getting Started
Before looking at the tips, we need a project with the localization enabled.
The first step is adding a new file called Localizable.strings
in our project. If you don’t know how to do it, watch the following video:
The syntax of this file is:
|
|
An example of localized strings may be something like this:
|
|
With the localization enabled, we are ready to see some tips to improve our localization handling.
String Extension
If we want to use a localized string programmatically, we should use the function NSLocalizedString
:
|
|
key
: The key of the localized string—likedata_loaded
in the example ofGetting Started
.tableName
: The name of the file where to search the string. The table name is the name of the.strings
file where to search the string. If we omit it, the default value isLocalizable
. InGetting Started
, we saw how to addLocalizable.string
just for the sake of explanation. We may add several.strings
files to organize in a clean way our strings. For example, if we create a fileLogin.strings
—which would contain the localized strings for our login page—we can pass totableName
the valueLogin
to use the strings insideLogin.strings
.bundle
: The bundle which contains thestrings
file. By default, it’s the main one.value
: Default string to show if the function doesn’t find the string associated tokey
.comment
: String used bygenstrings
to generate the.strings
files—you can find more details about the toolgenstrings
here.
An example of NSLocalizedString
usage may be something like this:
|
|
There is nothing wrong with the example above, it’s quite clean. But, I think we can improve this approach. We can refactor it with a String
extension:
|
|
If the string is not found, we show **<key>**
for debugging.
We can use the new extension like this:
|
|
Table Names Handling
As we saw in String Extension
, we can have several table names to organize our strings.
If you use just a Localizable.strings
file for the whole project, I would suggest you to split in subfiles to clean your strings.
Let’s consider that we have a file DataLoader.strings
which contains:
|
|
With this example, we would have a new table name DataLoader
. Then, if we want to use these strings, we should inject every time the table name in the method localized
of String extension
:
|
|
This may not be a clean approach, since we should avoid injecting the table name every time. We can clean this approach using an enum per table name to store our strings. In this way, we would have a type-safe approach to manage our localized strings without messing our codebase with hardcoded strings.
We can start creating a new enum DataLoaderStrings
which will contain all the strings of DataLoader.strings
:
|
|
Each case
is associated to a specific key of our strings
file.
Then, we can add a method to localize these strings using a specific table name:
|
|
With this approach, we can use the localized strings like this:
|
|
We may improve this approach using a protocol extension:
|
|
- Extend the extension only if
self
is a string and conforms toRawRepresentable
—enums conform to this protocol by default.
With this approach, we must specify just the table name in the enum. The computation will be done in the protocol extension.
UIKit Components
We sometimes add the strings in UIKit
components—like UILabel
, UIButton
and so on—via Interface Builder. By default, Interface Builder doesn’t allow us to localize the strings in the graphic interface.
For example, if we have an UILabel
with a text set in the Interface Builder like this:
We wouldn’t be able to localize the text “Data Loaded!”.
We can solve this problem subclassing UILabel
with UILocalizedLabel
. Then, we can localize the text programmatically in the method awakeFromNib
:
|
|
The method localized
comes from the String
extension explained previously.
With this approach, in the Interface Builder we must update the UILabel
object class to UILocalizedLabel
:
And, finally, we must replace the label text with the key of the string to localize:
We used the UILabel
for the sake of explanation. We can use this approach for any UIKit
components like UIButton
:
|
|
UITextField
:
|
|
And so on…
Note
As we saw in Table Names Handling
, we should use table names as much as possible to clean our .strings
files. Unfortunately, in the examples above, we have no ways to set the table names in these custom UIKit
components. We can solve it easily.
First of all, we must add the annotation IBDesignable
to our class:
|
|
Then, we must add a new property in our custom class with the annotation IBInspectable
:
|
|
Finally, we can add a didSet
observer at this property to use the new table name value:
|
|
We used an optional binding inside the guard
since tableName
is an optional String
.
At the end, our UILocalizedLabel
should be like this:
|
|
With this approach, we added a new field for our custom UILabel
component in the Interface Builder, where we can set the value of tableName
:
Pluralization
We sometimes need to handle a string with both singular and plural variants depending on an input value. For example, we may have There is one file
and There are 2 files
.
We can handle these variants with two separate strings and use them inside an if/else statement:
|
|
This approach is not very good since it adds complexity to our codebase to manage the string variants. Fortunately, Xcode provides a better solution to handle pluralization.
First of all, we must add a new file Localizable.stringsdict
in our project. We can add it following the same steps of Localizable.strings
with the only difference that we must add a new Property List
file instead of a Strings File
.
The default extension of a Property List
file is .plist
. We must rename it to stringsdict
.
For the sake of explanation, we used the table name Localizable
. We can add pluralization for any table names—like DataLoader.stringsdict
.
If we open the new file, we should have something like this:
Now, we can add all the sentences for our pluralization. This file has a specific syntax and the result will be like this:
- The key of the string to localize—the two
%d
are the placeholders for the values which we are going to set later. - The format of the localized string.
%#@<key>@
is the syntax of a placeholder key. - Placeholder key. We must repeat the steps 3-4-5-6 for each placeholder key of the point 2. In this example, we have two placeholder keys:
to_be
andnum_files
. - The type of the key which is always
NSStringPluralRuleType
. - The type of value received in input. In this case, we have an integer (%d). You can find the list of formats <a href=“https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFStrings/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265"**** target="_blank”>here.
- The variants of the string. We can use:
zero
,one
,two
,few
,many
andother
. Each language may have different pluralization rules which use these keys. You can read more details about language plural rules here.
Then, we can use this pluralized string like this:
|
|
The method localized
comes from the String
extension explained previously.
For the sake of explanation, we used this string with two placeholders. There are no limits for the placeholders to use.
Conclusion
These are the approaches which I use more often to organize the localization in my side projects. Feel free to add a comment with your tips. Thank you!