Active Filters: Localization

.stringsdict files were added in iOS 7 and OS X 10.9. They allow us to "pluralize" text in our app without a bunch of switch or if statements.

let spaceships = [Spaceship(selected: true)]

let pluralized = String.localizedStringWithFormat(
  NSLocalizedString("%d ships selected",
    comment: "how many ships selected"),
  spaceships.filter { $0.selected }.count
)

The localizedStringWithFormat function is terribly smart.

It will look for a Localizable.stringsdict file before returning and query it for the correct format string to use before returning our final formatted string.

A Localizable.stringsdict is really just a .plist file with a few special strings in it.

We can create a Localizable.stringsdict file either completely by hand in a text editor, or by adding a new Property List to our project and naming it Localizable.stringsdict.

Now that we have a .stringsdict file, let's use it.

The root key is the same one we passed into NSLocalizedString.

Then, we define a sort of string-replacement-variable using some special characters: %#@VARIABLE_NAME_HERE@. The system will then find the sub-dictionary that shares a name with this variable, and use it to pluralize.

We can provide cases for zero, one, two, few, many, and other. Each potential plural case is optional for each language, except other.

Download an example project showing off this functionality here.

Hat tip to Matthew Bischoff for the idea for today's Bite, which actually inspired a whole series of localization Bites with more still to come!

Today we'll continue our localization series with a lesser-known Xcode feature that makes the process of translating our apps easier. Usually we won't be translating our apps ourselves, at least not into all the languages we want to support.

It's common to send our strings off to a third-party service, who will translate them and return them to us. Let's get started.

In Bite #181, we enabled the basic localization features of Xcode. As we work on our app, we'll add new strings:

button.setTitle(
  NSLocalizedString("fly-here", comment: "Fly Here"),
  forState: .Normal)

When we do, we could manually add the new key to all of the different language versions of our Localizable.strings file.

We could also generate a brand new Localizable.strings file using Apple's genstrings command-line tool. Running gentrings *swift in our project's root directory will scan our code and generate a new Localizable.strings file with all the NSLocalizedString keys we've used.

That sounds like it could lead to lots of manual diff'ing.

It turns out that Xcode itself actually has a more powerful feature that can export our strings for translation with almost no manual labor. It pulls from all our Storyboards, .xibs, and scans our code for NSLocalizedStrings.

To use it, we'll select our project at the top of the Project Navigator, and then select Editor > Export for Localization... This will export a folder full of .xliff files for all the languages we've added to our project in Xcode.

This format is standard for most translation services. We'll find a service, then upload these files to them. They'll return us a similar set of files, filled with our translated strings. When they do we'll select Editor > Import Localizations... option then import them back into Xcode.

Popular translation services like Babble-on and Applingua specialize in mobile apps and support .xliff files. We could also edit the files ourselves using a tool like iXLIFF.

Today we'll continue our series on localizing our apps by looking at NSNumberFormatter.

It can help us make sure we're displaying numbers using the format a user expects in their culture or language, as well as display numbers in some fun ways. Let's get started.

First, let's see how we can take advantage of NSNumberFormatter when localizing our app.

We'll create a new formatter, and tell it to use the user's current locale. (An NSLocale is a class that encapsulates information about linguistic, cultural, and technological conventions and standards).

let formatter = NSNumberFormatter()
formatter.locale = NSLocale.currentLocale()

Now, we can set what kind of format we'd like to perform. NSNumberFormatter has a ton of great built-in styles. Let's try the Currency style:

formatter.numberStyle = .CurrencyStyle
formatter.stringFromNumber(2187.31) // "$2,187.31"

Then, we ask the formatter for a string from a number.

Apple has done all the hard work of figuring out how numbers should change when displayed in different locales. The example before showed the output from a device in the US, what about one from Germany? Let's try it:

formatter.locale = NSLocale(localeIdentifier: "de_DE")
formatter.numberStyle = .CurrencyStyle
formatter.stringFromNumber(2187.31) // "2.187,31 €"

Neat! What else can NSNumberFormatter do? Here's some fun examples:

formatter.numberStyle = .SpellOutStyle
formatter.stringFromNumber(21) // "twenty-one”
formatter.numberStyle = .OrdinalStyle
formatter.stringFromNumber(21) // "21st"

In addition to all of that, NSNumberFormatter also has an insanely long list of customizable properties allowing us to create completely custom formatters (best done via subclassing).

When browsing the App Store, users are likely to scroll right past apps that don't support the language they speak. Like any business, one way we can improve the sales of our apps is to make them useful to new, large groups of people.

Today we'll begin learning about localizing our apps. We'll start by setting up our Xcode project to be localized, then try a simple example using NSLocalizedString.

Let's dive in:

First, we'll head over to our Project's settings **and add a new **Localization language. We'll choose French, then Finish.
This will modify our project (and a few files) to make it localizable.

Next, we'll go to File > New > File… and add a new Strings file called Localizable to help us localize strings in our code.

Yep, it has to be named exactly like that. πŸ˜’

We'll select this new file and head over to the File Inspector panel. We'll click the Localize… button, then the French checkbox.

Now we can edit both versions of our .strings file, adding translate-able strings using a special key/value syntax:

// Localizable.strings (Base)
search-spaceships = "Search spaceships...";
globe = "🌎";
// Localizable.strings (French)
search-spaceships = "Recherche vaisseaux spatiaux...";
globe = "🌍";

Then, we can reference those keys in our code using NSLocalizedString:

searchBar.placeholder = NSLocalizedString("search-spaceships", comment: "Search Placeholder")

We can specify a language to test by editing the Application Language setting of our Scheme's Options.

Success!

Download the example project from this bite right here.

Localization can go a long way towards increasing the sales of our apps. Users are browsing the App Store all over the world, and when they come across a neat looking app that's not translated into their language, they're very likely to keep on scrolling. Today we’ll check out a library called Localize-Swift from Roy Marmelstein that makes this process much nicer.

The first helpful addition Localize-Swift brings us is a better way to localize our strings:

searchBar.placeholder = "Hello World".localized()

Then we can easily test our translations at runtime using:

Localize.availableLanguages()
Localize.setCurrentLanguage("fr")

We can respond to language changes using an NSNotification:

NSNotificationCenter.defaultCenter().addObserver(
  self, 
  selector: "setText",
  name: LCLLanguageChangeNotification, 
  object: nil
)

Localize-Swift also provides a custom genstrings.py script to easily fill our .strings files even though we're not using NSLocalizedString directly, neat!

More info about Localize-Swift can be found at git.io/localizeswift