Localizing with Xcode 9

Session 401 WWDC 2017

Build world-ready apps using Xcode by following some simple steps to manage strings and assets, and display your user interface. Take your localized apps even further with new technologies and features in Xcode 9. Learn how Xcode 9 helps you design, localize, and test your app in multiple languages.

[ Background Noise ]

[ Applause ]

Good morning, everyone.

Sorry about the technical difficulties, but we're ready.

[laughing] All right, so I'm Sarah Roddy and welcome to Localizing with Xcode 9 session.

And today, with my colleagues Ia and Chris, who would like to talk to you about some exciting new improvements we made to the localization process in Xcode 9.

So let's get started.

Around the world, you have so many people who are looking to access and use your great applications, but they can't do that if you don't give them the ability to use your apps in their native language.

And your app can easily access this global audience by following some simple steps and best practices.

And, in today's session, we will talk about how Xcode makes it so easy for you to add new languages to make your app look and feel local, which will allow you to go global.

So there are three main topics we will be covering today.

First, we will start with how to make your code world ready, then we will walk you through the localization process in Xcode and introduce new features we added in Xcode 9.

Finally, and through the presentation, we will talk about testing and give you some best practices to make sure that your app works and looks great in all languages that you support.

So let's start with internationalization.

So internationalization is the process of designing your software so it can be adapted to different languages and regions without making code changes every single time you are adding a new language to your app.

So the point here is it shouldn't matter which language your app is running in.

If the language has short text or long text or tall text or even text or layout that flows from right to left, your app should be able to adapt dynamically to all these scenarios.

And the first step in internationalization is managing your strings.

Your app will be localized by translators who would be working with your strings and in this localized string is there to make it easier for you to create localizable content for your translators as well as displaying the right text at the right time.

So if your strings are coming from a storyboard or a zip file they're localizable by default, so we don't need to worry about those.

But sometimes you might have some strings that are defined in your source code that will be displayed to the user, like error messages or notifications; if you would like to notify your users about something.

And you want to make sure that this translator is also localizable.

And to do that you just need to wrap those strings with NS Localized String.

In addition to that you can use NS Localized String as a format string with localized language format to get a localized and a formatted string.

So here let's go through a quick example together.

So here they have a label.

When I set its text to a string called population.

So again, my string is hardcoded and is not localizable.

To make it localizable, you just need to wrap it with NS Localized String.

And as you can see here on the screen, NS Localized String takes two arguments, your string and a comment.

Comments here are really, really important to provide to your translators so they have context about the string they are translating.

And I would say most of the time you just need to use NS Localized String, but in some cases when you're working on a framework or a shared component you might want to use NS Localized String from table where you specify the table name where your strings are coming from.

And as I mentioned before, you can also combine NS Localized String with Localized String with Format, where NS Localized String is a format string.

And in this case it's really, really critical that you provide those comments for your translators so they have context about the string they are translating.

Then at run time NS Localized String will determine the preferred language of the user and find the corresponding localizable string files.

As you can see here, this is an example of a localizable string files coming from the French localization projects and it contains all your localizable strings, the one that you wrapped with NS Localized String as well as the comments that you provided.

And this is the information that will be provided to your translator.

And if you're using Objective Senior App you can run the static analyzer to find localizability problems in your code, like if you forgot to make a string localizable or if you forgot to add a comment to a localizable string you can run the static analyzer and you get warnings that will show you what these problems are and you can fix them right on your code.

If you'd like to learn more about the static analyzer please check out this talk from last year.

Another thing that your app should handle well when it comes to localization is working with different visual representations of date, time, numbers, et cetera.

For example, 12-hour time format is a standard format we use here in the US, but you want to make sure that a user who's using your app in France, for example, is getting 24-hour time formatting set because this is the default format used there.

And we provide a variety of very powerful formatters that will let you handle the complexity of data formatting and regions, but just to give you an example on how formatters work, let's take a look at a date formatter example.

So here let's say you want to display a full date format in your app.

So instead of setting a date format string that is specific to a region or a country that has a specific order, we recommend that you use date style.

This formatter has different styles that you can choose from, but since here we want to display a full date format I'm setting my date style to full.

So with that you are making sure that your date is formatted correctly in all regions in the world.

