Localizing with Xcode 6

Session 412 WWDC 2014

Your app deserves a global audience, and iOS, OS X, and Xcode 6 make it easy to design and test applications in multiple languages. Discover how you can easily add and manage localized content, preview and test with different languages, and export and import content for localizers to translate.

Welcome everybody to Localization with Xcode 6.

My name is Zoltan Foley-Fisher and I'm an engineer with the Xcode team.

Later my colleague Chris Hanson will join me on stage.

So you've spent time on your applications artwork.

You've spent time on your applications code but you yet have to consider your global audience.

The App Store is available in 150 countries around the world.

Here's a small fraction of them.

And there are literally hundreds of millions of users shopping in these stores.

The interesting thing is there's no one dominant language around the world.

IOS is localized into 40 languages to reach a global audience to take your application to the next level, you simply must localize.

So if this sounds like an interesting opportunity, you've come to the right place.

We'll bring you guys up to speed on Apple's award winning support for iOS and Mac OS X.

And of course we'll detail some of the workflows in Xcode to help you get your application localized.

And for the old hands out there, we have some new features that I think you'll find very interesting.

I want to take a step back for a moment though and introduce an useful distinction.

Internationalization isn't quite the same as localization.

I like to think of internationalization as a milestone on a path to a localized application.

Internationalization is where you structure your application to be language and region independent.

That involves corralling the source strings, using interface builders auto layout support.

And then once the application is ready, then you can proceed to the second step, localization.

And I should be clear that users around the world see your applications in very different ways.

So here's an English application in the U.S. region.

Here's the same application in Mexico.

And you'll notice that in addition to being in Spanish, it's also got a subtle difference in the date.

Here's the same application running in Spain also in Spanish but again a difference in the dates and the number formats.

So your applications are going to have to handle these subtleties, and we're going to show you how.

First we'll create an internationalized application and then later, we'll localize it.

And to get things started, I'd like to introduce you to Chris Hanson.

[ Applause ]

Thank you, Zoltan.

So as Zoltan said internationalization is the first step on the route to having a fully localized application.

And I'd like to show you how to prepare your application and how to leverage Apple's frameworks to prepare for localization.

So we have some excellent Framework Support for internationalization and localization in iOS and OS X.

And it's completely pervasive throughout the systems frameworks.

Otherwise I don't think we'd be able to localize iOS into 40 languages.

And what our system does is separate your localizable data from the rest of your application thus making it much easier to produce localized variance of your application that are actually all built into a single bundle.

And these localized resources these localized data can be images, sounds and movies, and documentation in addition to the user-facing text in your application.

And even the user-facing text in your source code.

And as I said, it's extremely simple to use.

Now the first step is to start with NSLocalizedString.

NSLocalizedString lets you internationalize strings that are used in your user interface but that come from your source code.

And the way NSLocalizedString works, it lets you keep nice strings in your code such as format code.

You don't have to replace all the nice readable strings in your code with some sort of all uppercase identifier pointing into a resource file.

Instead with NSLocalizedString you can keep those strings as they are and still have your application fetch localized resources automatically.

And that's the important part.

There's no explicit loading or management of your localized strings needed at all.

And we have a couple of variants of NSLocalizedString such as NSLocalizedStringFromTable that you can use if you do need to keep different string tables for different purposes.

Let's just dive into some code.

Here we have the kind of format string that will often appear in your code.

Here I'm just saying how tall a particular mountain is so I can assign that to a label.

Now obviously this string is in English and it's completely unlocalized and un-internationalized.

So that's not really great for my users who use other languages.

Fortunately all I have to do is wrap that with NSLocalizedString and add in a comment explaining what the string is for and I've made that code localized.

Now that comment is important.

It tells your localizers, your translators, just why you're using that string.

Because different languages might use different terminology based on context.

So for example, a language might say that a person is a certain height tall in a completely different way than it says a mountain is a certain height.

Of course, that wasn't quite enough.

We were using StringWithFormat.

We should actually switch to LocalizedStringWithFormat.

What that will do is it will ensure that when we pass out that height as a number, that that number is also properly formatted for the user's language and region, including such things as localized digits because not everybody uses the same digit system, the same Arabic numerals that we use in say, the United States.

And of course, as with all of our system frameworks, this is all available from SWF as well.

And in fact, I think you'll like how we've how we're handling NSLocalizedString in SWF.

