Localization of Plural Forms

February 28, 2017

A Quick Recap

In a previous post, Chris explained how to get started with localizing an iOS application. In this post, we’re going to talk about a more advanced localization topic which will help us fix a grammatical error in the previous post’s example. To recap, here’s the basic mechanism by which you can obtain a localized string which contains variable elements defined by your code:

let msg = String.localizedStringWithFormat(
 NSLocalizedString(
 "There are %d oranges in the box",
 comment: "There are {number} oranges in the box"
 ),
 orangeCount
)

This has the obvious shortcoming that if you have 1 orange, the message will be “There are 1 oranges in the box”, which is not grammatically correct (at least in English). In the past, I have been guilty of “fixing” this by using a different format string if the count value is plural or not:

let fmt: String
if orangeCount != 1 {
 fmt = NSLocalizedString(
 "There are %d oranges in the box",
 comment: "There are {number} oranges in the box
 )
} else {
 fmt = NSLocalizedString(
 "There is %d orange in the box",
 comment: "There is 1 orange in the box
 )
}
let msg = String.localizedStringWithFormat(fmt, orangeCount)

This is better, but some other languages have more than two plural categories. Arabic, for example, has six plural categories! The variations between languages would be extremely difficult to manage yourself, so Apple has taken care of this for us with the stringsdict file mechanism.

iOS Pluralization Support

The stringsdict file lets you provide formats for the following plural cases: none, one, two, few, many, and other (English only uses one and other). Add a new file to your project called Localizable.stringsdict and check the language that the file applies to in the Localization section of the File Inspector. It is also required to have a Localizable.strings file present (even if it is empty) so that the Localizable.stringsdict file will be used by the iOS frameworks. Below is what the Localizable.stringsdict file needs to look like to make our box of oranges example work:


<plist version="1.0">
<dict>
 <key>orange_count</key>
 <dict>
 <key>NSStringLocalizedFormatKey</key>
 <string>%1$#@oranges@</string>
 <key>oranges</key>
 <dict>
 <key>NSStringFormatSpecTypeKey</key>
 <string>NSStringPluralRuleType</string>
 <key>NSStringFormatValueTypeKey</key>
 <string>d</string>
 <key>one</key>
 <string>There is one orange in the box</string>
 <key>other</key>
 <string>There are %1$d oranges in the box</string>
 </dict>
 </dict>
</dict>
</plist>

The top-level <key></key> is the key of the localized string passed to NSLocalizedString. I prefer to use a short description of what the string is about for the key instead of the default English text. That way, if the english translation changes, it is not necessary to change the string key in all of the Localizable.strings and Localizable.stringsdict files. In this example, I’ll name the string orange_count. If you use a key that is not the default text, make sure that you also have an entry in your base Localizable.strings file which specifies the default text.

The next step is to name the placeholders so that their plural forms can be specified later. The value %1$#@oranges@ for the NSStringLocalizedFormatKey key specifies that the first placeholder (1$) is called “oranges” (@oranges@). It is very important to use position indexes (like 1$) for strings with multiple placeholders because the format for another language may switch positions of the placeholders. The position indicies specify where the positional arguments to String.locallizedStringWithFormat() should be placed within our localized string. If no position is given, the arguments are placed into the string in the order that they are given, which may be incorrect.

A sub-dictionary for each named placeholder must also be given. This dictionary contains information about the language rule applied to the placeholder and the format type of the placeholder. NSStringFormatSpecTypeKey specifies the language rule applied and the only option is NSStringPluralRuleType. NSStringFormatValueTypeKey specifies the format of the number using a standard string format specifier. For an integer number, we’ll use d (which formats as a signed integer).

The plural forms of the placeholder are then specified with the none, one, two, few, many, and other keys. Though the only required key is other, you may need to specify additional keys depending on the rules of the languages you are targeting. In the example above, I’ve included both other and one for the English example above, because those are the only two forms used in English.

let msg = String.localizedStringWithFormat(
 NSLocalizedString("orange_count", comment: "Count of oranges in the box"),
 orangeCount
)

// Result:
// For 0 oranges: "There are 0 oranges in the box"
// For 1 orange: "There is one orange in the box"
// For 10 oranges: "There are 10 oranges in the box"
<span class="c1">// Result:</span>
<span class="c1">// For 0 oranges: "There are 0 oranges in the box"</span>
<span class="c1">// For 1 orange: "There is one orange in the box"</span>
<span class="c1">// For 10 oranges: "There are 10 oranges in the box"</span>

Android Pluralization Support

Localizing plural forms in Android is even easier than iOS! Android uses plurals resources, which are very similar to strings resources. Plural and string resources both live in your strings.xml file (and its localized variants) and use the same quantity keys as iOS:

<plurals name="orange_count">
 <item quantity="one">There is one orange in the box</item>
 <item quantity="other">There are %1$d oranges in the box</item>
</plurals>

You can then easily generate a formatted result for a given quantity using getQuantityString():

val msg = resources.getQuantityString(
 R.plurals.orange_count,
 orangeCount, // Used to determine which form of the quantity string to use
 orangeCount // The quantity value to be inserted into the format string
)

// Result:
// For 0 oranges: "There are 0 oranges in the box"
// For 1 orange: "There is one orange in the box"
// For 10 oranges: "There are 10 oranges in the box"

Thats it! With just a little bit of extra work, your app can be localized correctly and you can clean up some conditional logic in your source code!

Stay in the loop with our latest content!

Select the topics you’re interested to receive our new relevant content in your inbox. Don’t worry, we won’t spam you.

MichiganLabs to double workforce as part of expansion
Team Press Release

MichiganLabs to double workforce as part of expansion

October 10, 2019

“As our clients continue to grow and evolve, we intend to serve them best by growing along with them,” explains Michigan Software Labs co-founder and managing partner, Mark Johnson. “Organizations are increasingly seeing how technology can serve their customers and grow their businesses. Developing the right custom software gives companies a clear market advantage.”

Read more
MichiganLabs among INC. MAGAZINE’S BEST WORKPLACES 2019
Business Team Press Release

MichiganLabs among INC. MAGAZINE’S BEST WORKPLACES 2019

May 16, 2019

Michigan Software Labs is one of the highest-scoring businesses, with vibrant culture and deep employee engagement

Read more
Are You Solving the Right Problem?
Team

Are You Solving the Right Problem?

February 26, 2020

In Art’s Principles, renowned architect Art Gensler described part of his Design Thinking consultation process as “finding the right problem to solve.” This resonated with me, particularly as I’ve thought about how Michigan Software Labs interacts with clients in the Product Strategy Phase; something I believe sets us apart from other software development companies.

Read more
View more articles