As you can see on the screen, there are differences between the date format in the US and the date format in France, like the French date does not have any commas, the month name is not capitalized, the day of the month comes before the month itself, and this would not be achievable if you have pasted in a date format instead of a date style.

In addition to date formatter, we have a series of formatters for common data types available in foundation.

If you'd like to learn more about formatters please check out these two great talks from last year.

Finally, you want to make sure that your user interface is flexible and looks nice and beautiful in all languages that you support and to do that the process is really simple.

We just need to use base internationalization and auto layout.

So let's talk about base internationalization for a moment.

So by enabling base internationalization in your project, Xcode modifies your projects for Destructor and separates your UI from your strings, which means any file that is related to your user interface, like a zip file or a storyboard file, will be stored in base [inaudible].

While your strings, either if they are coming from a storyboard file or if they are in your code that you wrapped with NS Localized String, they will be stored in the specific language folders so that way you only have one set of user interface instead of duplicating your UI every single time you are creating a new language in your app.

And base internationalization is enabled by default since Xcode 5.

So if you have an older project that you want to localize, please make sure that you enable it there.

And you might be already familiar with auto layout or you're already using it to support different presentations of your app or different screen sizes, but auto layout is also a critical technology to use for localization.

So by using auto layout you are making sure that your app is flexible and it can adapt to different string lengths and different configurations of your app.

And new in Xcode 9 we are introducing new interface builder localization warnings to validate your constraints for localization, which means when you are working on your UI and your storyboard file or your zip file and you're setting up these constraints and they are not localization friendly you will get this warning as well as suggestions on how to fix them.

And new interface builder warnings will be available in the future seat.

Additionally we are introducing an Xcode 9 used to the localization options for you to simulate a localized environment.

So instead of having a real strings, translated strings, to see how they look like in your app you can just use one of these pseudo localization options that we are providing.

So, in Xcode 9, we have new options for accented Latin strings for a fixed strings and new options for right to left languages.

And with that I would like to call Ia on stage to give you a demo about how to prepare your app for localization.

Thank you.

[ Applause ]

Thank you, Sarah.

Hi. I'm Ia and today I want to show you this really cool app I've been working on called International Effects.

As you see on the screen, my UI consists mostly of this table view and a user can go and select a country or territory from this list and they'll see some information about it like the flag, the population, and even the language population.

So I made sure my app looks beautiful in English and I'm happy with the way it looks, but now I want to internationalize it so it can be ready for my first localization.

And to do that I'm going to follow the steps that Sarah just showed us in her slides.

So the first thing I want to do is my strings management.

I'm loading all of these strings in my table cells in code.

So I'm going to open my Xcode project.

And in my table view controller, in my cell for wrote indexpath function I want to make sure I don't have any hardcoded strings that I'm loading into my UI.

And instead I want to replace them with a call to NS Localized String.

And I'll pass in my English string and I'll also give a helpful comment.

So; "Label preceding the selected territory."

And then I also went ahead and made sure I'm calling NS Localized String for all the other text I'm loading in code.

So the next thing I want to do is I also want to make sure I'm displaying and representing data correctly for different locales.

So in this case my data are mostly these numbers that you're seeing on the right side.

And numbers are displayed differently in different countries.

So to format them correctly I'm going to use a number formatter.

Number formatter is similar to date formatter that Sarah showed us earlier, except, of course, it's used to format numbers instead of dates.

So for population I can simply set my formatter's number style to decimal and I set "uses grouping separator" to "true," which makes it easier to read by separating the number by the thousands, and that's because population tends to be a large number.

For GDP I can also use a number formatter, except this time I would set the number style to currency and the currency code to US dollars because our data is in US dollars.

And finally, for the literacy percentage cell, as well as my language population, I set the number style to percent and I give it a maximum and minimum fraction digits to make sure it looks consistent.

So now that my strings are localizable and I'm displaying my numbers and I'm formatting my numbers correctly, the next thing I want to do is make sure my user interface is also ready.

And to do that I'm going to open my main storyboard and I want to show you how you can use pseudo languages in Interface Builder Preview to test your app for localization.

So first I'll click on the top right corner on the Assistant Editor icon.

And then under this dropdown I want to choose "preview" and basically it's showing me a preview of my storyboard.

