Localization of Plural Forms
February 28, 2017A 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.

Drinks on the Deck 2022
July 19, 2022Join us and ~250 other friends for Drinks on the Deck 2022. Come help us celebrate the new office in Ada. Free drinks & appetizers.
Read more
Michigan Software Labs joins the Inc. 5000 list of fastest growing companies in the U.S.
August 12, 2020Michigan Software Labs has earned its first recognition in Inc. magazine’s influential Inc. 5000 list. The list represents a unique look at the most successful companies within the American economy’s most dynamic segment—its independent small businesses.
Read more
Chicago Roboto 2020: A Virtual Success
January 19, 2021Looking back on last year’s Chicago Roboto, it was a great opportunity to learn while spending time hanging out with teammates. So, when it was announced that this year’s event was going to be virtual, it was hard not to be disappointed. Then again, with so many great speakers lined up, there was still plenty to look forward to.
Read more