Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2009-106
$eventId
ID of event: wwdc2009
$eventContentId
ID of session without event part: 106
$eventShortId
Shortened ID of event: wwdc09
$year
Year of session: 2009
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2009] [Session 106] Building Lo...

WWDC09 • Session 106

Building Localized Mac and iPhone Applications

iPhone • 1:05:37

Users from across the globe are downloading more Mac and iPhone applications than ever. Internationalization is a critical part of the application design process that lets you easily expand your audience to new languages and countries. Learn the practical techniques behind efficient Mac and iPhone app localization and see how Xcode and Interface Builder help you build a multilingual application.

Speakers: Lee Collins, J.J. Enser

Unlisted on Apple Developer site

Downloads from Apple

SD Video (128.5 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Hello everybody. Lee Collins. I'm here to talk to you today about Building Localized Applications for the Mac and iPhone. It's really great to see such a large turns out early on Friday morning after the big beer bash last night.

[ Pause ]

So, the first question I suppose everybody is wondering is, why do you care about building localized applications? Well the answer for me and I assume for most of you is more customers. Apple certainly takes this very seriously. We continue to expand our global reach with the iPhone shipping now in more than 80 countries. Our App Stores available in more than 77 countries.

And the iPhone itself is localized in 30 languages and the Mac also in 18. And our platforms also support a lot more content in terms of being able to create documents, browse websites et cetera on other languages. So here's what it looks like for the Mac in terms of our global coverage. So we cover Spanish, South America, Spain, Chinese, Russian, English, French, a number of languages of to 18. And for the iPhone, it's even more. And will continue to grow over the years.

And in addition to the localized languages, we support a lot more text content browsing, searching, et cetera. For example on the iPhone you can also do a lot in Icelandic et cetera. On the Mac even more, languages like Armenian, Cherokee, Native American language, Persian, Tamil, Tibet and et cetera.

So I'd also like to impart today our design philosophy which is basically write your application once and make sure that it runs everywhere. This is the basis for the iPhone and Mac design. This is the philosophy that we follow. You should be able to write a single binary that supports many localizations and any language content, and also it just run right out of the box.

Of course I'm speaking metaphorically because now very few applications are going to be shipped in boxes. Most of your applications will probably be shipped from the App Store, purely software, but you get the idea. So let me talk about internalization and localization which are two aspects of the process of getting your application ready for international markets.

We begin with internalization which is basically getting your code ready to be localized. And then you continue with localization which is the data you need. Translations of user visible strings, et cetera, that you show at runtime for local users. You need to do both. They're separate processes. But if you could only do one, start with internationalization; get a firm foundation for localizing your application.

Localization itself once you're set up with a good internalization design, you're using all of our-- the APIs we provide to help you do localization, it could be easily outsourced to experts who know how to localize for different languages. So today I'll be talking about the interna-- internalization aspect, and my fellow employee J. J.

Enser will continue talking about the nuts and bolts of localization. So some key ideas you should be able to take away from today's talk. Internationalization can be difficult, sure. It's a very complex thing. It's you're doing many more languages than English. Language itself is very complex. We make it a lot easier using the API that we've worked so many years to refine and make it easy for you to adopt. Also localization and internationalization should just be considered part of good software engineering design. It's not-- not anything special. It's just obstructing like you do with other things.

You obstruct the language from your code, so you can adapt to different language environments. So one thing you should remember is there's a lot of code you shouldn't have to write. For example, don't write code and let the user select a language. We already provide that for you.

We provide a nice UI for that on the iPhone and the Mac. Don't write mechanisms-- mechanisms to get localized strings. That's already built in. Don't do code for writing date-time, number formats, that's part both the Mac OS and the iPhone. And finally, don't do separate binaries for each localization. You should be able to follow in the design principles I talked about today. We should be able to write a single binary that runs for each language.

So let's take a look at what you see when a user wants to actually select a language or a region format. This is the way it looks on the iPhone. Notice that you can select the language, in this case English, independently from the region format, in this case Egyptian Arabic. And you could also see the kinds of things that the user will actually get when they select a region format. For example, different day-time formats, on the iPhone, a telephone number format. This is the way the same functionality looks on the Mac OS.

Notice that on both the iPhone and the Mac OS, when you select the language, you select-- actually select a list of ordered languages. Now this is important because maybe we-- you don't provide all of the localizations that match the users' preferences, in that case, we'll fall back. So if English is your first-- first preference, you know, but you don't have English, what would be a very unusual situation will fall back, so in this case Arabic.

I'll talk more about that later. So there are three steps to good internationalization. You want to start out using Unicode. Use the bundle mechanism that we built-in to package your application. And then at least be aware of locales and how they work. So let me talk first about Unicode. I'll be surprise if no one in this room has ever heard of Unicode. It's now basically the standard for representing international text on all major platforms. Apple has been using it for many years and we use it for several good reasons.

First of all is coverage. Unicode covers virtually every language that is now currently spoken on the planet. Also because if its widespread use and its growing use. Unicode is now the standard for representing text on the worldwide web. Google stands some great numbers showing that. So we think it's a good idea to use Unicode.

And in fact, we don't really give you any other choice 'cause Unicode is the native encoding for Mac OS and the iPhone. So Unicode sets out to solve a very complex problem much more complex than the kind of problems that were solved by older encodings such as ASCII or JIS. ASCII I mean for US English is pretty, pretty simple encoding.

You can make a lot of assumptions that you cannot make about Unicode text. Unicode comes in lot of interesting features that you have to deal with. For example, there are 3 encoding forms, UTF-8, UTF-16, and UTF-32. These all have different purposes, but the upshot is they have-- the resulting text has very different byte patterns.

You have to be able to deal with that. The byte-order mark, the bomb. This is a Unicode character that appears at the beginning of a plain text file to identify the endianness of that file. Normalization forms. Normalization includes things like whether Unicode characters are represented as separate base letters with combining diacritics or a single character.

The Mac OS file system prefers the fully decomposed form of this. And there are times when if you're going to get file system text, you have to convert it to a different form. Grapheme clusters. These are several Unicode characters that represent a single semantic unit and many more. These are all that-- and that some details of Unicode that you'd have to deal with. Our advice basically is don't deal with them on your own but use the preexisting API that we provide.

Another thing you have to keep aware of especially now as we have added Arabic and Hebrew to the iPhone, text direction can be-- can vary. It can be English order, left-to-right, Arabic order right-to-left or combination Arabic and Hebrew both contain text that has to be run both ways. And unless you really want to get into the details of the Unicode by the algorithm, I suggest that you just use our text routines for this and don't assume that your text is going to flow in any one of these directions but all of them.

Another thing to think about is the characters in your backing store are different from the actual glyphs that a user will see on the screen. For example, you may have multiple characters that form a single glyph which you call a ligature. Similarly, I mentioned the graphemes, several Unicode characters that compose a single logical element. You don't want to, when you're dealing with editing operations on those elements.

You don't want do things like breaking them apart. You want to treat them as a single logical character even though there are three Unicode characters. And then surrogates. Surrogates are mechanism that Unicode has to extend the 16-bit range of the original Unicode UTF-16 into a much larger space.

Surrogates represent a single character but they come as two Unicode UniChars. So the upshot is when you're dealing with Unicode, don't assume that you can do it simply. For example you cannot break between arbitrary UniChars. And you cannot assume that just by adding up the single Unicode character widths, you're going to get the actual width of the character that the user sees on the screen.

So okay, Unicode is-- Unicode is hard, we don't deny that, but we provided way to isolate you from the details of Unicode. And that is the string class we provide, we provide NSString. This is the class that most of our APIs or castings, it was very easy to use once you get into Cocoa on either the iPhone or Mac OS.

Now it's toll-free bridge with the CFString, so you can use it if you're using-- if your code is based on-- in a Core Foundation rule-- world. But basically you want to use it because it will isolate you from all of the complexities of dealing with Unicode text. Now you might say well, I have all this old data that's not in Unicode or I have arrays of UniChars, how will I deal with those? Well, NSString supports conversion to in and from virtually any encoding you can imagine.

So it's very easy to generate an NSString from legacy encodings such as JIS, GB2312, ISOlat1, whatever you have. So in working with the NSStrings, there are a number of subclasses you can also work with. NSMutableStrings for changing strings in place, NSAttributedString which is a rich text form of NSString, and finally, NSMutableAttributedString which handles both editing and rich text.

So there are a number of ways to create NSStrings. You can create NSString constants. Now, don't do this for localized strings because you can't localize them, but they're very appropriate to do for identifiers. For example the keys that you use to get to localized strings can be created this way as a string constant. If you have an array of UniChars, just create an NSString calling stringWithCharacters, provide the length and the buffer, that's all you need to do.

If you have legacy characters, a cString and another encoding, call stringWithCString. And finally, there are many ways to get NSStrings from other NSStrings for example getting a range. It's very easy to work with, although the names are somewhat long. Now, a very common operation that you do for localization when you want to present much longer or complex strings to the user is formatting strings.

NSString has a method called stringWithFormat. Now this is, basically it works the same way as sprintf using that same kind of syntax. We've added one refinement which is the &@ syntax and this lets you pass in any Objective-C object to be formatted in with your string. You can also index the parameters in your string format. This is very useful for the localizers, so they just have to change the order when they're localizing. J. J.

will talk a little bit more about that later. So I said don't store localized strings as hard-coded strings in your Objective-C code or C code or whatever. And where do you store those? Well, they're stored in separate strings files called "Localizable.strings" files. Now these are UTF-8-- UTF-16 encoded files. Each line contains a key value pair separated by an equal sign. So the key is how you access the string and the value is the actual localizable content that will change for localization.

You can pass these files out to localizers and they will localize the value part of the key value pair. At runtime the way you access your localized strings is through NSLocalizedString. And that takes a key, just going to match the key in the localizable.strings file and a hint, the localization hint.

And these are very important actually because the localizer will not see what's happening inside of your code, but they will see the hint if you add the hint in your call to NSLocalizedString and hint will tell them exactly how this string is meant to be used at runtime. And that's to give them a better idea of what-- what they should be translating to.

It's not always easy to translate from one language to another if you don't know the context in which that translation is taking place. So we encourage you to use the localizer, localization hint. And as I said these strings will come from the localizable.strings file that will be packaged as part of your application.

I'll get to that later. Now when formatting strings, of course there's a wrong way and a right way. Let's look at the wrong way. So this code tries to take several localized objects which are indeed obtained in a correctly localizable way into a single string. Now the problem with this, so you've got a date and you've got a number representing the height of the mountain, you've got the name of the mountain et cetera, and then you've got some text that tries to format around that to put it together into a coherent human readable string. Now what's the problem with this the way this is done? Well if you look at the format itself, the format itself, the four &@s there in that string is hard coded.

So localizers can't get to that unless they go into the code. It's not localizable because obviously the order of words is one of things that varies highly between languages, so your format needs to also taken into account the different syntax between languages. So what's the right way to do that? Well first of all, break your format out into separate localizable string. So here you see imagine two localizable string files one for French and one for English.

We have the localize format as the key and then you have the content, the format itself for English and then the French. So then at runtime the first thing you want to do is grab your localized format and then use that format itself to create your formatted string. Calling stringWithFormat passing that in as the first parameter of the stringWithFormat, and then you can add in your other localizable objects.

That's pretty simple straightforward out. So if you're here-- if you were here for Session 112, the session previous, you probably saw a lot of detailed discussion of Cocoa text and the string et cetera. I'll go through some of the things that are available in case you missed that session. So NSString itself common operations like getting the case, changing case of strings, comparing strings, getting substrings of strings are also-- are all available. If you want to do with graphemes, you can detect the graphemes NSString to determine how to say cursor around them for example.

Now if you want to convert an NSString to another encoding, you can call getCharacters which will extract the UniChars or getCString which will extract that same text as an array of bytes. Find and replace and then editing. Editing, of course, would be done on a mutable flavor of NSString.

Now if you're here for 112 or you want to review that later, you saw a lot of detailed discussion of other classes that help you with deal with your Unicode and NSStrings.

These include tokenization, you can use CFStringTokenizer. CFStringTokenizer is so powerful it would even tokenize Japanese and Chinese text which are not separated by-- which words are not separated by byte space.

Number formatting, date, time formatting, pretty standard stuff. Protecting character properties such as a character whitespace is a punctuation, use NSCharacterSet for that. And you can also define your own arbitrary sets of characters. Use NSCharacterSet to detect if a string contains those. Language context-- content. If you want to determine whether your string contains English, Chinese, different scripts, whatever, we now have NSOrthography which will do the work of that kind of language analysis for you. Transliterating between scripts.

For example say you have a file of Chinese using Chinese characters and you want to convert that to pinyin, the Romanized orthography, you can use CFStringTransform to do that operation. And finally, normalization between the various forms of Unicode text, for example, I mentioned the Mac OS file system uses fully decomposed Unicode. Well typically, other systems don't really support that well so you probably want to convert text as file, filenames from the Mac OS into fully composed using NSStringNormalize or CFStringNormalize.

Now we provide a very useful tool to help you bootstrap your localization process called genstrings. Genstrings will take your source code run through it, look for all the places where you call NSLocalizedString and then produce an appropriate template file for each of those instances. So we have one line-- one line for each invocation of NSLocalizedString that house the key and the value. So basically it takes, it takes the key and copies it as the value also with the idea that the localizer will then sit down and translate the values.

Now it would also look for the localization enhancing and provide and insert those as comments into your .strings file template. So it's pretty easy to use, just run it over your .m files. The defaults are the best settings. I'm not going to go into those right now, but J. J.

can talk about that more. We can answer questions later. So what does it look like when you use genstrings? So first of all you start out with your Objective-C source file. Here we see a call to NSLocalizedString. The key is tallMountain and then there's the localization hint. This string is meant to represent the name of the tallest mountain, and fire up the terminal go into the directory where you have your resource file, run genstrings on that source file which in this case is Mountains.m which is one of the sample code files that we'll provide in a separate package. After running genstrings, you get this output.

You see you have a new localizable.strings file. Now if you look inside of that localizable.strings file you see that it's copied the comment or the hint to localizer and then provided the key value pair, and the localizer will then proceed to translate that value on the right-hand side.

It's pretty easy. So the next piece of the puzzle in doing, preparing your code for internationalization and localization is using bundles. And what's a bundle? A bundle is basically just a collection of the code and data that you want that your application needs. They're basically special folders. Users will see them as a single item although you can actually go and see the contents if you need to at least on the map. They have an identifier for easy and efficient access. And as a flexible but well-known structure that lets us get it all of the resources you need at runtime. In particular, bundles contain subfolders called with the extension .lproj.

The .lproj files are how you store your localized data. So basically anything can be a bundle. Applications on the Mac and iPhone are packaged as bundles, frameworks and bundles. Nibs which are the collection of UI elements, for example, menus, tables, buttons, et cetera are also bundles especially beginning with Leopard.

So basically the bundle lets you-- the architecture lets you combine all of the elements you need, code and data, UI of localization inside a single package, and this is how you should be delivering your applications. So we talked about the localizations that actually reside within your bundle. So I said that within the bundle you'll find multiple.

lproj folders. This will contain nibs with your localized UI elements. Now you can fire up Interface Builder. Localizers can go through and change the menu in place and they can actually see how the localized string should fit in the menu so they can adjust the size of the items in the menu to get the best appearance for the localization and the strings files, the localizable.strings files. Now functions especially NSBundle which we'll talk about in a second. You know how to go through the bundle and look to get the right localization for the user's preferences.

Now remember I said that users can choose a list of preferences in language order. But you may not have all of those localizations in your application. So NSBundle knows how to look for the-- compare the users' preferences with the available localization. So for example, if the user has choose-- chosen Albanian, Bulgarian, Catalan as their preferred languages but you only localize Bulgarian, the answer is going to be Bulgarian.

Now, that for the names of the lproj folders, we use the convention BCP 47 which is a standard internet convention for naming languages. So if you look inside the lproj-- inside of your bundle and you've done some localizations say, you might see files like en.lproj, fr.lproj for French, zh.lproj for Chinese, et cetera, depending on which localizations you've added.

Now also you don't really need to set these things up yourself because inter-- excuse me, Xcode will add in all of the correct lproj names as you edit a new lproj as well as you add new localized resources. It'll handle the structure of the lprojs within your bundle for you.

And J. J. is going to show that a little bit later. So let's look at the NSBundle class. This is how you deal with bundles. Now one thing to keep in mind about NSBundle is it's not toll-free bridge with CFBundleRef. So you can't just patent, you can just interchange them. So typically, to do something with the bundle, you'll first get your bundle. If you're an application you call mainBundle which will return your bundle for you. You don't need to do anything else. And then you ask the bundle for things within it.

For example, you want to get a particular resource, call pathForResource and then the name of the resource and the type which is the extension. Typically it's going to be something like plist if it's a, you know, some kind of NSObject say an array for example. Now, there are numerous ways to get different resources but this is one good example.

Now note that you don't have to provide a language when you make this call because at runtime NSBundles will actually figure out, looking at the users' preferences and matching up your localizations to get the best-- the best path to a localized resource. One is appropriate for user settings.

Let's talk a little bit about using locales. So what is locale? It's basically a collection of objects-- of objects that correspond to customs, settings, use in a particular region, for example calendars, date-time formats, currency. On the iPhone how you format a telephone number. These are all the kinds of things associated with locale, measurements. Probably the US is the only country in the world where measurements are separate from the rest-- rest of the planet but it's a kind of thing you can get.

Now all of our locale data actually comes from the Open Source project called the Common Locale Data Repository, CLDR. I mentioned that because if you find that locale is missing or you find that locale data is incorrect especially if you're, you know, you know a lot about that locale and you know that it's something's wrong, well you can actually work directly with a the CLDR and get that changed. The next time we release the OS and it matches one of our, you know, something that's changed in CLDR we'll just pick that up and get a new version of CLDR.

Let's look at the NSLocale class. NSLocale represents both all locales as a class and individual locale objects. So for example if you want to find all of the locales available on the system, you've used a class method on NSLocale. It's toll-free bridged with CFLocaleRef. Now often when you're doing something like formatting a date or time, the locale is actually assumed. You don't have to worry about it. But if you need to change the locale for example where you want to create a number format and use a specific locale, you can actually set the locale on particular objects. For example here we see a call to setLocale.

So how did you get a locale? Well, you can start by calling currentLocale which-- this is actually a misnomer because it's only in the current local when your application was launched. As I showed you earlier, the user can actually go in and change the locale. They could change the locale using the settings on the iPhone while your application is running.

So what do you do? How do you get the really current locale? Well you can pull-- you can call another method autoupdatingCurrentLocale which will actually give you the current locale or you can register for notifications. That way say you have some data that you've been cashing and you need-- you need to refresh it, just do it whenever you get the notification. And finally, you can create any locale you want using standard formatting pattern identifiers, typically like name and country, using identifiers defined by ISO.

So there are a lot of ways to create locales. Typically, you probably won't use this. Just assume the current locale is the one that's best. Given the users' preferences, let the user be in control. Now what are the kinds of things you can get for locale objects? As I mentioned, locale objects are basically collection of things.

In fact if you look at the name of some of the objects on a locale object for key, you can imagine what the implantation is, it's some kind of NSCollection. Currency, measurement, location, we define all these. And if you want to find a particular locale item for example the currency, you just use the predefined name and call objectForKey on that locale.

Here are some examples: NSLocaleCurrencyCode, NSLocaleUsesMetricSystem, it's just true for every place in the world but US and maybe Burma, the last time I was there, but they are probably more progressive than we are. So one thing I'd like to point out though is that the locales can be different from your current language. So, for example, you can get the country name for the locale.

So if your country is the US locale, you should expect some-- hence your language is English, you'll expect something like the United States. But if you have a US locale and your language-- your UI language selected is French, it would be [foreign language] or Japanese, [foreign language] Cocoa whatever.

Let me talk a little bit more about the differences between locale and language. So as I said the current locale gives you the current settings at least when your application was launched for the region formats, date, time, number, currency, et cetera. Now those can be dependent-- independent of language.

If you want to find out what the users' preferred languages are, call preferredLanguages. That's also an NSLocale class method. This will return an array of languages using BCP 47 language IDs, and the order will be the preference that the user put them in using the UI that I showed earlier.

So just remember that language and locale are independent of each other, that's why you can get, you can have a US locale with French language, because maybe a French user here wants to use a US date time formats but they want see all the strings in French, it's a very common scenario.

Now finally, let's put it all together. I'm going to show you three common operations you'll be doing to support localization. First of all, getting a data file, then a localized string, and finally a very simple formatting a number. So imagine that your application contains some data, it's basically an array of localized mountain names.

You'll have a different, different set of strings for each localization you support, and you want to get those at runtime, then you call pathForResource on your bundle. Start by getting your bundle. Call pathForResource with the resource name which in this is mountains in plist. Once you've gotten the path, which will be the path that's correct for the current localization, just instantiate those objects as an NSArray using NSArray method, arrayWithContentsOfFile, and you've got your array do something interesting with it. Pretty simple. Something slightly more complex. Here's the right way to do to get a localized string.

Start out by getting the format. As I mentioned earlier you want to separate the format from the actual runtime string, so the format itself can be localized. In this case you call NSLocalizedString with the key the sentence format which should be match a key in your localized.strings file which I showed you earlier and then the hint.

Oops, okay, and you get the name, so you have the mountain names and some canonical mountain name format and you want to get the localized version of that using a key. Now since your intent is to display a string something like, you know, "On such as such a date, this mountain having, an altitude of so many feet or meters was climbed". You need to get a correctly localized number representing the height.

So create a number formatter. Set the format you want, in this case, decimal style that will make sure that you get the right decimal separator for example for the locale that you're going to be representing the string in. And then, you know, pass in that number which is NSNumber, the value mount.height and then format the string.

Next you go on to do the same thing with the date. In this case get the date which is represented as NSDate. You format it using a medium style and then you get the string formatted string. And finally you put all of those things together using stringWithFormat and the output if it's English, for example, would be 8,848 meters tall, Mt. Everest was first climbed on 26th of May 1953.

Finally this was what it would look like to format just a number. Now in Snow Leopard, there's a much easier way which was discussed in Session 112, but that's not available on both platforms so this is the way you do it for the phone and the Mac if you want the same code. Again create your number formatter, set the style for the formatter, and finally just generate the formatted string. Pretty easy.

So with all of these tools, I think you can go on and start building your application making it local-- making it localizable and have a great product. Now let's talk about the details of actually localizing. I'm going to turn over this session to J. J. Enser. He will talk about localization. Thank you J. J.

[ Applause ]

Thank you. Thank you, Lee. Hi, I'm J. J. Enser and I'd like to do a quick run of the localization process itself. So after you've done all the cool stuff that Lee talked about and you're code is ready, has been internationalized, you can start considering localization in itself. And localization can really be summarized in three step process: Preparing your project, translating your strings, and applying any UI layout adjustment as needed for you languages.

So the first thing you want to do is tell Xcode which files in your projects need to be localized. And that's done very simply by selecting the files that you're interested in under your resources view, you bring out the inspector and click "Make File Localizable." That puts all the files that you selected into the English lproj that Lee talked about and put them in one place that allows you then to add additional languages following the same structure, just click Add Localization, name your lproj, and as it was mentioned, Apple recommends that to use standard identifier for locales defining BCP 47 and Xcode will create the corresponding lproj folder for you in your project.

From that point you can go ahead and edit the files in their respective lproj using Xcode for or any text editor for strings files or using Interface Builder for your nib files. Now that would work for small size project. If you want to deal with a larger project with a lot more UI complex files and also reuse previous translation, you can use AppleGlot. AppleGlot is an extraction leveraging and integration tool for localization. It supports multiple file types such as strings, nib files, XMLs. It extracts all the-- your localizable content from those files and put them into one single document that can easily be shared with your localizers.

This document is actually using an XML-based format for localization interchange called XLIFF and supported by most of the standard or modern translation tools. AppleGlot also has the ability to apply standardized glossary of translation to your application so that you don't have to translate everything from scratch. Apple supplies such glossaries based on Mac OS X. And you can put them in the right place in AppleGlot so that those translation are there for you. And finally, AppleGlot has the ability to carry over any translation work or UI work that you've done in previous translation or previous localization of your app.

Thus this-- applies this to strings as much as UI. So let me give you a quick overview of AppleGlot itself. So for those of you who might be familiar with these tools that have been around for maybe fifteen years or so, this might come as a surprise as AppleGlot is no longer GUI application but a common line tool. You can get the full usage by typing appleglot -h, I'll just go over here are the comments available for you.

Create will simply create on your disc or an environment that setup subfolder to contain all your localized data, glossaries, produced translation, and results. Setlangs would-- well setlangs, I'm sorry, will simply define the source and target languages. Populate will effectively extract all the strings from your localizable files and generate that XLIFF document I talked about. It would also apply glossaries if you supplied any and carryover previous translation.

Once that XLIFF document has been translated by your localizer and you get them back, you use update command to integrate those translation back into your software. And finalize just cleans up the environment, removing temporary files. So once this is done, all your translation has been carried over but your UI hasn't changed in terms of geometry or layout. So you, at this point, need to probably do some work to address the UI for each language. These can easily be done directly in Interface Builder.

You can identify and address those UI layout issues by doing some resizing and getting to something like this. In this example, you know, you can-- might notice in the second box that the text overflows on top of another UI object and that the button label was clipped in initially 'cause it kept the English size, and after very easy manipulation, you can address your UI this way. Now this works fine for MAC OS applications.

You have, you know, a lot of room. For those of you who develop iPhone applications, you might need to address things slightly differently because as you can imagine, you don't have the same realistic data to work with. Here's an example of what's been done on the iPhone OS with the familiar slide to unlock button on the locked screen. Internally, we translated-- we decided to translate this as unlock, just unlock, not slide to-- and that's possible thanks to genuine and very intuitive UI that was designed for that.

The text is shining and, you know, there's an animation that shows that the text can-- that your fingers should slide across and you have of course the arrow button that also slides. So that's a hint also to think really deeply about how you design your UI so that you can keep the text to a minimum especially on a device like the iPhone where the font needs to be a decent size, you know, for example reducing the font size in order to translate, you know, slide to unlock would not be a recommended solution for usability reasons and consistency.

So let me show you how this works in practice. So I have an Xcode project right here that's the same as was used in the previous examples, international mountains, that's part of the simple codes that you have in the iPhone SDK. Currently, this application is not localizable. I'm going to run it just as it is just to show you what it does. Simple navigation-based application with the table view, a list of a mountains, you know they have very original names.

And each roll leads to a detailed view that shows when the mountain was first climbed and how high it is. That's directly the result of what you've seen in the previous internationalization slides.

That's it for the application. It also has Settings Bundle that you can see here. And that Settings Bundle has a simple switch to sort the table.

So what happens if I change language right now? I'm going to do this at the OS level again, switch to French. I can see that my settings application is-- has a localized name, but apparently you know, my Mountains application hasn't changed. It's still in English, it hasn't been localized yet, but it defaults to English because there's no French localization available. So let's fix that. So as I showed you, I'm going to do this with one file here as an example. We have a localizable.strings with all our key value pairs. I'm going to simply add French localization to this file.

Here's fr. And you can see that I now have two copies of my file that are identical but Xcode explicitly shows that as well as in the resources hierarchical view where you see both localizations here. From here I could directly, you know, of course edit those strings in Xcode and translate them, but let me show you how this is done in AppleGlot, using AppleGlot.

So here's what an AppleGlot environment looks like after you run the Create command. There's number of subfolders here for your glossaries, for your English software, for your localize software, for your previous software if you have any. And also, this is where the centralized XLIFF document leaves where translation is going to happen.

Let me quickly show it to you. This is the XML content where you can see strings have been extracted from all of your files and positioned under source and target tags within the XML. You can also see the comments that Lee mentioned, the localization hints that you put in your code that ends up also in the XLIFF so that localizers can find it and see it when they-- when they translate. And you can see that there are file tags that that show that all the strings have been combined into one document for multiple source files.

So you send that to your translator, you get a translated file back, you put it back directly in your AppleGlot environment. And from here, I'm going to run AppleGlot to show you how to integrate that back in the software. So I'm going to point it to my AppleGlot environment.

And I'm going to type update. That's it. It processed the nib files, plist files, and strings files that were in my English lproj. And if I look-- take a quick look at the new lock folder, I now see a French lproj, and hopefully those are localized. I find all the pairs here have been updated. All I need to do now is to copy this French lproj back into my Xcode project.

[ Pause ]

And now I should have here the two flavors of this file, for example, showing the French translation. So let me go ahead and try and see the result directly in the simulator. Here we go. Application is now localized including the Navigation bar and the Detail View.

Let's check the Settings Bundle. Here it's localized as well and here it's localized as well. Oops, there's a bit of problem here. My translation is slightly too long, it no longer fits in the UI label. So I can go ahead and edit that directly over here. I go to my Settings Bundle in Xcode to find my .strings file. I can shorten the translation and also improve it at the same time. And if I build that again, this should show, localized. Let me go back here.

Okay, so yeah, it works.

[ Applause ]

Okay, the last thing I want to show you here is if I go back to the table view, I notice here that this is almost localized or is it? Data is still in English and the unit showed-- shown here although translated still means foot which is not the unit used typically in French, but that's not a localization issue As Lee mentioned, we use the proper APIs here NSNumberFormatter and NSDateFormatter to rely on the locale settings. And if I check here, sure enough I'm still using United States settings.

If I change that to French--

[ Pause ]

This is now properly localized for French. The translated date, unit that is French.

[ Applause ]

And by the way, the number here is of course addressed to-- it's recalculated. Okay. So back to the slide. So before we conclude let me give you a few design tips that will allow you to make your app a good world citizen.

If you design your app in English you need to know that most languages or many languages let's say use longer strings, longer words, to say the same thing and it's a good practice to allow for 25 to 30 % of growth of your UI. So try and keep leave some room around your text objects so that localizers can increase them if the need without making your UI too busy.

If you use a string with format and placeholder in your string, those %@ that's were mentioned before, it's a good idea to index them using the $1 in the middle or $2 or $3 and so on so that when you switch to Chinese or German, you have the ability, the localizers have the ability to easily swap those variables around and keep just a good structure for the sentence in their language.

Also if you can afford it, try and use graphical representation rather than textual for your button labels. That's the two examples shown here on Mac OS and the iPhone where it's obviously easier to localize 'cause you don't have any text to translate and it makes your UI more lightweight as well. And finally, those couple-- there are couple command line tools like xmllint and plutil that can be used to verify the integrity of your localized .strings files and make sure that they're structurally valid.

Things to avoid are the following: the number one is of course using string concatenation in your code. Putting pieces of a sentence together at runtime will practically make that string impossible to localize. If you want to said you want-- you really want to save, you really want to quit, then by all means, create two strings. There's no point in trying and optimized that by having one string and the verb being a variable because that will break in some language structures.

So instead, it's much better to use the NSLocalizedString in combination with stringWIthFormat where you would have the full sentence in one piece that localizers will do a better job with.

Also don't assume gender and number in your strings. Those are two notions that vary a lot across languages.

And if you want to say, "Your preferences will be saved" or maybe "Your investments will be saved" or "Your children will be saved", it's much more important to create three strings, same thing. "Your" and "saved" will likely be translated very differently based on what the placeholder will be substituted with.

And also, general, you know, guideline tip here which is to keep text and graphics separate using text embedded within bitmaps will obviously make that bitmap much harder to localize. It can be recreated but you setup in a word than having them on separate layers or use 2 objects, distinct object, one for the text, one for the graphic behind it. And to finish, I'd like to a say word for iPhone developers who intend to publish their app on the App Store.

Apple's single binary multiple localization model allows you to ship one single application with all your localizations in it. That simplifies in itself the submission process 'cause you don't have to submit multiple times multiple applications and then wait just as long for each of them, instead you do a single submission and if you support multiple languages, you can set your primary language at submission time and you add additional languages in iTunes Connect.

At the same time, you can also, and that's really recommended, to provide localized product description and screenshots for each of those localizations so that your customers on the App Store will see this information in their language. So Apple's localization model is really simple and really efficient. And by adopting it, your application will fit right in on your customer's desktop and device around the world.

Thank you.

[ Applause ]

Thank you J. J. So we talked about a very big topic today, a lot of different aspects, internationalization, localization, there's just a lot to absorb. But in summary I like to just focus on a few things. Remember Unicode. It does everything you need. You don't need to worry about other encodings. In fact, you don't even think about the encoding most of the time.

Just use NSStrings. When you're dealing with Unicode, don't assume that you can just deal with it character by character because as we found, you might be breaking things up the wrong way. You might be trying to add things together that don't add correctly. Do use the API we provide for doing this kind of operations like measuring string length, for example, breaking words and characters.

Don't try to write your own internationalization support, there's a lot already built in. If you find something that's really missing or that's broken, let us know, use radar, power plugs [phonetic]. But I think everything you really need, 99 % of the time is already there. Just take the time to learn it and I think you'll be well on your way to a good localized product.

Above all, don't hard code strings in your code, strings that are meant to be shown to users, because they're simply not localizable. There's no way to farm out your code. You don't want to farm out your code to a localizer. You want to have a separate .strings file that the localizer could translate so they don't have to touch your code.

And finally, avoid assumptions about the visual layout of UI elements and menus of text on the screen; it could change depending on the language. Similarly, don't assume the order of words. Syntax and language are highly flexible. Your code and your data should be similarly flexible to handle that. Okay, here are some more information. After the session you want to find, talk to an evangelist, some useful documentation.