Creating Great Localized Experiences with Xcode 11
Learn how your app can support per-app language settings in iOS 13. Get the details on localizing assets with asset catalogs and simplifying your localization workflow with Xcode 11. Understand how to generate screenshots in multiple languages for localization and testing.
My name is Kulpreet. I'm a Software Engineer in Localization and I'll be joined by my colleagues Vivian and Arthur.
In this session, I'll be going over some exciting new localization features in our platforms.
Vivian and Arthur will then touch on some new tooling in Xcode 11, including some great enhancements to localization workflows, localizing images, and how you can use testing to generate screenshots of your app in any language.
Today, we estimate over 70% of our users are outside the United States.
Localization is an opportunity for you to expand your reach and capture new markets with your app.
At Apple, we care deeply about creating a great experience for all of our users around the world. This year, we have a host of new features and improvements for our Multilingual and International Users.
To name a few, in iOS 13 we have keyboards for 38 new languages.
I'm sure if your language is up there you're really excited.
We also have a new setup flow that guides multilingual users to the right language settings.
Now one of these features I'm particularly excited about, and I want to dive a bit deeper with you.
In iOS 13 there's a new setting that appears from Multilingual Users in your app settings page.
I'm happy to say that users can now set the language of your app independent of the system language.
If a user has a connected watch, the setting will also sync to the accompanying watch application.
And similarly, on macOS, users can set the language of an app via Language and Region preferences.
This is important because in multilingual regions, users may prefer to use different languages in different context.
In a region like Hong Kong, someone might read their news in Chinese even though they do their banking in English.
In India, a student will go to school and learn in English even though they come home and speak Hindi with their family.
Apps are windows into these interactions we have everyday. And this new setting allows users to choose a language that reflects their preference for your app.
Now this works for your app out of the box and for free if you're using all of our standard foundation APIs for localization.
But there are couple things to keep in mind to make this work great for your users.
First, do not attempt to set the application language manually in code.
Our APIs do a lot of work to ensure users get the right language and font fall backs.
If you do need to add the option within your app to switch languages, we recommend you Launch into Settings where users can go in and switch the language of your app.
Next, when the user does change the language of your app, your app will be relaunched in the target language.
Now, to make this language change a seamless experience for the user, we recommend you adapt state restoration APIs.
In iOS 13, there are some new state restoration APIs with NSUserActivity.
To learn more about how you can use them, you can check out this talk on Friday.
And, finally, it's important that all of your content is shown consistently in one language throughout your app.
If you're doing some custom or server-side resource loading, there are handful of APIs that might be useful for you to determine which language to request that content here.
Let's go over a few of them.
Locale APIs are useful for fetching the user's Language and Region preferences.
Locale that preferred languages returns a list of languages in order of user preference.
Now, this list might be useful to you if maybe you're building an app that needs to handle multilingual content, and you need to fetch resources such as font for all of the user's languages. And one thing to know is that locale, that preferred languages will return the language list, that user's preferred languages, regardless of the languages your application bundle supports.
So it's not useful for getting the current running application language.
To get the current running application language, we can use Bundle APIs.
And Bundle APIs are useful for language matching.
Bundle.main.preferred Localizations considers both the user preferred languages and the languages that your application bundle supports and returns a list of languages your application Bundle supports in order of user preference.
Now, if you have an external list, say if you're loading some remote content from a server and your server has a different setup supported languages, you can use the class method Bundle.preferredLocalizations from.
And this will also consider that third language from your server.
Now, to help walk you through this feature and what it means for your apps, I'd love to jump in to a demo.
OK. So, last year, we demo'd Vacation Planet. The first Interplanetary Travel Booking app, and we've since expanded the app to be able to book complete travel experiences.
We had this new Discover tab with the collection view that shows some of our great offerings in these thumbnails like this romantic space cruise to Pluto or Moon Yoga.
Now, some of these are seasonal offerings like this WWDC Afterparty.
And because this is seasonal offering this content is actually pulled from our server. So this image and this great description about how this is the first Interstellar Bash and how we're going to be going on a onboard-- we're going to be going on a ship with an onboard seafood restaurant, the Codable, is actually being pulled from our server.
Now, after reading that description, I think this looks great and I'm ready to book.
But as you know, WWDC attendees has come from all around the world and they might prefer to do their travel booking in a different language.
So, what I'm going to do is I'm going to go down to the profile tab where we have a table view cell that shows the current running application language.
And when I click on that, it's going to pop me right into Settings where I can click in to see a list of all the languages my application supports.
I'm going to go ahead and change this to French.
And when I go back, you can see that the System UI is still in English but when I go back to Vacation Planet, the app will relaunch and it'll be in French.
Now, one thing you'll notice is that it actually brought me right back to where I left it and that's because I've adapted those state restoration APIs.
And you can see now all of our strings and all of our date formatters are in French which is great. But when I go to that server side loaded content, I can see that that is still in English.
Now that seems problematic because we want to be consistent in our app and we want their app to be in one language.
So, let's take a look of what might be going on there. So, I'm here in our Collection View Controller and I have this method Local Discover Items that downloads that content from the server. And when we download that content, we actually need to pass in a language because the server needs to know what language are we fetching that content in.
And here you can see that the language I'm passing in is Locale.preferredLanguages.first.
As I mentioned before, Locale.preferredLanguages going to return the users preferred languages regardless of the language that my application supports.
And so, this could be something like Persian that my application doesn't necessarily support. Now, ideally, I'd like something that I'll consider the server support languages, my application bundle supported languages, in order of the user's preferences.
So, I'm going to start by fetching the server's supported languages.
You can see that here. And then I'm going to use that Bundle API, that class method on Bundle.preferredLocalizations from, and I'll pass in the availableServerLocalizations.
And now the first element of this will be the best language to request that content and from the server.
So I went ahead and I rebuilt.
And wait a couple of seconds for it to relaunch.
And you can see now that image and all of that content is in French.
Isn't that great ? Cool, so, I'm ready to book and I can't wait to see you guys all at this Interstellar Bash. All right.
So, to summarize, App Language Setting allows users to choose a language for your app.
Launching to Settings is the best way to allow your users to switch the language of your app.
Use state restoration to make that language switch a seamless experience for the user.
And, finally, if you're doing some custom or server-side resource loading, be sure you're using the right bundle or locale API for your use case.
I'm incredibly excited for all of the great features and enhancements we've worked on this year and can't wait to see how you're going to bring your apps to an international audience.
Now to tell you how we've made it easier for you to do just that, I'd like to call up Vivian. Vivian? Thanks Kulpreet. So, let's talk about some of the enhancements that localization tooling in Xcode. And this year, we've done a lot of things you've been asking for, so this is going to read a little like a wish list.
First up, performance.
So, I'm very excited to announce that exporting projects with lots of Interface Builder files for localization is now on average, 15 times faster.
So, this means if you had a project that was taking about a minute, that's going to be down to four seconds.
And we did this by redesigning the string extraction process, so the more Interface Builder files you have, the bigger an improvement you're going to see.
Also to really speed up and streamline your workflow, don't bother with calling genstrings or ibtool directly, instead use xcodebuild, exportlocalizations, and importlocalizations to take care of creating all of your strings files.
Next up. iPad apps for Mac, so you may be really excited about bringing your iPad apps to Mac, but maybe you have some strings that wouldn't make sense, and maybe something it says, tap here but the user would now be using a mouse with a cursor, maybe clicking, or here in news, you can control Siri's suggestions in Settings, if you're on the iPad, but that would be the apps preferences on a Mac.
And, of course, you want to have that one NSLocalizedString called it's already in code.
So, to help with this, we're introducing a new stringsdict rule for device-specific strings.
And this rule can be combined with the plural and variable width rules. So, you can use this for any of your existing strings even if they're already in a stringsdict file.
So, the new rule is NSStringDeviceSpecificRuleType and the supported device keys are apppletv, applewatch, ipad, iphone, ipod and mac. So, it's not just for iPad apps on Mac.
And your stringsdict will look something like this, at the top, your existing string key then the new rule NSStringDeviceSpecificRuleType and inside a dictionary, with all of your device specific strings index by those device keys.
For more on iPad apps for Mac, please check out the session Taking iPad Apps For Mac to the Next Level.
Next up. Settings bundle.
Settings Bundles are now included in your Xcode Localization Catalogs.
So these are as easy to localize as anything else in your project.
And one of the more significant improvements we made to the localization tooling in Xcode is in how you localize Assets.
So until now, in order to localize an image it needed to be an independent file in your project and this may have meant you had some files in your Asset Catalog and the localized ones floating around lose.
So I'm very excited to announce that starting now you can localize images directly in your Asset Catalog.
So, this means you can now localize your image sets, watch complications, Apple TV image stacks, Sprite atlases, Game Center dashboards and leaderboards, as well as the new symbol sets.
So, let's take a look how you do that.
In the Asset Catalog Editor when you select a supported type in the Attribute Inspector, you'll now find a localized button. Click that and we add the localized of our attribute to the content so adjacent to that asset, you're ready to export an Xcode Localization Catalog.
But, say that you're multilingual and doing some of the localization work yourself or maybe you're moving already localized images into the Asset Catalog, well, when you click that button, it's going to reveal check boxes with all of the localizations already in your project.
Check any of those, and it will create the wells in the Asset Catalog Editor, so you can drag in the localized images like you would any other asset.
Now, what about those new symbols? Well, SF system symbols are localized for you, you don't need to do any extra work.
Custom symbols can also be localized.
You can either set the directionality, if you just need them to flip for right-to-left languages like Arabic and Hebrew, or you can do a full localization cycle in the same workflow as images. You'll find the same localized button in the Attribute Inspector.
For more on symbols, please check out the session Introducing SF Symbols later today.
And with that I'd like to show you all this live in the demo.
Now, you may have notice in Kulpreet's demo that we had not localized the rest of these images, so I'm going to do that now.
If I go into Xcode, I can go to my asset catalog and let's fix up this nice yoga image. It's a very nice yoga retreat on the moon where you can feel lighter, mentally and physically. So, I just go over to the Attribute Inspector, click localized, save, and that one is ready to go.
I've already done this for my other vacation packages but I have one image that's already localized and I want to move into my Asset Catalog to take advantage of all the great features of Asset Catalogs like being able to specify a different appearance for Dark mode.
This is super easy, I just grab my old image and drag in to the Asset Catalog.
Xcode has created a new image set with the same name as my old resource.
So I don't even need to go make a change in my storyboard, it will just work.
Now, I want to bring my localizations along too, so I click localized, check off the existing language and I'll drag that one into place, and save.
And I can just delete the old one, from my project and on disk.
OK, so everything is ready. I can now export an Xcode Localization Catalog.
This is the same workflow as Xcode 10.
I select the project, go to Editor, export for localization.
And let's just do French for now, I don't need the rest of these.
Let's see what we got. OK, here is my Xcode Localization Catalog and I can just send this off to my localizers and I'll find my localizer. I'm going to pop this open and I know I can dive right into Localize Contents and get to work.
So, what do we got here, OK, here is the xliff, I know what to do with that, I know it has all the strings for the project but what else is here? OK, we have an Asset Catalog.
And this is a fully formed Asset Catalog. I could go drag this into another Xcode project and use it.
But this one has been filtered down to only what the localizer cares about.
So, it's only the localizable content and only for this locale.
Now, in this case, we don't have any French images yet, so on here, it's just the copy of the English. This is for the convenience with the localizer. They can just jump in, start editing.
Save time, I've had some localizers do exactly that for me. So, let's see what they sent back.
Just go open the catalog in my favorite localization tool here.
And we can see side-by-side the English and French versions of all images there is yoga, yup.
OK. Let's import this into the project, go back to Xcode, again same workflow as Xcode 10, select the project, go to Editor, import localizations, select that finished Xcode Localization Catalog and click Import, great. So, now, I have to go to my asset catalog, I can see, I now have two versions of my image in here all in one place.
But, it's kind of small, so let's run the app again, so we can see that a little bigger.
So, this time when we go back to the app, we should see all of our vacation packages now with the French titles.
There we go, beautiful.
OK. Let's go back to the slides.
So, we just saw some of the enhancements to localization tooling in Xcode.
Export and import of interface Builder files is now a lot faster.
There's a new stringsdict rule for device-specific strings. Settings Bundle is now easy to localize.
And symbols and images are localizable in Asset Catalogs.
Now, of course, once you've localize your app you're going to want to test all of those localizations.
And we've made that easier too. And I'd like to call up Arthur to tell you all about it.
Now, let's take a look at how you can take advantage of UI testing for localization.
Testing is a significant part of any development cycle.
It takes time and resources but it's very important to help catch regressions as you add new features and detect bugs before you release your product to your customers.
If your app support more than one language you will have to test full language specific issues.
Clip strings, truncated content, overlapping layouts or even issues specific to right-to-left languages such as Hebrew, Arabic.
Now, if you want to test for every device you support, in every language you support, your test metrics can repeat and explore.
You can easily end up with thousands of different scenarios.
And that will make it really difficult for your testers to cover all these scenarios manually.
So, it's even more important to include test in your project to help produce manual work load for your QA.
In Xcode 11, we made it easier for you to cover all of this test automatically with test plans.
Test plan is a very easy way to run the same tests multiple times using different configurations.
I'm not going to go too much into details about test plans today. So please check out the Testing in Xcode session on Thursday morning to learn more about it.
What we will be covering is how you can use test plans in your localization workflow.
So, first, we will see how you can make sure that your test localization friendly, then we'll talk about how you can use test plans to generate localized screenshots.
And, finally, we will go over some of the many use cases for those screenshots, because they're not only good for testing.
First, let's see how we can make sure that your test can work in every language you run them in.
To do so, you should rely on Accessibility Identifiers.
These identifiers are unique, stable, and we'll guarantee that your test can run in any language.
You can easily set them up, setting the property of the same name in code or if you're using interface Builder to create your UI element, you can define them right there in the Identity Inspector, and that's all it takes. So, now, that your test are localization friendly, let's see how we can use them to Generate Localized Screenshots.
As I mentioned earlier in your test plan you can create multiple configuration.
In each of this configuration you can set a different language and a different region to run your testing.
Then by simply turning on the Localization Screenshots option in the UI testing section, your screenshots will be persisted and there's so much you can do with them. Well, some of this option available in the Scheme Editor that we already-- you already know and use in case you haven't market it to test plans yet. And now that your tests are set up to capture Localized Screenshots, let's see what we can do with them.
They're actually a great marketing resource.
You can pass them along to your marketing team or even upload them to the App Store to showcase your latest design. You can use them in your documentation or in your tutorials.
But more importantly they can help your localizers get the context they need to provide you with high quality localization.
Let's take a quick example.
A simple word like Start in English could either be a verb meaning to begin or it could be a noun and referred to the beginning of an event.
While this difference might seem really subtle in English, it can lead to widely different localizations in other languages.
Here, we can see it's a noun because it's labeling the starting date.
Giving this visual context to your localizers we'll alleviate the ambiguity and make sure that you get the most accurate localization.
Last year, we introduced the Xcode Localization Catalog.
It's a very common way to handle localizing both strings and non-string based resources all in one place.
In it we included the Notes directory, specifically for you to include contextual information such as screenshots for your localizers.
This year, I'm very happy to say that we are automatically including these screenshots for you in the Xcode Localization Catalog catalog.
And all it takes in one-- is one single click during the Export Localization Workflow that you're already using.
If you rely on automation to generate your Localization Catalog, you can simply add the include screenshots argument to your xcodebuild exportLocalizations command.
You can also customize which screenshots I exported.
We made it easier for you to specify which test targets, devices and languages I exported to Localization Catalog.
But that might be a lot of screenshots for your localizers to come through to see exactly what Strings shows on the UI.
So, alongside every Localized Screenshot that we generate, we also generate a metadata.plist, mapping the strings to the exact location on the screenshots.
This plist include the strings IDs, the ones that you can find in the Eclipse but also the framing formation, which is the exact location of the Strings on the screenshots.
This makes it really easy for localization tools to map the Strings to the Eclipse and highlight them directly on the screenshots.
And once you export your localization catalog, this new content is neatly organized in the Notes directory and the metadata.plist consolidated into one for test targets and device.
And this is how easy it is to direct Localized Screenshots with Xcode 11 and test plans.
And with this, I'd like to call Vivian back on stage to show you all of it in action.
So, now that we've localized our app, we want to test those Localizations.
So, I've already created some UI tests for my project here.
I have some here that will capture all of my screens, which I think would be really helpful for giving to my localizers for context. And I have another test that just zips in there and gets a picture of those nice vacation package images, which I think would be good for marketing.
But I want to run all of these test in all the localizations I support. And that could take some time which is where test plans comes in handy. So you can add a test plan to your project like any other file.
File, New, File, and there's a template for it, so I can just search for plan. There it is.
You can also, migrate your existing test to test plans.
Let's save time.
I've already made one.
So, let's take a look. OK. So I have two test targets here. I have one that will run all of my screens tests and another that's just going to capture that marketing image.
I also have some configuration setup.
The important thing here is in the shared Settings.
Localization Screenshots is turned on.
So, I've got one configuration for English, United States and another for Swedish, Sweden. So, since I've just got those images in French, I'm going to add one for French.
Just click Plus.
Let's call this one French, France.
OK. And we will set the application language to French and the application region to France.
OK. It's now-- I think, I'll rerun just the test to get that marketing image. So, I can get a picture of those newly localized French images.
So, I go back to my UI test in order to run it.
And now, when I click the test diamond, instead of running my test once, it's going to run it with all of my configurations and we can actually watch as it goes through all of this.
So, it's going to run three times, English and then Swedish except my localizers aren't done. So, we'll probably going to see some English strings again and then one last time in French.
And I know that this will work because I use accessibility identifiers like Arthur said. So my test will work with all three language configurations.
All right. Here we go again. French, OK, great.
So let's see what we got here. OK. If I look at the test that I just ran. OK. I can see it run, it's on my configurations and if I check out French and look at my attachment.
Beautiful. So, I can use this one for marketing.
But my localizers are waiting for an update on Swedish that they can finish that.
So I need to send them a fresh Xcode Localization Catalog and I want to make sure that they get that really tricky start string, right. So, let's make sure they get a screenshot.
I ran my test earlier for it to capture all of my screens.
So, if I look at the Swedish one-- let's see if we can find that tricky start string. I think it's down here.
Yeah. There we go. OK. So they haven't translated Start yet or End and they got Travelers.
And also in here I have that plist with the frame information which should be useful.
So, I'm now ready to export an Xcode Localization Catalog.
Select the project, go to Editor, export localization. And this time, I'm going to do Swedish and importantly I'm going to click Include screenshots.
All right. Xcode has found I have 19 screenshots available. Let's see exactly what I have here. If I click customize, I can see I ran my test with iPhone 8 Plus, 10R and 10S. So, if there's any layout issues that might catch some of those. OK. I'm definitely going to keep all of those.
And, now, I'm ready to export.
OK. So, now, if I look at my new Localization Catalog, now in the Notes directory I have screenshots.
And here I can see I have all of my screenshots organized by test target and device.
So, actually, I'm going to pull the marketing ones out for later and just send the rest to my Localizers.
So we look in here, we can see I have all of my screenshots.
OK. There's that tricky start string and actually worst than Start, you can see in this one screenshot, we have the string Book, one, two, three times.
So that plist with the frame informations can be really useful. And this is the consolidated plist, so it has the frame information for all of the strings captured in this entire set of screenshots.
So, they're really handy because now when I'm on localizer I can open the catalog in my favorite Localization Tool. And now, I can preview where string shows up.
And not only can I see one place where a string shows, it will show me all of the screenshots that capture the given string.
So, my localizers are going to be thrilled.
All right, let's wrap things up.
So, today, we saw how users can run your app in a different language to their system language.
We saw a bunch of improvements to the localization tooling in Xcode, including faster export of interface Builder files, a new stringsdict rule, how easy it is to localize your settings bundle, and of course how you can now localize images right in your Asset Catalog.
We also saw how you can generate Localized screenshots with your Tests and some of the uses for those screenshots.
For more information, please visit our page on developer.apple.com.
And if you have any questions, come see us later in the labs.
Thank you. Enjoy the rest of the conference.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.