And on the bottom, right corner I get a language chooser and if I had any localizations on my project I would see those here, but in this case it's showing me all the pseudo language options I can use in the preview.

So new in Xcode 9 we've introduced a fixed pseudo language.

So I'm going to select that to see what it does.

As you can see it simply added those square brackets and the pound symbol before and after each string.

And that makes it really easy to spot if something is clipping or not fitting in my UI because those symbols would be missing.

So in this case it looks like my button on the bottom doesn't have the pound symbol and that's the first thing I saw, but if you take a closer look, even the y is clipping in "play."

So I'm going to go back to my storyboard and see why that is.

And it looks like I have a fixed width constraint, which in this case is not necessary and generally fixed width constraints on text controls are a bad idea for localization.

So if I get rid of it you'll see that my pseudo language looks exactly as expected and now I know that I'm not going to be clipping anything.

So these interface builder previews are even more helpful if you have more of your UI defined in your storyboard.

In my case, a lot of my UI is loaded in my table view controller.

So I also want to show how I can use pseudo languages at runtime to test that.

So if I select if I click on my scheme and select "edit scheme" and make sure you have "run" selected in the sidebar under "application language" you get a list of languages you can choose from, but over at the bottom you have a list of all the available pseudo languages you can use as well at runtime.

Another pseudo language we've added in Xcode 9 is the accented Latin pseudo language.

And if I go ahead and play that My app will launch with accents above and below each of my localizable strings.

And what this really helps with is it gives me a preview of what my app how my app would handle languages that use a lot of accents, diacritics, or even languages where the scripted self is generally taller than English like Thai for example.

So I can quickly scan this and make sure that everything's fitting vertically and I'm not seeing any clippings.

And everything looks good here.

And then finally one more pseudo language I'd like to show you at runtime is the new right to left pseudo language with right to left strings.

And this pseudo language is very helpful in testing whether your app is ready for right to left languages like Arabic and Hebrew where the text itself is written from right to left.

So as you can see, my UI controls are already adjusted and mirrored to look the way they would look in a right to left localization.

And what we also do is we display the localizable strings from right to left.

So the letters themselves have flipped order and that really helps give you an idea or put you in the mindset of somebody who reads their text from right to left.

So these are some of the pseudo languages.

I highly encourage you to try them all.

They're very helpful to test your app before you even add localization.

And to summarize the internationalization process; first make sure you're using standard APIs to load localizable strings as well as to format your data, like dates and numbers.

And make sure your app is using base internationalization to separate your UI from your localizable content.

And, of course, use auto layout in your UI to make sure that the app itself is flexible and can adapt to different translations.

And, finally, we showed a lot of tools that can validate your internationalization, like the new pseudo languages we've added and the auto layout warnings that are coming soon in the future seat.

So my app is internationalized now and I'm ready to add my first localization.

So I'm going to call Sarah back up on stage to show us how to do that since she's the expert on the topic.

[ Applause ]

Thank you, Ia.

So now that you're done with the steps that Ia showed us earlier in her demo, you are ready to add new languages to your app.

And you can do that right from Xcode from your project info and you have over 100 languages to choose from.

So now that you've added these languages that you want to support in your app, so to generate the localizable content for your translators Xcode will find all the localizable resources that you have in your project, then when you export your projects for localization Xcode will extract the strings from this localizable resources and generate XLIFF files for every single language that you have added.

And XLIFF file is simply a standard XML localization file format that is standard in the localization industry.

So if you send your XLIFF files to one of the localizers they will be already familiar with the formats and how to work with it.

So once your XLIFF files are translated you can import them back and Xcode will integrate automatically your localized text back into your project.

And that's the localization process in Xcode.

It's very simple and straightforward.

And new in Xcode 9 we are adding support for exporting and importing string dics file formats.

So string dics is the powerful tool that lets you handle plurals and adaptive strings in your app.

In Xcode 9 you can now add and create string dictionaries right from the file template list in Xcode and that will generate an editor for you where you can add and edit your keys and values.

So now let's talk more in detail about what can you do with strings dictionaries and why they are helpful.

So first strings dictionaries can help you handle the complexity of plural forms without writing any code in your app.

So let's assume I have this logic in my code to handle plural variants.

So here I have an array.