Now those localized strings, when you localize your application into another language will come from a dictionary called a localizable strings file.

And this is a nice readable file within Xcode where you can specify all of your localizable strings.

Of course, Zoltan will show you how you don't even have to author these files yourself.

How Xcode will take care of that for you.

At the very beginning of a particular entry in the file, you'll see that comment that you put in your source code.

And that's so somebody who is localizing that file will know just what that string is for.

Then the string exactly as it appears in your code will show up as a key in that file, and that's how our frameworks localization system will look up the string to use in another language.

And finally on the right-hand side, you'll see a modified version of that string.

Now here this is just an English file, but some languages actually use a different word order than English.

And instead of making you write some code to say, well, if I'm using this language, then I need to pass my arguments to StringsWithFormat or LocalizedStringWithFormat in this order, and then in this other language I need to pass them in a different order, you can pass them all in a stable order in your code and you don't have to write anything language specific.

Instead you can use this one dollar sign and two dollar sign in syntax which Xcode will put in for you automatically to indicate what order the arguments come in.

And here's an example just in English of how you might say the same thing but in a different order.

And some languages will use one order.

Some languages will use another order.

One order might be formal; one order might be informal.

It doesn't really matter.

The point is that Cocoa will take care of the heavy lifting for you.

Now sort of at the next level up, we have NSFormatter.

A lot of what your apps do is present information from objects to users as human-readable text.

And that's exactly what formatters are for.

Formatters actually translate between objects and human-readable text.

Not just from one to the other.

And they can be used both for presenting and interpreting text.

So if you're say getting numeric input from a user, you'll probably want to use the formatter just as much as you'll want to use a formatter to present numeric output.

And formatters use the user's current locale, their current language and region and other settings by default.

You don't have to do any special setup or configuration to tell a formatter, hey, use the user's specific language and region.

So some very simple formatter code.

Dates are actually extremely difficult to format properly for every language and region in the world.

And that's why we built support to them in our systems frameworks so that everybody doesn't have to do that work.

Here we're configuring the formatter to present a medium length date string.

Now that's a date string that might not have the day of the week but might present say the month and the day and the year only.

And since a NSDate doesn't just carry a day, a year, and a month, but also carries a time, we're telling this formatter that we actually don't care about the time that's in the date.

We just care about the date portion of it.

And of course, once we have a formatter set up, we can just keep reusing it.

So once we have a date, we just need to pass it through that formatter using the stringFromDate method in the case of NSDateFormatter.

Or in the equivalent method for any other formatter and we can the assign it straight to our label.

And of course, in SWF, this all looks very similar.

It's actually a little bit more concise though thanks to SWF's enum syntax.

Now we have formatters for dates as I said, but we also have formatters for individual date components, and for time intervals which are also very hard to get right in all situations.

We also have formatters for numbers.

And for formatting numbers specifically in currency which might have slightly different rules and even for formatting byte counts such as the number of bytes or megabytes uploaded or downloaded.

That way you don't have to figure out how to format that properly in your apps themselves.

And new in iOS 8 and OS X Yosemite we have support for formatters for health-related quantities such as energy in calories and kilocalories.

We have formatters for length and height.

We have formatters for mass.

And this way you can present this information consistently to your user with other apps on the system.

Now underlying all of this is the concept of a bundle.

Your apps are actually built into directories, not single files.

And those directories are called bundles.

And that doesn't just cover your apps.

That goes for your app extensions, your frameworks.

Everything else in the system too.

And NSBundle provides an API that's the standard way to access resources within those bundles.

And when you use NSBundle's APIs, they automatically get the most appropriate resource for your current language.

And what's more, building bundles is directly supported by Xcode.

So when you have localized resources in your application, you don't need to write scripts to place them in the right place in your app.

Just tell Xcode that they're localized and what language they're for and XCode will automatically include them in the right place.

The NSBundle API is very simple.

Here we see getting a resource that represents a game menu from our main bundle which represents our app.

And then we're just creating an image with it and assigning it to UIImage View.

Now most of the time, these are very common operations so we have some shorthand in our frameworks for them.

For example, UIImage has the image name method and that will just automatically go to NSBundle for your main bundle and ask for that image, and it will even handle differences in things like the path extension for you.

But not everything is going to come from one of those methods.

For example, if I want to play a welcome message when my application starts up, I'm probably going to want that audio message to be localized as well.

But AVAudioPlayer doesn't simply have a player with file method.

