Internationalization is like flossing: everyone knows they should do it, but probably don't.
And like any habit, it becomes second-nature with practice, to the point that you couldn't imagine not doing it. All it takes is for someone to show you the way.
Let NSHipster be your dental hygienist Virgil through these foreign lands.. without all of the lecturing about tooth decay (promsies!)
i18n versus l10n
As is necessary in any discussion about Internationalization (i18n) or Localization (l10n), we must take some time to differentiate the two:
- Localization is the process of adapting your application for a specific market, or locale.
- Internationalization is the process of preparing your app to be localized.
Internationalization is a necessary, but not sufficient condition for localization, and will be the focus of this article. Localization, which involves the translation of text and assets into a particular language, will be covered in a future edition of NSHipster.
What makes internationalization difficult is having to think outside of your cultural context. All of the assumptions you have about the way things are supposed to work must be acknowledged and reconsidered. You have to fight the urge to write off things that may seem trivial, like sorting and collation, and empathize with the pain and confusion even minor differences may cause.
Fortunately for us, we don't have to do this alone.
NSLocale is a Foundation class that
encapsulates all of the conventions about language
and culture for a particular locale. A locale
encompasses all of the linguistic and cultural norms
of a particular group of people, including:
- Number, Date, and Time Formats
- Collation and Sorting
- Use of Symbols, Colors, and Iconography
Each locale corresponds to a locale
identifier, such as
en_GB, which include a language code
en for English) and a region code
US for United States).
Locale identifiers can encode more explicit
preferences about currency, calendar system, or
number formats, such as in the case of
which specifies German spoken in Germany, using
phonebook collation, and using the pre-Euro
Users can change their locale settings in the "Language & Text" (or "International" on older versions of OS X) System Preferences on the Mac, or "General > International" in iOS Settings.
Formatting Dates & Numbers
NSLocale encapsulates a rich
set of domain-specific information, its typical usage
is rather understated.
If there's just one thing you should learn about
NSLocale, it's that you should always
[NSLocale currentLocale] into your
NSNumberFormatter instances. Doing this
will ensure that dates, numbers, and currencies will
be formatted according to the localization
preferences of the user.
Actually, make that a meta lesson about locales:
NSNumberFormatter when displaying
anything to do with dates or numbers, respectively.
But let's get back to some of the cool features of
NSLocale itself, shall we?
NSLocale typifies Foundation's obsession
with domain-specific pedantry, and nowhere is this
more visible than in
the list of available constants:
While this all may seem like fairly esoteric stuff, you may be surprised by the number of opportunities your application has to use this information to make for a better user experience.
It's the small things, like knowing that quotation marks vary between locales:
English: “I can eat glass, it doesn't harm me.” German: „Ich kann Glas essen, das tut mir nicht weh.“ Japanese:「私はガラスを食べられます。それは私を傷つけません。」
So if you were building a component that added
quotations around arbitrary text, you should use
rather than assuming
@"\"" for English
Another impressive, albeit mostly-useless method is
-displayNameForKey:value:, which can
return the display name of a locale identifier
NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"]; NSLog(@"fr_FR: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"fr_FR"]); NSLog(@"en_US: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"en_US"]);
frFR: français (France) enUS: anglais (États-Unis)
You should use this method any time you need to display information about the user's current locale, or any alternative locales available to them, like in this screen from the Settings app:
One final method worth mentioning is
+preferredLanguages, which returns an array of
47 language identifier strings, in order of user
An app that communicates with a web server can use
these values to define the
Accept-Language HTTP header, such that
the server has the option to return localized
NSMutableURLRequest *request = ...; [request setValue:[NSString stringWithFormat:@"%@", [[NSLocale preferredLanguages] componentsJoinedByString:@", "]], forHTTPHeaderField:@"Accept-Language"];
Even if your server doesn't yet localize its resources, putting this in place now will allow you to flip the switch when the time comes, without having to push an update to the client. Neat!
Internationalization is often considered to be an un-sexy topic in programming--just another chore that most projects don't have to worry about. In actuality, designing software for other locales is a valuable exercise (and not just for the economic benefits of expanding your software into other markets).
One of the greatest joys and challenges in programming is in designing systems that can withstand change. The only way designs can survive this level of change is to identify and refactor assumptions about the system that may not always hold. In this way, internationalization represents the greatest challenge, making us question everything about our cultural identity. And in doing so, we become not just better programmers, but better people, too.
So go and be a better person: make
NSLocale part of your daily ritual.