If the count of my array is one I set my string to one popular language.

Else if the count of my array is more than one I set my string here to something like person d popular languages where person d is a format string that will replace the runtime with the count of my array.

So this logic might work well it would work for a language like English where there are only two plural forms; one singular and one plural.

But this would be a complete disaster for a language like Russian where there are more plural variants.

So Russian has four different plural variants and your translators need to think about work arounds and ways on how to translate all these variants with the one generic plural that you provided the one you are using for English, which will make basically your translations sound robotic and not very natural for a native speaker.

So this example here sounds like something; "popular languages column one, popular languages column two," and you don't want that in your app.

So to solve this kind of problem, when we're done writing any code you can just use strings dictionaries.

So all you need to do in your strings dics file so you need to add your localized format key.

In this example my localized format key is "languages."

Then you need to add the plural variants that are relevant for your development language.

So since here my development language is English, I only care about two plural variants, one and other.

So one is for the singular case and other is for the plural case.

Then by wrapping your plural string with NS Localized String, as you will do with any other string that you have in your code, when you export your projects for localization, Xcode will generate automatically for you the plural keys for other languages that you support.

So the variance for other languages will be included in your XK files and your translators when they are translating the strings they will find the plural variants that are relevant for the language.

And now our translation will be correct and your users will have more grammatically correct translations.

So this is Strings Dics support for Plural Variants in Xcode 9.

Another thing you can do with strings dictionaries in Xcode 9 is support for adaptive strings.

So if we take this example from Ia's app earlier, she had a string here called "Gross Domestic Products in Billions," which is kind of long, but it fits fine on an iPad because there is enough space for it.

If we run the app again in a smaller device like an iPhone 7 so this string does not fit anymore.

As you can see on the screen, the number on the right side now shrinks and you can only read the three first characters, which is kind of wrong because we are given the wrong information to the user.

So you can take advantage of strings dictionaries again to solve these kinds of problems and provide different string variants for your app.

So here I have three different keys; 20, 25, and 50.

They correspond to different screen sizes.

And then I have the strings that I want to show for every screen size.

So if you call NS Localized Strings on the key within your strings dics file.

So you enable we pick the right value for you at runtime based on the space available on the screen.

So if you are not using UI label and you are using a custom control of your own, you can still take advantage of the functionality by calling variance fit in presentation with API, where you pass in one of the keys that you have defined in your strings dics file and then at runtime this will choose the right value for you that corresponds to that key.

And we will be providing the keys and the corresponding screen sizes in the documentation if you would like to check out that later.

So now if we run again the app on the iPad, still using the full string, and if we run it again in a smaller device like an iPhone 7 now we pick the right variant that will fit nicely on the screen size.

And thus without erasing a single line of code your app looks nice and beautiful in all different screen sizes and configurations of your app.

And adaptive strings are really helpful if you are supporting localizations in your app because you might have some languages that are longer or shorter than others and you want to give this flexibility to your translators to provide like adaptive strings for the language.

So now that you're done with supporting plural support as well as adaptive strings you are ready to localize your app.

And the first step in localization is exporting your project.

So when you export your projects for localization so Xcode will ask you which languages you want to localize first.

So once you pick the language you want Xcode will generate XLIFF files for you to send to your translators.

So once your XLIFF files are translated you can import them back in your project and Xcode will show you this nice diff with what was changed by your translators.

And after that Xcode will just integrate your localized text back into your project and your strings will be localized.

So now that your strings are localized, what about other resources that are not string based that you want probably you want to localize, like images or audio files?

So you might have something like this, an image that has text on it, and you want to provide an alternative image for other languages.

So you can localize other resources in Xcode easily by clicking the "localize" button in the "file inspector."

Again Xcode will ask you which language you want to localize this asset for.

You can pick from the list of your languages and Xcode will move this asset to the specific language folder where you can replace it with the image or the asset that you want to show for that language.

So this is the localization workflow in Xcode 9.

So we saw how to support plurals as well as adaptive strings and how to localize other resources.

And with that I would like to call Chris on stage to give you a demo.

Thank you.

[ Applause ]

Thank you, Sarah.

Yeah.

[ Applause ]

So earlier we saw Ia validate that her app as internationalized using both our framework and tools features.