That's okay.

We can just the URL from our main bundle to that message and then pass it along to AVAudioPlayer because those URLs are just regular URLs.

So there's nothing special about them that requires any form of translation before they can be passed elsewhere.

And as always, we support this in SWF.

And in SWF, when you're following sort of a chain of methods like this, you can actually use the Let Syntax so you don't accidently reassign to one of these variables.

I find that really handy.

Now let's talk about tools.

Since Xcode 5 we've supported a concept called base internationalization in Xcode that helps you internationalize your interface by keeping your strings that are in the interface files separate from the UI components that are in those interface files.

And of course in Xcode, we also make it very easy to localize your other project resources as you need to.

And new in Xcode 6, we have some powerful preview features that let you preview how your application will look in other regions and in other languages, both while it's running and directly within Xcode in any interface builder.

But first let's talk a little bit more about base internationalization.

As I said, it keeps your strings and your user-facing text separate from your interface objects.

So the placeholder text that you put into your IB storyboard or your xib files is only representative of your development language.

What this means is that when you have other languages added to your project, you only need to change the strings.

You don't need to go through any more and adjust every single xib file or every single storyboard for every single language you support.

And for other resources, we just show them exactly the same way that we show localized strings in Xcode.

So as an example, I have an app here that has a stop sign icon and the yield sign icon.

Well those have text on them so I'm probably going to want a different stop sign icon and a different yield sign icon, at least for the text, possibly even for the shape for English and for Swedish.

And you can do this with any kind of resource: Images, sounds, movies, documentation, and really any kind of resource that you can put in Xcode and in your project.

And Xcode knows about the structure of bundles so you don't have to tell it, hey, I want these over here; no, I want those over there.

Xcode just knows and will automatically put everything in the right place in your app.

Now new in Xcode 6 we support preview at run time.

This debug time preview is accessed through the edit scheme sheet where you can set your application language from a pop up.

And this pop up will let you pick the system language; it will let you pick any language into which your application has been localized; and it will let you pick a couple of pseudo-languages that I'll explain a little bit later.

But that's not all.

We've also added the ability to IB's preview assistant to select a language in which you want to preview your NIB.

So you can preview your interface by just selecting any language in which your application has been translated from the language pop up on the lower right corner of the preview window.

Now we also support a pseudo-language directly within IB for checking your auto layout constraints.

And I'll show you that a little bit later too.

And here it is zoomed in a little bit so you can read it.

Now the stages of internationalization are simply to identify the strings that you're going to use for your translations, to make sure that your application is properly using base internationalization and Auto Layout; and then to localize any other project resources that you need.

And I'd like to show that to you now in Xcode 6.

And so here I have an application that when I run it will show some earthquakes data from the U.S. Geological Survey's web data feed.

Now I'm not actually using the live feed so don't be alarmed if you see anything in there.

On the other hand, we won't see what might be going on, this is California after all.

And you can see that in this app, I have a nice big title.

I have a label indicating where a particular earthquake happened.

I have a graphic indicating the well, indicating the strength or the severity of that earthquake; the date and time of that earthquake, and its magnitude on the Richter's scale.

And then when I press or when I tap one of these earthquakes, I also get an action sheet allowing me to see that earthquake on the U.S. Geological Survey website and to see its location in Maps.

Now there's a lot of strings, a lot of text in this interface and where does it all come from.

Well, I need to figure that out to make sure that my application is properly internationalize.

So to start with let's take a look at that action sheet.

I happen to know that that's brought up from my tableView's delegate which happens to be my mainView Controller too.

And I just have a straightforward method called presentActionsForearthquake that shows those actions.

And here you can see that to present that alert sheet I'm using the new iOS 8 class, UIAlertController or I'm adding alert actions.

But here I'm just passing the title the string directly to my UIAlertAction.

That means that even if I'm running this application in Swedish or Hindi I'm still going to see this English text.

So I'm just going to wrap this in NSLocalizedString like I showed you.

And I'm also going to put in a comment indicating what this string is for.

And that's all I have to do to insure that this text will come from my localized strings when running in other languages.

Now I also have the same problem with my mapsAction title and even with my Cancel title.

I could adjust those here but in the interest of time let's just pretend that I did.

Now there's of course a bunch more text here.

I showed you the name of the earthquake or rather the location of the earthquake and the dates and the magnitude of that earthquake.

