Video hosted by Apple at devstreaming-cdn.apple.com

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: wwdc2011-131
$eventId
ID of event: wwdc2011
$eventContentId
ID of session without event part: 131
$eventShortId
Shortened ID of event: wwdc11
$year
Year of session: 2011
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2011] [Session 131] Getting You...

WWDC11 • Session 131

Getting Your Apps Ready for China and other Hot New Markets

App Frameworks • iOS • 48:41

Sales of Apple products are increasing dramatically in new markets such as China. Apple platforms now support new localizations such as Arabic. Quality localization is critical to the success of your apps in these rapidly growing markets. This session will cover the things you need to know to succeed, including: 1. What kind of language support is offered and how you can test. 2. Making your application localizable for these markets. 3. How to make sure your application supports bidirectional UI.

Speaker: Brent Ramerth

Unlisted on Apple Developer site

Downloads from Apple

HD Video (174.8 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Good morning. It's great to see you all here on a Friday post-Beer Bash. My name is Brent Ramerth and I work on natural language processing and internationalization. And today I'd like to talk to you about how to make a great international app. So you're all here at WWDC because you want to make great apps for iOS and Mac OS X. Well, part of making a great app is making an app that is accessible to as many people as possible. And there's no greater audience for your app out there than the international market.

There is a wealth of potential customers for your app in countries like China and Japan, in regions like Europe and the Middle East. And in order to take advantage of these markets, we want you to be familiar with the basic concepts of internationalization. So that's what we're going to talk about today.

Now, if you've never made an app for a country outside of your own, this talk is definitely for you. We're going to cover the fundamental concepts of internationalization, the APIs that you'll use when making an international app, and we're going to get you started. So if you've already got some experience in this, we're also going to have stuff for you today.

If you've already done some internationalization or you've already done localization, that's great. We're going to talk today about some common mistakes or pitfalls in using the APIs and how you can avoid those, as well as look at some new APIs and features for internationalization. So let's get started.

Now, for the last six quarters, the majority of Apple's revenue has come from international sources. In Q2 of 2011 alone, 59% of revenue originated from outside the United States. And, so, there is a lot of potential market for your app in these countries. The fastest growing market for Apple products is not the United States. It's China.

So just to give you an idea of all the different countries out there, there's a lot of different countries you can be shipping your app in. And most of these, outside of the United States, most of these customers are not native English speakers. They use different writing systems, different date and time formats, different calendars, just to name a few.

So here we see a map of all the countries that are currently shipping iPhone as of May. As you can see, there's a lot of coverage in the Americas, great coverage of Europe. Asia and Africa are covered. And a lot of coverage in the Middle East and the largest markets in the world, China and India.

So the point here is that as a developer, you don't have to be constrained to shipping your app in your own country. You can ship your app in China if you're an American developer. If you're a Chinese developer, you can make your app for the United States, Russia or Brazil, just to give you some examples.

As far as localization goes, on iOS, you can make a localized app for any of these languages. These are the system UI languages. And on Mac OS X, you're not constrained to this list. You can make your app localized in any language. Now, localization is not going to be the primary focus for today's presentation, but I just want to give you an idea of the potential scope for the languages of your app.

Now, before we get too far, let's clarify what is the difference between internationalization and localization. These concepts are sometimes confused. Now, internationalization, simply put, is designing your app for international compatibility. This includes text content. We want your apps to be able to handle input, output and to be able to process text in any language and in any script. Furthermore, your apps should be able to handle different date and time formats, different number formats and calendars and time zones which give us no end of trouble.

Now, internationalization is something that you as a developer have to do. You have to adopt the APIs, make the code changes. Localization, on the other hand, another piece of this puzzle is something that you can ship out and get experts to help you with. Localization is translation of your app's user interface and resources. So once you have extracted the strings from your app and the resources, then you can ship them off to a language expert and they will help you translate them.

So about internationalization, maybe you've heard that making international apps is hard and there's a lot of different languages out there, a lot of different formats and, you know, why would you want to do that on top of all the other things you've got to do with your app? Making new features and fixing bugs, all that kind of stuff. Well, the point is internationalization doesn't have to be hard and if you follow the APIs and the guidelines we're going to set forth in today's presentation, it's easy.

We're going to be able to make one app for all locales and that app will be able to support content in any language. So here we see pages and it is localized in English. The content is in a variety of languages including Chinese, Thai and Russian. So that's the kind of thing you'll be able to do.

For today's talk, first off, we're going to talk about APIs. We're going to look at the APIs that you'll use to develop an international application and some guidelines for use of those APIs. We're then going to look at some international user interfaces and how they pertain to testing and what your users are going to be seeing. And finally, we will look at some new APIs and features in the international space.

So first, let's look at international APIs. Now, many of these APIs are probably familiar to you already if you're a Cocoa developer. APIs such as NSString, NSLocal. Today we're going to look at these APIs as they pertain to internationalization. How to use them appropriately when developing an international app.

First of, let's talk about locales. Now, locales are going to drive all of the other topics that we will cover today. A locale is fundamentally an encapsulation of regional formatting standards, which are specific to a language and usually a region in which that language is spoken. So, for example, US English is a locale, which corresponds to properties such as does not use metric system, separates groups of numbers with commas, et cetera. And so these locale objects are going to drive a lot of the APIs that we're going to use. They're going to be used to localize UI controls, etc. And we're going to use the NSLocale API to handle these.

Now sometimes locales are confused with localization. These are two separate concepts. Locales represent the formatting standards for your app. How does it handle dates and times? What is the calendar? What is the currency? The localization is the UI language. How are the strings localized? What is the language of the app? Now, it's often the case that these two correspond, but it's not necessary that they do. It is perfectly legitimate to have your app be localized in English and have locale set to Japanese. You'll see English UI strings and your dates and times will be shown in Japanese.

So let's take a look at what that would look like. On the left, we have iCal. And it is localized in English. You have an English keyboard up and English UI strings. The dates and times are in the standard U.S. English format. And using 12-hour time. Here we've set the locale to be Japanese and now the dates are in Japanese. However, the localization of the app didn't change. So you can mix and match these two.

Now that we've seen what a locale looks like, how do we actually access them? There are two ways. There's a way that you can get a dynamic locale object which will update as the user changes their locale. And that is done with auto updating current locale. There is also a way to get a locale as a static object. And there are three different ways to do that. First off, you're probably going to want to use the current locale which is the user's settings for formatting.

Whenever you're doing UI visible things, you always want to use the current locale. You can also create an arbitrary locale using init with locale identifier. You might use this if you were, say, trying to display a calendar from a different locale. And finally, there is the system locale. The system locale has limited uses. It's primarily used for parsing and formatting fixed format expressions, and we'll get into that a little bit later in the presentation.

So now that we have locale objects, we can do a variety of things with it. First off, let's say you're making an app in which you want to present measurements to the user. Well, you probably want to present these measurements in the measurement system that the user is familiar with. So we can use the NSLocale as a dictionary and pull out the key associated with NSLocale uses metric system. And that will tell us whether the current locale uses metric system.

Similarly, we can access the current currency code. Let's say you're making an app which needs to display currency information. We can do the currency conversion, or you want to do currency conversion, so you need to know what the currency code is. And so we pull out the currency code from the locale like so.

Now, let's say you want to present a quotation in your UI. Well, quotation marks will vary from locale to locale. Different locales use different symbols. So we want to create a localized quotation string. And again we use locale. We can pull out the begin quotation mark like so. And the end quotation mark. Create a formatted string with those two. And now you see this will do the right thing in all the different locales. For China, it will give you double quotes. For Korea, single quotes. And for Japan, the brackets.

Now throughout today's presentation we're going to talk about some common mistakes and pitfalls that we see in using international APIs. First off for NSLocale, it is commonly thought that you can access the UI language using locale but as we saw in previous examples that's not the case. You can have those vary independently. So it's not a good idea to use NSLocale language code in order to access the UI language. Instead you can use the NSBundle API.

NSBundle is your resource for accessing all of your localized resources. There is a preferred localizations method from the main bundle which gives you a list of the user's preferred UI languages and the item at index 0 in that list will be the preferred UI localization. visualization. Also, system locale is commonly confused with current locale. It's important to always use current locale when you're doing UI formatting. System locale is a static locale and it's going to be used for formatting and parsing fixed expressions.

So now I'd like to show you what this looks like in the UI and how you configure these various settings in the Mac OS X and iOS. So let's go to the demo machine. So here I have System Preferences and we're going to go into Language and Text. And the formats panel.

So here is where we would configure the locale setting for the app. This is typically going to be done during installation for the user. The user will configure the locale appropriately. But when testing, you might want to test with different locales. So here's how you would go ahead and change that.

The current region here shows what I have set. It's currently set to United States. And these are all the locales that are associated with my current UI language of English. I can go ahead and show all of the regions that are in the system. And let's see what happens when I change that to Chinese as spoken in China. So now as you see, we have a different format for dates and times. Up in the menu bar, we're now showing a Chinese date and time.

And in our apps, our apps are showing Chinese dates and times. So this is going to be useful when testing various locales. For changing the language, we can go to the Language window here. And this list is freely orderable. You can drag these into any language. This will affect things like font fallback.

So if you have some questions about that, we can cover that in the lab. But let's say we want to change this to Korean. So we're going to drag Korean up to the top. and restart the app. So now you see the UI strings of the app are in Korean. However, the date and time has stayed in Chinese.

Okay, let's see what it looks like on iOS. Okay, so similarly, I'm going to go into settings. And on the general panel, let's go to International, Region Format. So here you have all of your region formats. And we can change this to, say, Thai. And now in the region format example, we have different dates coming up and that will show up throughout the UI.

So this will be useful when testing and just to make sure that your app is showing strings that fit in the UI and so forth. Okay, next let's talk about numbers. Number formats will vary widely from locale to locale. First off, not every locale uses ASCII digits. In Arabic and Hindi, non-ASCII digits are used in numbers.

The grouping separator and the decimal points change from locale to locale. For example, in French, the space is used for separating groups of numbers and the comma is used for decimal point. Different locales will use different currency symbols, of course, but also the order relative to the number will change and the spacing of the currency symbol. And finally, percentage amounts. In Arabic, a different percent symbol is used for marking percentages.

So in order to handle formats in a locale appropriate manner, we want to use an API which is aware of all these differences. That API is NSNumberFormatter. Now NSNumberFormatter gives you a convenience method called localized string from number, which we can use to create localized numbers. And you just pass in NSNumber and a style for that number and it gives you the string. If you need to do things like customizing the number of digits in your number or if you need to parse a number, then you need to create your own NSNumber format or configure it appropriately and maintain it.

So here are some examples of number formats that are built in. The general format will be good for general purposes. You have the currency and percentage and scientific, which are self-explanatory. And there's a spell out format, which would be good for things like doing text to speech, if you need to send a number to text to speech.

So the most common mistake in working with number formatting is using old C style functions like printf, scanf, and string of format to format your numbers. Now these expressions like %d and %f, those work fine when you're doing ASCII digits, but they're not going to handle non-ASCII digits correctly.

So if we use code like this in an Arabic locale, we're not going to get the right result. That's not the correct Arabic digits. Instead, we should use NSNumberFormatter and the LocalizeStringFromNumber convenience method to format this number appropriately using the decimal style. And again, if you need to configure the number of decimal points, you can do that with an option on the NSNumberFormatter.

Now we will get the right result for both locales. Another common mistake is in setting constant pattern on the number formatter. So in this example, we've set the format string to include a dollar. And so we are trying to do a currency format string. This might work fine for the United States, but in countries that do not use the dollar sign like China, it's not going to work. So we don't want to eliminate that information that the locale object is providing for us by overriding it with a constant pattern. Instead, we can use the simple built-in format, the NSNumberFormatter currency style, and that again will do the right thing regardless of the user's current locale.

Now, let's take a look at dates and times. A lot of similar suggestions apply to dates and times. Dates and times start incorporating more of the natural language strings. So when we are formatting a date for French, we will see French strings in the date. That would be today's current date in France.

The calendar in use will affect the date format string. There are many calendars out there besides the Gregorian calendar and the Japanese calendar is just one of them. This is what the Japanese style date would look like in the Japanese calendar. The digits in your date will change as we saw earlier. This is what it would look like in Devanagari in Hindi.

The 12 versus 24 hour time varies from locale to locale, so we want to present the right time string for that. Finally, the order and the format for dates will change. So in Vietnamese, the time string comes first, and then the year, then the month, then the day.

So when we are working with dates, we want to display dates in a locale-specific manner. And to do that, we use NSDateFormatter. Again, NSDateFormatter has a convenience method called localized string from date. You can pass it in NSDate and the styles for the date part and the time part.

If you don't want to have the date part or the time part, you can pass no style. And if you need to do more complex applications such as parsing dates and times or setting custom formats for your dates, then you can configure a date formatter and maintain it.

So here we see some of the standard date and time formats. This goes from most abbreviated to least abbreviated. And there's, of course, the option for no style. So these will be localized appropriately depending on the user's current locale. It may be the case that those built-in formats don't meet your needs. Let's say maybe you have a very constrained UI field and you want to just display the month and the date of the current date.

To do so, we want to make sure that you are maintaining locale sensitivity and not setting a constant pattern. So we have a built-in function called Date Format from template, which will give you a properly formatted string for the current locale. So in this example, we're going to pass in the day and the month, and we're going to get a format string for the user's current locale.

And we're going to set the date formatter to use this format string and then create a localized string from the current date. So now you can see this gives the right result for both French and for Chinese. In French it places the day name, the day first and then the month. And then for Chinese it places the month digit, then the character for month, and then the days, and then the character for day.

Again, using static formats is a way that you can get in trouble with these formatters. So in this example, we're setting a custom format which puts the month and then the day and then the year separated by hyphens. And that's just not going to work for the majority of the formats out there in the world.

So it will work for English but not for, say, Chinese. Instead, we encourage you to use localized string from date. There is an easy way to do this using short style for the date and no style for the time. And then we get the right string in the UI for both English and for Chinese.

Now let's talk about a slightly more complex example involving dates and get time zones involved. Let's say you're making an application and it's pulling dates off of the web in a fixed format, something like this, where year comes first, then the month, then the day, and then hours, minutes and seconds.

So we've got a date that we pulled off the web and it has an inherent time zone associated with it, in this case UTC. So this is roughly the current time. Now we've parsed that date and we want to show it in the UI in a localized fashion. So we're going to display it in the long style in the UI.

Now, when we're using this app in San Francisco, we've got the wrong time. It's not 5:46 right now. Also, we're getting the wrong time in Beijing, where it is definitely not 5:46 either, because there's a 15 hour time difference. So what did we forget to do? What we need to do here is set the time zone before we parse that date.

That time zone was in the UTC format, but we interpreted it as the local time zone. Instead, we wanna make sure that the date formatter is using the UTC time zone, which you can get with time zone for seconds from GMT-0. And then, We will get the right values for the dates out and be able to localize them appropriately.

Now there is still one thing missing in this example, and it's a little more subtle, but it'll come up when you are using a locale like Arabic. Now as we saw in a previous slide, Arabic does not use ASCII digits for dates or times or numbers. It uses Arabic style digits. And so when we were parsing this string, the date formatter was looking for those Arabic digits and it doesn't find them. So it's returning null.

And of course, that's not the right answer. This here is where we want to use system locale. So system locale is going to be constant across all of your user systems and it basically corresponds to POSIX settings which allow you to parse and format dates in a numeric POSIX style format. So using system locale here will override the user's current locale setting and make the date formatter look for the ASCII digits and allow it to get the right value out so that we can localize the string appropriately.

So next, let's look at some calendars. As you're probably aware, the Gregorian calendar is the standard calendar across most of the countries of the world. However, there are countries that do not use the Gregorian calendar, such as Thailand. And there are many calendars that are in use for religious purposes, for example. So in the current Thai calendar, the year is 2554. The era of your calendar may change. In the Japanese calendar, the current era is Heisei and this corresponds to the current emperor's reign.

The number of months in a year is sometimes variable. You may have 12 or you may have 13 in some learner calendars, or it could vary from year to year. And also the number of days in a month. So for example, in the Coptic calendar, which is used in Egypt, there is a five-day month which picks up the rest of the days from the other 12, 30-day months, which could be six months in a leap year. And of course, February changes in the Gregorian calendar.

The first day of the week could be Saturday, Sunday, or Monday depending on your calendar. And finally, not even the day on which years change is static. For example, in the Japanese calendar, whenever a new emperor is coronated, a new era and a new year begins. So the new era, the Heisei era began on January 8th.

So, in order to handle calendars in a regionally appropriate way, we want to use NSCalendar. NSCalendar has all of these smarts baked into it for handling all these calendars. It's going to be able to give you information such as the number of days in a month and weeks in a year.

It will also be able to break down a date for you and give you the components like what is the current day of the week or the current day of the year. And it's very handy for doing delta computations. Let's say if you need to figure out what the day is three days from now in the current calendar.

Now, we had a talk about this at WWDC, I believe, on Wednesday. If you didn't catch that one, you can catch it in the video Slater. But there's some more details in calendar processing at that talk. So for Lion, we support a number of non-Gregorian calendars, including some lunar calendars like the Islamic calendar and the Hebrew calendar. And for iOS 5, we support Gregorian offset calendars. So this includes Thai, Buddhist, and Japanese.

Now our final stop on the internationalization tour today will be strings and Unicode. All of the operations like sorting, searching, and tokenization are very language and locale dependent. The behaviors of these functions are going to depend on the user's current language and the language of the text and the locale of the system. So we want to use NSString APIs to handle strings in a locale sensitive way.

Let's look at sorting and comparison first. So, for English, it's true that you could just order a string using the Unicode ordering of characters, A through Z. That's not always the case, though. For example, in Hawaiian, native Hawaiian letters sort before non-native letters. Also, the significance of diacritics varies. So, for example, in German, the letter U with a numlaut is sorted after Z. But in English, we wouldn't put it there, we'd put it with U. So that's going to vary from language to language.

Also, sorting might use language-specific knowledge. For example, in Chinese, the pronunciation of characters or the stroke count of characters can be used in sorting. So we definitely want to use a sort which is localized, which is going to do the right thing regardless of the user's current locale.

So let's take a look at a sorting example for Chinese. Now, as you can imagine, it's very difficult in Chinese to find what you're looking for when the characters are out of order. And when you use a standard compare like seen here, the characters are going to be sorted by the Unicode code point. And that's not going to help your Chinese users find what they're looking for.

Instead, we want to use localized standard compare. This is a function that will consider the collation ordering of the locale for the user in sorting. For the Chinese characters, it's going to use the pinyin pronunciation, which is the romanization of Chinese, and sort them alphabetically by pinyin. So now your users can find the characters they're looking for.

Some common errors in sorting are using diacritics and case sensitivity when sorting. So this will get you into trouble with languages such as French, where diacritic ordering is a more complicated process. And some languages might sort uppercase first and then lowercase. So in general, we don't want to normalize case and diacritics when doing sorting. Those are operations that we should reserve for doing searching.

Let's talk about searching now. For searching, we want to use range of string. Range of string is an API which will allow you to search for substrings in a locale specific manner. The reason we would want to do that is because in different languages, case and diacritics comparisons will vary. So, for example, in Turkish, what we consider the capital letter I when lowercased, that is the dotless I in Turkish.

And the lowercase I in English when uppercased is a dotted capital I. So these kind of comparisons are locale specific. Let's see what this would look like doing a localized case-insensitive search for the string "iki" in Turkish. So we have a string and we want to find the occurrence of "iki" in it.

We're going to do a search within the range of the string. And this search is going to be case insensitive, but in a locale sensitive manner using the user's current locale. So in a Turkish locale, we will be able to find that capital I with a dot for IKI. Now if you're doing searching in your app and you're writing an OS X app, you might be interested in taking advantage of NS Text Finder, which is a new UI class in Lion, which will give you that nice search call-out bar for searching in your app.

Next, I'd like to talk about tokenization. Now there are a number of situations in which you would want to break up the text in your app. Maybe you'd want to enumerate your text by lines or by paragraphs, possibly by sentences or words. Now, for words, this is a very locale sensitive operation because of the different writing systems out there that may or may not use spacing. Finally, there is the issue of iterating through character sequences, which we will talk about here. We want to use this API for all these different types of iteration, including characters.

This is what it looks like, enumerate substrings in range API. It is a block API so it takes the range that you are going to search in, a enumeration option which in this case is words, and a block which will process these substrings in a sequential fashion. So here we are just going to log out the substrings that we find in the string. For Chinese, it will do the right thing even though Chinese is not written with spaces. And of course for English, uses whitespace and punctuation tokenization.

Now, it is important to not assume that whitespace and punctuation are going to give you an adequate segmentation for all kinds of text. Because as we saw, Japanese and Chinese text don't use whitespace at all. So we actually need to do a linguistic analysis of the text to analyze it for its word boundaries. And so enumerate_substr_and_range will give you that information. For Thai, whitespace separates phrases roughly, so that's not going to give you the correct word boundaries either. So generally just stick with enumerate_substr_and_range, and you'll get the right results.

Now, I mentioned characters and that you should be using enumerated substrings and range for characters. The reason for that is that not every -- not every character in Unicode can be expressed as a single Unichar. It is not always the case that one user visible glyph corresponds to one Unichar. So, for example, the capital letter E with an accent can be stored as either one or two Unichars. You can store it as a capital letter E with an acute, which is the combined form, or a capital letter E plus the combining acute accent.

It is true that there are methods in Unicode to normalize a string. There's the canonical mapping which will compose it as best as it can. This is not going to fix this situation for us though because there are many situations in which there are combining character sequences that have no pre-composed variants. For example, in Vietnamese with tones and all the diacritics that are involved there. So, and also, any character outside of the 16-bit range cannot be canonicalized.

So we don't want to rely on canonical mappings. Any character outside of the 16-bit range is going to be stored as two surrogate pairs in your NSString. This is primarily used for idiographic character extensions for Chinese and some historical scripts, but these are important for Chinese, definitely. So, for example, this Chinese character is beyond the 16-bit range, U+272FE.

and it is stored as two unitars in memory. If we use a naive algorithm like character at index to access the characters of this string we are going to retrieve broken Unicode because these surrogate pairs will not render correctly. So whenever we're working with characters it's important to use enumerate substrings in range to access those character ranges appropriately and to render the string appropriately.

So now I'd like to talk about some different UI topics that will come up when you're developing your app and some different UIs that your international users will be using. First off, let's look at input methods. Now, for many alphabetic languages like English and French and German, it's sufficient to just put all the keys on the keyboard for all of the letters of the language.

But when you have a language like Chinese or Japanese that has tens of thousands of characters, that's simply not possible. And so we use input methods to input complex scripts to use a romanization, say, and turn that into the appropriate characters. An input method may generate single or multiple characters depending on the language of the input method.

So for example, here is what input looks like in Chinese. Now we have pinnion here, which is the romanization of Chinese. And we are converting that into Chinese characters. Now the pinnion here is in a state which we call marked. It has the underline, meaning that it has not been converted yet. So once the user has chosen a candidate in that candidate bar, then it's going to be committed. It's going to be turned into the Chinese text, and the marked text is replaced.

So when you are working in UIs, it's important to wait until the marked text has been committed before you start to process the text. Because in this example, the user is trying to search for the word "Apple." And they've written the pinion, but they haven't converted it yet. So if you go off and start searching on the pinion, it's not going to do the right thing. Instead, wait until the candidate is converted, and then we can do the search.

The way to do that is to identify the marked text range and there's a couple of different ways to do that depending on your platform. On iOS there's the UI Text Input Protocol which gives you a marked text range. NS Text Input Client Protocol will also give you has marked text and a marked range.

And a couple of other things to note about input methods. On iOS, we put the candidate bar above the keyboard. So if your app has content behind there, your app needs to be resilient to changes in the keyboard size so that the candidate bar is not obscuring important information. On OS X, it's important to avoid intercepting key events by, say, overwriting key down. There are ways to do this on NSTextView that will not break input methods. So it's important not to intercept those methods because input methods rely on space and enter.

So let's take a look at how you would bring up input methods. And I'm going to show you that on iOS. Okay, so here we have the general panel. This time I want to go into keyboards. And I'm going to turn on some international keyboards. So if you've never added a new keyboard to your iOS device, now's the time to learn. Let's say we want to add the pinion keyboard.

Now if I go into notes, Let's bring that back down. Now you see a new glow button has come up. And this is going to allow us to change between different keyboards. And so now we have the Chinese pinyin keyboard up and the candidate bar appears above the keyboard. So when I type Chinese in pinyin, we have the candidates showing up.

And there's a variety of different interactions with that candidate bar. So it's good when developing your app just to check that the candidate bar is working properly in your app and that your app's content is not obscured and that sort of thing. You can also use this to try inputting Chinese text and test your app with Chinese.

So now I converted and that's Chinese text input. Okay, so a couple more topics around international user interfaces. First off, let's look at text layout. Now if you're handling Arabic and Hebrew text, Arabic and Hebrew are traditionally written from right to left. So these scripts can also have left to right embeddings and the order and directionality of the text can get somewhat complicated. So here I see this is what it would look like on the screen. Let's see what it would look like in memory. The first word of this sentence is on the right and it flips around so that the first character was actually on the left.

So, for the Latin text, that comes in left to right. So, that alignment is different from the Arabic text. So as you can see in this example, the text that is visually adjacent on the screen is not actually always logically adjacent in memory. You can have directionality shifts in the middle of the text.

So if you are working on a text view and you want to handle bidirectional text appropriately, it's important to not make assumptions about the directionality or the alignment of text. Text needs to have its natural alignment. So you can do this by setting the NS paragraph style to natural alignment, and that will make sure that the text is right aligned for Arabic and Hebrew and left aligned for Latin languages and other scripts. And there's also UI text input protocol set base writing direction, which will help you handle the bidirectional text appropriately.

Along the lines of text layout, new in Lion, we now have vertical text, which is very popular in Japan. It's a very traditional way of writing Japanese text. So you can now edit and display text in a vertical fashion in your Cocoa apps. And this is accomplished by setting the layout orientation on your text view to NS Text Layout Orientation Vertical. The substitutions of the vertical glyphs will happen by default. It'll handle all the font changes and all that for you. And you want to use a horizontal iBeam cursor in these scenarios to match the style of the input.

And the final new feature is emoji. So we now have these in both iOS and in Lion. Previously emoji were only available in Japan before standardization into Unicode, but now these are in Unicode. So you can use them. We have an emoji font on both iOS and Lion.

And these characters are all in the supplementary multilingual plane, meaning they are beyond the 16-bit range of Unicode. So all of those -- the comments about Chinese characters and iterating through characters with ranges definitely apply to emoji here. So for more details on emoji, please see the Unicode 6.0 specification, and that will give you all the information you need.

To wrap up today, the important message is even if you don't remember all the particular examples of all the different formats out there, the key is to use foundation APIs and use the standard UI widgets and they're going to do the right thing for you in a locale-sensitive manner.

Test your app in situations that expose different languages, different locales, and make sure that your UI is rendering appropriately. If you're using the built-in foundation APIs and the built-in widgets, then things are going to work properly for you. So with that, I'd like to refer you to some of these related sessions. These have all actually passed already, but you can catch them on the videos. So thank you very much.