So let's get started on actually localizing it.

First though, I really appreciated Sarah's discussion of plurals and adaptive width strings.

So I've added a strings dict file to our project here.

Oops. [laughs] And I've added that GDP adaptive width string with all of the different variants for our different device widths; 20, 25, and 50.

And I've also created a plural variant, just like Sarah showed, for the number of popular languages.

Now though, I'm ready to localize my application into Russian.

So what I'm going to do is go to the project editor and right under my localizations, where it tells me that English is my development language, I have this plus button.

I can choose that and just go about halfway down and choose Russian.

And Xcode will ask me which of my resources I want to take and make Russian placeholders for.

Since I want to localize everything I'm just going to tell Xcode to finish.

And you can see that in addition to my main storyboard file I now have a placeholder for my Russian strings.

Still in the project editor, I'm going to go to the "editor" menu now and I'm going to export my project for localization.

And I'm just going to save this file actually this folder on the desktop and Xcode is going to read through all of my source code, all of my resources, and it's going to generate that XLIFF file.

So let's take a look at what it looks like.

Here you can see that the file is named "ru.xliff" where ru is the ISO language code for the Russian language.

And if I double click this we have a little tool that we've written so that we can see all of this localization data in sort of a tabular form so that we don't have to wade through a whole bunch of XML angle brackets just to show you.

And up at the top here you can see a bunch of strings that came directly from our storyboard file.

And you can see in our translation column everything is in red because we haven't actually put any translations in this XLIFF file yet.

Here in the next section we have file we have elements that will go into our localized strings our localizable.stringsdict file.

And you can see that there are more variants of the percent d popular languages string than we had in our original localizable.stringsdict.

And this is because Xcode knows that we're exporting to Russian and creates placeholders for all of the Russian variants for you automatically.

We also have our adaptive width string variants for the gross domestic product and we have a couple other things like our application name and our info.plist and the strings from our source code that will then show up in a Russian localizable.strings file upon import.

Now let's say I have a translator who worked really fast and I have a translated version of this XLIFF ready to go.

I could just go to the project editor in Xcode and choose "import localizations" and just choose that file and export Xcode will read your project, read the file, and then show you how what that file is importing differs from your project.

Here I'm just getting a warning that I didn't actually localize the name of my app in English yet, but I do have a localization now in Russian.

And I can also see in the files view here exactly what files will be affected by this import.

And this is all what I expect.

Now I tell Xcode to import it and it'll write out these new resource files for me.

And you can see that where we didn't have a localizable.strings file before, now we have one.

And if I choose it we have a localizable.strings file with a whole bunch of Russian text.

Now if I run our application it looks like I've hit a little bug here, that's okay.

Oops. I'll just quit and relaunch Xcode.

Now if I run our application we're building and Xcode is integrating all of our localizable strings that we just imported.

There is one step that I missed though, sorry about that.

I want to run my application in Russian so I can see those translations.

So I'm just going to open the scheme editor and I'm going to switch from that right to left pseudo language that I was using to the Russian language.

And now if I run our application it'll build the storyboard, copy in the swift standard libraries, and launch.

And we can see our application is running in Russian.

And not only are all the formatters and everything correct for all of our dates and our numbers, but we also have all of that Russian text that we just imported.

So, to summarize, strings dicts are really powerful and you can use them for pluralization and for adaptive width strings in your applications to ensure that they look right not only based on the user's language, but also based on their device width.

It's easy to export an XLIFF from your Xcode application for your localization process and to import your translated strings via XLIFF back into your application's Xcode project.

And Sarah showed us how you can also localize non-string resources in Xcode.

And now Ia is going to show us how you can test your localization.

[ Applause ]

Thanks, Chris.

I can't believe that my app is already localized into its first language and the steps were really simple and easy to follow.

So now that I do have my localization, let's talk about some things that we could do to test it.

New in Xcode 9 we've introduced the ability to specify the application language and region that you would like to run your tests in.

And you can do this in the test scheme editor right in Xcode.

And today I want to talk about UI testing with XC test.

So UI testing is a great way for you to validate both your application's UI as well as its functionality.

Let's take this line of code on the screen for example.

This is what you would write to simulate tapping on a table cell that has the text territory in it.

Now that's not going to work for localization because that text is going to be translated into each language that you run your test in.