Now those are going to be in my tableView cell because this is just a standard tableView application.

And when I select my cell I have a handy configureWithEarthquake method that hooks up my interface for me.

Now if I zoom in here a little bit you can see that I'm just passing the location of that earthquake straight over to my location label.

And that's just text that comes from the USGS data feed.

However I'm not passing the date directly to that date label.

Instead I'm passing it through a dateFormatter that I've configured as a property.

And the same is true with my magnitude.

I'm not passing it directly.

I'm not using string of format.

Instead I'm passing it through a numberFormatter.

I'm just going to Command click that property so that it shows in Xcode.

I'll scroll down just a little bit here so this is more visible.

And you can see that this property is just a lazily initialized numberFormatter where I'm saying that I want that magnitude to be presented in a decimal format.

And because earthquakes use a logarithmic scale, we tend to always want to present a fractional portion.

So I just tell my formatter I always want to show the decimal separator.

And that I want to show at least one fractional digit and actually so as to avoid overflowing my label, I only want to show one fractional digit.

And for that date I'm just telling my dateFormatter that I want to use a medium style and for the time I want to use a short style.

For the date I don't really care if the earthquake happened on a Saturday or a Wednesday.

I just care about the actual day of the month and the month and the year.

And for the time I don't really care about the milliseconds at least in this app, I just want to know the to the minute time of that earthquake.

So we've done a lot of work to actually make sure that we're getting everything from localized resources and that we're passing our data through formatters.

Now what does that actually get us?

Well, we haven't translated the app yet but if I just stop it and go to my Edit Scheme Sheet I can look under the Options and I have my Application Language pop up and my Application Region pop up.

Now this is big menu so I am going to zoom back out.

And I'm going to choose Sweden so I can see how our manager would see this application when he's back home in Sweden.

I'm just going to close the sheet and rerun my application.

And you can see that even though the text is in English still, a number of things about the application have changed.

For example, the date and time are presented differently.

In Sweden evidently the day of the month comes before the month whereas in the United States we tend to write the month and then the day.

And in Sweden they use a 24 hour clock instead of a 12 hour clock like in the United States.

And finally one of the most noticeable differences, in Sweden the comma is the decimal separator rather than the period.

And the System Frameworks handled that for me just by using a formatter.

Now let's take a look at our interface in Interface Builder.

I'm just going to select that earthquake scene that I'm looking at in the simulator.

And I'm going to bring up the Preview Assistant that we introduced in Xcode 5.

And here I can select the Preview, and in addition to allowing me to preview the application in different orientations down in the lower right I can also select a language in which to preview the application.

So here if I move over a little bit you can see that I only have my Development Language actually in this project.

However under the Separator I also have one of those pseudo-languages I mentioned.

A Double Length Pseudo-Language.

So if I select this and zoom out, what do I see?

Well, all of my labels have had their text doubled.

And this is to simulate running your application in a more verbose language, a language that uses more text to say something.

For example, German tends to use about 30 percent more space than English does to say about the same thing.

On the other hand, there are some languages that use even less space than English to say the same thing.

And your application's interface needs to accommodate them all.

Fortunately Auto Layout will do most of the work for you.

And this Preview Assistant lets you see that it's actually working.

For example, I actually do have an Auto Layout issue in this preview.

You can see that the Magnitude field has an ellipsis in it indicating that its text is overflowing the label.

Now the largest earthquake that's ever been recorded was a 9.5 so this probably isn't an issue.

At least for this app.

However, it is something that you'll probably want to take into consideration.

Of course not all of the text in this preview is doubled.

That's because this heading that says Prototype Cells and this background that says Table View Prototype Content, those are actually part of Interface Builder's user interface, and not your own text.

Now if I wanted to localize this graphic because it uses color and color is another language and region dependent piece of information that would also be very easy in Xcode.

I would just select the image I want to localize and then open the Inspector and go to the Inspector's Localization section.

And we have this nice big Localized button that will turn any resource into a localized resource.

And that button will just bring up a pop up alert that asks where I want what localization I want that resource to be a part of initially?

Now it can't actually be a part of the base internationalization because it doesn't have a place as an image into which I can substitute strings.

However, I can make it a part of my English localization.

And let's talk about what we just saw.

We saw how the system frameworks on iOS and OS X will really do a lot of the heavy lifting for you in internationalization and localization.

We saw how you can use the debug-time preview to check your region support because after all even if you don't localize your application, your users are still going to be in different regions all over the world and use different conventions.

And we saw how you can take advantage of design-time previews and base internationalizations to ensure that your application is properly reactive when it comes to the length of text in the interface.

And of course you can localize other project resources too as you need to.

Now to talk about the actual process of localizing and translating your application I'm going to hand things back to Zoltan.

[ Applause ]

Thanks, Chris.

So you saw how Chris prepared an internationalized application.

He structured it to be language and region independent.

And you saw how the frameworks provided some of the localized data for users around the world for free.

So what's left to do?

Well, it turns out that the bulk of the remaining work is to translate your User Facing Strings.

And this is still best done by an expert in the language and not just machine translation.

So your challenge is to extract those strings from your project.

Then patch them up from your translator for your translator.

After they've translated them you have to integrate them back into your projects.

Sounds like a lot of work.

Well, I'm pleased to announce in Xcode 6 we've streamlined the process.

Xcode will now carefully examine your IB resources and your source files and extract the source strings for you.

It will generate an open XLIFF format.

Now this is that mature standard that's accepted by a number of translation companies around the world and it's compatible with a number of translation tools.

So when translators insert their work into the XLIFF they then return the XLIFF file to you and then you simply import it into your project and then Xcode will meticulously create the resources or update existing resources to put the translations where they need to go.

So you no longer have to run genstrings or IB tool in order to do this.

[ Applause ]

Thank you.

I'd like to show you that now.

So here's the project that Chris created earlier.

I'm just going to navigate to the project where you find a lot of the localization related details.

So here you can see that at the moment it's only got English in this project.

So if I want to extract the source strings from this project, I simply go up to the Editor menu and choose Export For Localization.

I choose a destination in the Home folder.

And then Xcode generates that XLIFF file based on what it's found in the projects and I'll just show you that quickly.

It's simply an XML file and down here you can see the source strings that Chris localized or internationalized earlier and the comments that he added to the Translator.

So now imagine that I send that to my translation team.

I would e-mail it or upload it to their service.

And after they've completed their work, when I want to import these translations, I go back to the Editor menu and choose Import Localizations.

Here's some prepared earlier.

Now I've heard that smorgasbord is grace in Sweden but personally I prefer curry in India.

So we've got some Hindi translations.

And Xcode is inserting those translations into the project.

Now you can see that Hindi is added to the project and that when I disclose the storyboard variant group, there's string files waiting for us.

And if choose to run this application I'll use the new debug-time menu.

I'll choose Hindi.

I'll choose to preview it as if a user in India.

And now you can see that beautiful script in place.

The dates have changed.

The region the numberFormatter is the same as in the U.S..

And this is one of the things I like about the Preview feature, it's immediately obvious that this is not in the right script, the title of this event.

So in this case you would ideally use a localized feed.

I also like to point out this is available on the Command line.

So you can now use the exportLocalization and importLocalization verbs to build this into your localization workflows and to automate this process as you want.

[ Applause ]

So you've seen how Xcode 6 will now streamline this process of localizing your application.

How it will help you to communicate with your translator in this open XLIFF format.

So now that you've got a localized application, you'd like to keep it that way.

And you'd like to build confidence that it is going to appeal to a global audience.

And so I'd like to invite Chris back on the stage to show you to best maintain this application and build confidence that it's ready for a global audience.

Thank you, Zoltan.

That's pretty cool stuff.

So of course you probably don't want to put off localizing your application until the very end of your development process.

That sounds a lot like waterfall development and we really want to be iterative these days.

And the expert and import workflow that Zoltan showed you allows you to do that.

It allows you to do what I like to think of as continuous localization because you can export and import at will.

This lets you stay localized as you work and you can use previews to catch missing or bogus translations very easily.

And we even include some tools that let you prepare your application for right to left languages like Arabic and Hebrew independently of translating your application into those languages.

That can make it much easier to actually experiment with your user interface even if you don't speak or read Arabic or Hebrew.

So I'd like just like to show you that right in Xcode.

So let's say I've been working on my project for a while.

And I've just received my Spanish translation back from my translator.

To import it all I have to do is as Zoltan showed select My Project and then choose Import Localizations and I'm going to pull in my Spanish XLIFF file.

Now what happened here?

Unlike what Zoltan showed you with Hindi there appears to be an issue with this translation.

Well, it turns out that it's missing the Show Location and Maps functionality.