And additionally, if you decide to go and change the wording in your app, even in your development language like let's say I want to call it "region" now instead of "territory," you don't really want to go back and update every single test that you've written just so that it can start succeeding again.

So instead, what we recommend you do is use accessibility identifiers.

These identifiers are unique to every element on your screen and you can use them to find these elements without really knowing what strings are loaded into them.

So to set the accessibility identifier, you can simply set the property on your UI control when create it, or your NS control.

Or if you're loading your UI from a storyboard or a zip file you can set it in the identity inspector in interface builder.

So now that your tests are localization friendly and they're succeeding in all the languages that you support, there's even more that you can do to take advantage of that.

We're introducing this year we're introducing XCT attachment APIs, which allow you to attach screen shots as well as any other data you collect during your test and it'll attach it to your test report at the end.

What this empowers you to do is get a full coverage of your UI.

So every screen in your app, in every localization that you support, as well as every device that you support.

And you can do all that while writing only one test well, one test for each thing you want to test.

[laughing] To find out more about XCT attachment and what else is new in testing please see those related sessions throughout the week.

And now I want to show you a test that I've written for my international facts app.

Okay. So in my Xcode project I've already created this UI test target and I wrote my first test, called Test Some Territories.

So let's run it to see what it does exactly.

Now Xcode will launch my test runner, which in turn launches my app.

And in this specific test we select a different territory, in this case Belgium, and then the test succeeds.

So now let's try running my test in Russian.

And to do that I'll go into my scheme and instead of "run" this time we want to select "test" and under language I'm going to choose "Russian" so I can test the new localization that Chris added to my project.

Now again my test runner launches, which launches my app.

And this time my app is launched in Russian, but it looks like my test failed.

So let's see what's going on.

And it looks like my test can't find a cell that has "territory" in it, which is what we just talked about.

Silly me. So oops, sorry.

So in my project I'm going to go back to my main table view controller and make sure that I'm setting an accessibility identifier for my territory picker cell so I can actually select it.

So when I create my cell I also want to give it an identifier.

So territory picker cell.

And I want to use the same identifier in my test.

So where it's failing, instead of using static text I'll simply replace it with this identifier that I just created.

And now I'll try to run my test again in Russian using the identifiers.

As you can see, my app launches in Russian again, but this time it actually got further and it chose Belgium as the country and the text succeeded.

So I just want to show you what my test does exactly.

First, I want to validate some of some functionality.

So I have this XCT assert equal to make sure that my app initially launches with the US selected and then I use the XCT attachment APIs to collect screen shots.

So my first screen shot is a screen shot of the US page.

And I add it to my test case and then I collect more screen shots later when I open the territory list and after I've selected Belgium.

And now finally if I want to see these attachments I can simply go to the "reports" navigator in the sidebar and select my most recent test that I've run.

And then if I expand my test some territories test case, you'll see that some of these lines have the attachment icon on them.

And these are the attachments that I've created.

So as I've mentioned, they're all screen shots in this case.

And there's so much that I can do with these attachments.

I can bundle them up and send them to somebody to ensure the quality of my translations, somebody who actually speaks these different localizations and to make sure the UI looks good and the formatters are correct.

And I can even use these screen shots in my app store preview when I want to launch my app in all these different storefronts around the world so that users can get a preview of what your app will look like in their language.

So, I show you how with one test you can test every single localization that you add in your apps.

And to summarize what we talked about today, we covered the localization process for an app.

First, you need to prepare your code for localization, and that's the internationalization steps.

And then we showed you ways you can validate your apps readiness including the static analyzer and auto layout warnings and the new pseudo languages.

And then you can export your all your localizable content into a single XLIFF.

And now that includes string dict files, which are really powerful for handling plurals and adaptive strings.

And finally, when your XLIFF is translated you can import it back into your project and that integrates the translations into all the different pieces of your app.

And then you can take advantage of your existing tests to run them in all your supported localizations.

And you can take advantage of the new XCT attachments to do a lot more than test your app's functionality.

For more information, our session is 401, and you can visit our page on developer.apple.com.

We highly recommend that you attend, or at least watch, these related sessions throughout the week.

Thank you and enjoy the rest of the conference.

[ Applause ]

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