I must have actually exporting this for localization before I added that feature to my app.

But that's okay.

I can still import this incomplete localization and at least see my application as it is right now with all of the localization that has been completed for Spanish.

So I'm just going to import that.

And if I just close my storyboard here, I can see that now I have strings in both Hindi and Spanish for it.

Now if I want to see how my application actually runs, as always, I can just choose my Application Language, in this case Spanish now that I've added that localization.

And I'll also choose the Spain region so I can see how somebody who speaks Spanish in Spain would see my application.

Now if I run this, I see that earthquakes here isn't localized.

That must have been another thing that my translator missed.

And if I tap an earthquake, I can see that one of my actions and the Cancel button are translated but that button that I called out earlier has not been.

Now if I wanted to see this right in Interface Builder, I could select my main storyboard and let's just select that earthquake scene and bring up the Preview window again.

The Preview Assistant I should say.

And if I choose my Spanish localization in Interface Builder, I can see that some of my placeholder labels have been translated.

However, this earthquake's title is not only all in English but it's actually been changed to all capital letters.

And that's actually the frameworks calling out to me that they recognize that this should be coming from a localized resource but it wasn't found.

And I can get the same effect in the Edit Scheme Sheet when running my application by choosing this new Show non-localized strings option.

So if just stop and rerun my application now that I've seen set that, I can see that earthquakes is called out in all capital letters.

[ Applause ]

Now I said you can export and import at will.

And one reason that you'll want to export is because you'll be adding features to your application as you go.

And you'll want those new features that you add to be translated.

And you export in exactly the same way no matter when you do it.

You simply select your project and choose Export For Localization but I've already added some localizations to this app so what happens?

Well, Xcode gives me a choice of whether I just want to export strings in the Development Language or if when generating the XLIFF files, it should already include the strings in the translations that it has.

This way I can hand those XLIFF files to my translators and they can see the work that they've already done and won't redo it.

Instead they'll only do the work needed for the new and changed strings.

So if I save this and replace the one that was already there, I can see now in the Home folder that I have three exported localizations in XLIFF format.

One in English, one in Spanish, and one in Hindi.

Now finally I'd like to show you what your app would look like in a right to left language.

For convenience we're just going to bring up the storyboard again so we have something to compare against.

Stop my app from running in the simulator and I'm going to Edit My Scheme once more.

This time I'm just going to set my region back to the System Region but I'm going to set my language to the new Right to Left Pseudo-Language.

Now when you're using a right to left language on an iPhone or an iPad or a Mac, it's not just the text that goes from right to left.

The entire direction of the user interface is changed based on how the user prefers to read.

I'll just also turn off my Show non-localized strings setting here.

And let's see how Auto Layout reacts to just changing the user interface direction.

Well, because we use the concept of leading edge and trailing edge rather than left and right in our Auto Layout for this app, most of the app just flipped right to left, just as an Arabic or Hebrew user would see it.

We have our magnitude all the way on the left instead of all the way on the right.

Next to it is our graphic indicating the severity of an earthquake and then we have some text overlapping it.

That indicates that we probably still have a little bit of work to do on making this fully support right to left text.

Now the frameworks do that but I probably just have a bug in my Auto Layout constraints.

And this preview helped me find it before I actually went to the trouble of translating my application and I don't have to understand Hebrew or Arabic in order to debug that problem.

So what we saw was how you can update your translations as you go to keep your app current and really to do iterative development at the same time that your app is being translated.

We also saw how you can use the various kinds of preview to catch missing translations before your users and possibly even before your QA ever sees them.

And we saw how you can simulate many different behaviors between regional formatting and even right to left text to ensure your apps work the best they can for everyone.

Because that's really what it's all about.

Reaching a global audience.

Our frameworks for iOS and OS X make that really easy.

Especially if you take advantage of Xcode's new workflows for XLIFF, export and import, and for previewing localizations at debug time and at design time.

There's some related sessions.

There was actually one earlier today called Advanced Topics in Internationalization that goes in deep into what you need to do in terms of formatting and code to deal with some of the subtleties of internationalization.

For example, mixing right to left and left to right text.

And that's actually the sequel to a session that was held last year that I'd also encourage you to check out in the WWDC app.

And as always the Advanced Topics Internationalization is going to be available for streaming soon.

There's also an Apps for China Get Together tomorrow at 4:30 for anyone who's interested in tapping this huge new market.

Thank you very much.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US