Common assumptions can break when your app is used by a global audience. Learn about the many aspects of creating apps for different regions and languages. Understand how to use fonts and typography, layout techniques, and support text input so your app shines in all languages.
Good morning. Wow. Thank you. Thank you, very much. And once again, welcome to your first session at WWDC. My name is Joaquim. I don't think I have to reintroduce myself. Karan, Dongyuan, and I are really excited to talk to you about internationalization, at long last. So, what are we going to talk about? I'm going to do a brief introduction, like I started doing. And we're also going to do some deep dives on making your app's layout adaptive. And also, cover some really fun discussions on text, as well. So, why is this important? This applies to every single app. Internationalization is a crucial concept and we're going to go over some of the details as to why it applies to every single app, regardless of the set of languages that you support.
And once again, this is all about reaching out to more people. It's enabling everybody to learn about and use your app. Regardless of the language they speak or the country that they live in, or the country that they grew up in. And it's important to note that those last two might be different, as well. And that's something that's worth keeping in mind.
So, internationalization covers a wide range of topics, and potentially, every single aspect of your app. And if you're new to the topic, probably, the best way to think about it is that internationalization is best applied as a constant process. It's a discussion that belongs in every single stage of your app. Whether it's the initial inception, its design, to its implementation, and even distributing it on the App Store. Things like your App Store screenshots and even your app's name should be open for discussion in an international context. And like I said before, this covers a wide variety of topics.
At Apple, we have a lot of firsthand experience about this. So, we estimate that over 70% of our customers are outside of the United States. And this means that when somebody downloads your app they expect that app to match your language preferences. And not only that, but also, be aware of any details that might be related to their region settings, as well.
We support exactly 38 different written languages and many, many more keyboards and input methods for people to type and text. And what this means for you, is you have a rock solid foundation to get started with. Every single one of our API's supports these languages and input methods. And this means that you can get started right away by using these and you're off to a great start in providing really great international support to your app.
So, for example, this might entail presenting dates and times. This wouldn't be an international talk if I didn't talk about dates and times. But it's important to think about, right? Because people have different expectations around the world.
This is the Sao Bento in Portugal. It's a lovely train station. But my focus today isn't so much the train station as much as the way the times are presented in the train station. And if you go there, you might notice that everything is in 24-hour time. And this is true, not just in train stations, but it's commonplace everywhere in many countries.
And so, your app should know about these and present time accordingly, depending on the region. And you can do this using date formatter. It's a set of APIs we provide that lets you do this. And it really does all the heavy lifting for you. Another example is calendars. There are many different calendrical systems around the world. And it's also important to note that many people use more than one on a daily basis. They might use one for religious or festive events and another for business. And these all have different properties like leap months and leap years and different numbers of days and different numbers of months. And so, being precise about these and knowing how to present these appropriately means being internationally aware.
This is an example of the iOS Calendar app showing the Chinese Lunar calendar on top of the Gregorian calendar. And we have a set of API's in Calendar and alongside Date Formatter that help you do this.
Could also talk about units and measurements. Whether it's presenting things in the metric versus the imperial system, or even temperatures like Celsius or Fahrenheit.
Your app should be aware of the appropriate regional defaults for these, and also, respond to changes in the user's preference. And not only that, whether they're units or not, even numbers themselves. There are so many things to cover there from the decimal separator to the thousand separator. And even, the entire class of digits used to represent and talk about numbers. These can change between regions. And if you use number formatters, your app gets this behavior for free. And this is important not only for consistency, but also, understandability. Text is, also, another great topic with many different areas. And depending on the language or languages that you grew up learning and writing, and the scripts that those language are written in. You might have thought that there are some properties that are completely immutable across other scripts and other languages. And that might not, necessarily, hold. One example, probably the biggest example, is script directionality. So, some languages, like Arabic and Hebrew, and in this case Urdu, are written and read from right to left. Which, is completely the other way from English, from what you might be used to.
And taking this further, many books, most books published in traditional Chinese and Japanese are presented in vertical and right to left. And so, knowing about these formats and knowing about the context in which it's appropriate to present these and adapting your layout for these could be an important aspect of your app. And are why frameworks alongside our text frameworks like TextKit and CoreText can help you make sure you're doing the right thing in every single one of these cases. Going a little bit higher level, even things like names. Depending on where you grew up, you might have a first, middle, and last name. Or a family name that comes before your given name.
And again, knowing how to present these, and even asking your user about these could be a crucial part of your app. And this is something that users expect that you get right and know about the details and intricacies of formatting names.
And we have PersonNameComponentsFormatter API that help you do this.
Okay. So, throughout all of these concepts it's also important to note that many of the users might interact with and speak more than one language on a daily basis. Whether it's because they moved countries, or simply because that region, in and of itself, uses more than one language. And taking this example further, even if your app only supports one language it's very likely that your customers are using your app to publish and consume content in their own native language. And so, regardless of the set of languages that your app supports, these are all still very important topics to consider and talk about throughout the design and implementation of your app.
So, if all of this is new to you, worry not. Like I said before, we have a great set of APIs that really do all the heavy lifting for you and take care of all these different aspects. And are aware of all the different details between languages and regions. These are some examples of the Formatter APIs that I talked about. And we've, also, got some great sessions that go into great detail on how to further tailor these for your app, as well. So, let's start our first deep dive in to layout and some of the goals related to internationalization when it comes to adaptive layouts.
Because, really, the core goal of an adaptive layout is to present all kinds of different information.
And when you're adapting your application for other languages, probably, the biggest guarantee that you'll get is that these translation lengths are going to be different. They're going to be much shorter or much longer, depending on the language. And this is something that your design and layout should adapt for.
On top of this, there's also directionality, like I mentioned before. Because some languages are written in right to left this has some design considerations, not just in the text that you have in your app, but also, the way you present information. Especially, horizontally flowing information and how that general flow should adapt for both script and writing directions.
So, a great starting point to help you do all of this is Auto Layout. You might have heard of Auto Layout, before. This is a powerful technology that is at the core of our layout engine. And Auto Layout, instead of describing explicit frames or positions of your controls and labels, describes constraints or relationships between these views. And therefore, how they're positioned relative to each other and how they're allowed to grow relative to each other.
On top of this constraint-based system we have the idea of leading and trailing constraints.
And what this means is that this, essentially, describes properties that are left and right in a language like English that automatically evaluate to right and left, respectively, in a language like Arabic and Hebrew. And this means that with Auto Layout you can create these adaptive layouts that flow depending on the writing direction without having to write special code for either one of the writing directions.
Another great starting point is to use high level components and containers that we provide in our UI frameworks. Because these use all of these concepts and are aware of how to be adaptive. Some examples are collection views and stack views that we provide, both in UIKit and AppKit. And you can create really complex layouts and even embed these within each other to, potentially, even just create the whole layout of your app just using these.
Just as an example for Stack View itself, I could arrange these horizontally in a Stack View. So, I have the city name on the left and the time on the right. And this is, this could just be put into a horizontal stack view. And because this flows left to right in English and because I'm using a StackView if I run my app in Hebrew, this is the result I would get. Because that information would adapt automatically. Because StackView is aware of these concepts and uses Auto Layout under the hood.
If you'd like to learn more about Auto Layout and details on how to adapt your application for different writing directions, these are some great starting points to check out, as well.
So, when it comes to the text in your app and the content in your app the key goal here and, obviously, the number one recommendation is to simply not assume fixed widths. If you don't do that you're off to a great starting point in terms of allowing your app to adapt to, not only different changes in length horizontally. But even, potentially, allow your labels to grow vertically and adapt to multiple lines if necessary. And this is something you'll have to decide in your app and your app's design on what you want to prioritize in terms of what is allowed to grow and use up the real estate of your app. If your labels and your controls are either in stack views in these high-level components that I mentioned before, or positioned using Auto Layout itself, this work is pretty much done for you. You're already allowing your labels to grow and your controls to take up the content that they need to take up. And then, it just becomes a matter of prioritizing how you want to let them grow relative to each other.
So, this is great. Let's say you've done all this. And how, exactly, do you make sure that your app is checking off all the right boxes in terms of adaptive layout and responding to all of these things? Well, the good news is that Xcode provides a number of features for you to test. And not only that, it allows you to test early and find these out quickly in the development, and early on in the development of your app.
One example is pseudolanguages. Pseudolanguages are great, especially, when you haven't yet added language support to your app. So, what you can do from within Xcode is run your application in a pseudolanguage and this changes a few details of the layout and text of your app. One example is the bounded string pseudolanguage. This adds a few characters at the beginning and end of every single UI string that you display. And then, you can see, make sure that there aren't truncations or unexpected clippings in the content of your app. So, this is a really useful one. And we have many more pseudolanguages that can help you out, as well. Specifically, for Auto Layout we have Auto Layout warnings built into Xcode. So, Xcode can tell you about common antipatterns right within an interface builder. And this can be things like fixed width constraints or too few constraints on a control or label that might, potentially, introduce issues at runtime, especially, in other languages.
So, with that, I'd like to hand it over to Dongyuan, who's going to give you a demo of all of this in action. Thank you, so much. And hope you have a great week. Thank you, .
Thank you, Joaquim.
Hi, I'm Dongyuan. Let me show you some techniques to and some common pitfalls to avoid in real world.
Here's an app called Vacation Planet, which is the first interplanetary travel agent that allows you to book travel for not only other countries, but also, as you can see in this table view, other planets.
So, I wanted to go to the moon for quite a few times. So, I can do that, this time.
And here are all the locations available on the moon and their distance from Earth.
Let me choose the location like the Clavius Base.
And here is our Travel Details page.
It seems pretty cheap for one trip. So, I'm going to buy more tickets. Let's buy three of them.
And there we go.
As you can see, because we design and developed the app in English our layout works great in English. However, we still want to make sure it can adapt for other languages.
Because we are still early in the development cycle and we haven't localized the app, yet, we can use the pseudolanguages in Xcode. To do that, I'm going to go through the Current Scheme, click Edit Scheme.
And in the scheme editor I have an Application Language selector.
I can select one of the pseudolanguages like the Bounded String Pseudolanguage.
It's very useful for exposing potential clipping or truncation issues.
Let's run the app in this configuration. As you can see, this pseudolanguage adds a few special characters at the beginning and end of every UI strings.
Our tag line here is still good. But the Browse button is truncated.
Let's try to fix that. When I select the Browse button I can see that there's a wording in Xcode.
The wording shows that we have a fixed width constraint and that may cause clipping. If I want to know more information, I can click on the I button.
I'm going to fix the issue by click the growth, sorry. By click the Warning sign.
Here are three options. I'm going to select the first one, which is simply remove the constraint to allow the button to be wider when there's more content. Let's do that.
Okay. No other layout issues. And then, I'm going to show you a very simple way to verify the change that was made.
I'm going to go to the Assistant Editor at the top right corner, the middle button here. Here I can select Preview and Main tell Storyboard.
The Preview pane allows you to view your layout in multiple screen sizes and different languages without running the app.
On the bottom right corner, I'm going to select the first, the same Bounded String Pseudolanguage we just used. Great.
Because we don't have the fixed width constraint, anymore, our Browse button can now accommodate a slightly longer string. Another very useful pseudolanguage is the Double Length Pseudolanguage. It's for verifying your layout against potential languages that have longer string length, like German, Finnish, or Russian.
They can sometimes be two times longer than English. Let's see if that works. Now, the Browse button is still okay. But our tag line is clipped by the screen boundaries.
When I select the label, I can see that we only have a center X constraint. We don't have any leading or trailing space constraint, so that the label can overflow.
Let's add a leading space constraint to fix that. Again, I'm holding down the Control key, drag from the label to view, and select Leading Space to Safe Area. I can select the constraint to adapt at just its value. Let's put something reasonable, like 20, for the margin. And here, I can see instantly that my label is no longer clipped.
However, it's still truncated and it's a very important message we want to show to all of our customers. That's not ideal.
Instead of truncation, I can allow the label to wrap into multiple lines when necessary.
Here, when I select the label I can see that we have Lines property set to one, which means we only allow one single line for the label.
If I change the value to zero I can allow the label to wrap into any number of lines, when necessary. I can see that my label is now three lines in this Double Length Pseudolanguage.
If I switch language back to English my label is still one line, which is exactly what we expected.
I, also, encourage you to check the app down here, where you can select different screen sizes.
I encourage you to check your layout in the smallest device, like iPhone SE, because clipping and truncation are more likely to happen in the smaller devices. Now, I wonder if our layout can adapt for right to left languages, like Arabic or Hebrew.
As I said earlier, we don't have localizations for those languages yet.
We can run another pseudolanguage.
Let's open the Scheme Editor, again, this time in application language. And I'm going to select the Right to Left Pseudolanguage and run the app in the simulator.
As you can here, our table will, now flows from right to left without us making any changes. That's because we used UITableView and other standard UIKit components.
The system had done the hard work for us. All the section titles are now at the right side, which is the leading side for right to left. The chevrons here are at the left side, which is the trailing side.
I'm going to go to Jupiter, this time.
And you may notice that the Back button is now at the top right corner, instead of top left.
That is, actually, very natural for right to left languages.
Let's select a location on Jupiter.
So, here's our Travel Details page. Everything seems to be okay, except the stepper and the Traveler label.
The first issue is that the stepper should be at the trailing side, which is the left side for right to left. The Traveler label should be at the leading side, which is the right side, here. And also, there's an unnecessary spacing for the Traveler label.
Let's fix that in that Interface Builder. Let me locate our trouble zone cell, is here. Okay.
Zooming in a little bit. When I select the Traveler label and the stepper I can see that there's no constraint on them. That's not good.
And of course, Xcode has a warning for that.
So, one way to resolve the issue is to add a leading space constraint for the Travelers and a trailing space constraint for the stepper.
Leading and Trailing will translate to right and left for right to left languages.
However, here I'm going to show you an even simpler way, which is to use UIStackView, a high-level container view that uses Auto Layout under the hood.
By using Stack View I can get right to left support for free. Let me select the two views.
And click the Embed down here at the bottom right corner.
Select Stack View. Now, my two views are inside this UIStackView. The only thing left is to add constraints to the Stack View itself. I'm going to select Stack View, click the Add Constraints button.
I'm entering four zeros, here, because I want the Stack View to fill the table view cell as much as possible.
And I like to select the Constrain to margins here, because I want some default margins for the Stack View. And I want the leading edge to align with the cell separators. And add four constraints.
There you go.
And I'm going to verify my change.
This time, I'd like to visit Earth. And I'd like to go through Lisbon, Portugal, to visit . Do that. Now, as you can see, because our Traveler label and the stepper is now inside a UIStackView, we get right to left support for free.
Let's book this travel. Great. Now, let me summarize what we talked about.
So, to make our app layout great for our global audience, there are a few simple steps. First, is to use high level containers like StackView whenever possible, as they provide a lot of heavy lifting for you. And believe me, they are just simpler to use.
For finer control, remember to use Auto Layout and make sure to use leading and trailing constraint so that you can adapt for right to left languages.
For early testing without even localizing your app, you can use the pseudolanguages in Xcode Scheme Editor. And please, don't ignore the Auto Layout warnings in the Interface Builder. They are very useful for avoiding clipping, truncation, and overlapping issues.
Now, over to Karan to talk about text. Thank you, Dongyuan.
Good morning, everyone. Let's talk about text.
At Apple, a high quality typographical experience is a key part of our design process.
And it's very important to us how text looks on screen and how it's designed.
And also, the way that it translates to other languages.
So, let me walk you through some key aspects that we keep in mind when we localize our own apps into other languages. And how you can take advantage of these things to make your apps look great in other languages.
Now, I'm going to talk about three main topics, set up some fundamentals with languages and scripts. And then, dive into typefaces and styles.
Let's talk about languages and scripts.
What is a script? So, when I'm talking about script, I don't mean a Bash script or a Python script. I'm talking about the way that a language is written. So, the writing system. The letters that you use to write a language. These are some of the scripts that we support that are written from left to right.
We, also, have scripts that are written from right to left. The key thing to note about all of these scripts is that they're multilingual. So, each script supports a huge variety of languages that are written in it. And as you can see, the Latin script here supports everything from English to Vietnamese.
And this is true for other scripts, as well. For example, the Cyrillic script supports a variety of different languages.
And if you look at right to left scripts, we see that the Arabic script, not the Arabic language, but the Arabic script supports a variety of different languages, such as Arabic and Persian, Urdu, etcetera.
So, what you're seeing on screen, yes, there's a lot of visual variety. But this is not just for show. There's, actually, a lot of implications when you develop your apps and different scripts because some concepts don't map across scripts as easily as others. So, let me show you a few examples of this. Let's talk about typefaces.
So, here you see the Health app in English. So, you see a lot of labels.
Now, you see the same app in Catalan. And lastly, you see the same app in Vietnamese.
Now, the thing that I'd like to call your attention to is that all the text here on screen is rendered using our system font, San Francisco.
And the other thing I'd like for you to notice is that everything is rendered beautifully.
That's because San Francisco has support for a huge variety of different languages. And when you use the system font in your apps, you're guaranteed to get that support for free.
Now, any text label that you create in Xcode will get San Francisco by default. But if you want to go one step further, use a text style.
We support a variety of different styles in our OS that are meticulously implemented to support a variety of different use cases. And also, to map across different languages, really well.
So, when you use a text style you guarantee a consistent high quality experience to your users. You can also go one step further.
If you set your label to automatically adjust its font size, it will do so with respect to the user's text size setting. And this is really handy for people who use smaller or large sizes for their text. Like myself. So, I highly encourage you to use that and this will ensure that your text styles scale appropriately. But let's say, that like us, we're making this Vacation Planet app to go to Jupiter, to go to Mars. And we wanted to have this fun vacation look to it, which it doesn't right now, because everything is in the system font. So, we decided that the title of the font should evoke the personality of the app.
And so, we looked at a few different fun fonts to choose from.
We looked at this one, for starters. But as Joaquim mentioned from the start, we like to keep localization as a central part of our development and design process. So, the first thing we did was to check does this work for all the languages we need to support? Well, we already had a French localization. So, we tried it out. As it turns out, it doesn't. So, we kept looking at our short list of options. And we looked at this other typeface.
And when we tried it out in French, voila! It works because it supports all the characters needed for French. So, that's great news.
So, the next step for our app was to expand to Vietnamese. But we didn't have a Vietnamese localization, yet. So, one great tool that you can use is FontBook, which comes installed on every Mac, even if you're not a developer. It comes installed on every Mac. And in FontBook you can easily search for the name of a language, like Vietnamese, here. And we see here, that in the font that we chose Vietnamese is in the set of supported languages. In fact, this font also supports Cyrillic and Greek script. So, we are somewhat sure that if we expand to Russian and Ukrainian and Greek later that this font will work for us.
Now, I should advise some caution here. Just because FontBook says that a font supports a given language doesn't mean that you don't have to, actually, test your app in that language. You still need to make sure that the font really works for that language, by trying it out. So, we've got our beautiful fun font for our Vacation Planet app.
And now, we're going to expand even further. So, we want to do more languages. Specifically, we want to localize into Chinese.
Okay. So, we sent off all our strings for translation and they've come back. And our app is now perfectly localized into simplified Chinese. Cool.
But it's not really, though. Because look at what happened to the title.
So, in English, we have this fun font, but in Chinese we are using the system font. That's because our font only supports the Cyrillic, Greek, and Latin scripts.
So, of course, the way to fix this is to repeat the same process for Chinese. And here now, we have out fun font in Chinese. It's as simple as that. And unfortunately, I can't really help you with that. That's a stylistic thing and you have to choose your own fonts. But I can show you how this is done in code.
So, this is really straightforward. First of all, you start with your font for your development language, normally. Such as, in our case, it's English. So, we choose our Latin script font and get our font here.
And the key concept that I want to introduce you to, here, is called a Cascade List. So, a Cascade List says if I'm looking to render this Chinese character and this first font doesn't have it, what font should I use after this to look for this character? Now, if you don't specify a Cascade List you're going to get the system font. But if you do have a Cascade List, then you can specify other fonts to try before falling back to the system font.
So, in this case, we create a Cascade List with a font descriptor for our font for Chinese that we hand-picked. Now, if your app supports multiple scripts you add multiple things here and that's as simple as it works.
And once you've got a Cascade List you create a new font descriptor. And then, you create a new font. Pretty straightforward. And also, make sure that if your app used dynamic type, which it really should, then your font should adjust to that, as well. And again, that's as simple as an API call.
Let's see some examples.
So, this is the new Word of the Day screensaver in macOS Mojave.
And as you can see, for this new design we've chosen a rounded style. And of course, it wouldn't make sense if we didn't, also, choose equivalent rounded styles for all the languages that the screensaver supported, which includes Japanese. And also, new in macOS Mojave, simplified and traditional Chinese.
Another example is the Messages app, in which you can respond to a message using a tap back reaction. Now, here you'll see that the ha-ha in English has been not only translated to other languages. But has, also, been matched in style, so you get the same bubbly fun look in all the languages that we support.
That's it for typefaces.
Let's talk about styles.
Again, I should start with the definition.
What do I mean by a style? So, broadly speaking, I'm referring to aspects of your text that you choose once you've chosen a typeface. So, let's say the font weight, like how bold it is, or whether it's italicized, and what the size of the font may be.
So, the key thing to keep in mind is that some aspects translate better to other languages and others don't. So, let's take a look at an example of where something might not translate. So, here we have a simple string in English. We've italicized Mars and 2 Travelers to indicate that they're variables, so they can change.
And this is how it translates into traditional Chinese.
There are a couple of issues here.
Mars is italicized in English. It's not italicized in Chinese. Why? Because italicization is not a concept in Chinese. And it's not a concept in, actually, most scripts outside of Latin, Cyrillic, and Greek.
So, a design that uses italics, probably, won't work. The other thing to note is that in Chinese there are no spaces between words, and also, there is no concept of uppercase and lowercase letters. So, you kind of lose a natural distinction that you get in English when you translate it into Chinese. A couple of small things to note, also, is that the word order is different. And also, because the 2 still comes from the Latin script and is still italicized, it doesn't look great.
So, how can you fix this? Well, the keys to realizing that you're doing emphasis, not necessarily italics. And emphasis can be done in multiple different ways. This is a great way to do emphasis that works across several different languages. In fact, it works for all the languages that we support, which is to bold a given word. Lastly, let's talk about emphasis at a character level or sub-word level.
Let's say we have an app with a Search feature and we want to highlight the part of the results that matched to make it clear to the user what's happening.
Now, this works really well for English, by using a bolder weight for the matched segment. It works really badly for Hindi. So, what you're seeing here, so anybody who can read Hindi will tell you that everything on the right-hand side looks completely broken. Any time you see a dotted circle inside a Hindi word, something has gone very, very wrong.
And the reason this is happening is because even though it's the same font family, different weights within that font are, actually, a different font. And you can't have the appropriate joining behavior for languages like Hindi if you have two different fonts.
So, one easy way to solve this problem is use a different kind of emphasis at a character level. So, at a character level you can use something like using a different color. For example, here we use black color for the math segment and gray for the remainder of the word. This works really well. Now, you'll see some dotted circles on the keyboard. That's perfectly fine. They're dependent marks.
And here's the same example in Arabic. Again, the same approach is used all over iOS and macOS, and it works really well for many purposes.
And I should also mention that this is really easy to do with attributed strings.
Finally, let's go over everything that we've talked about.
First off, it's important to start planning early in your apps, not only the development, but also, the design phase.
Once you know the languages you're going to localize into and plan to extend to in the future, internationalize as you go. Technologies like Auto Layout, StackViews, and dynamic type are very simple to adopt while you're in the process of building your feature. But if you finish your project and try to come back and use them, then you may have to completely rearchitect your app. And that will be a lot of work.
If you're doing something for which you feel, oh, yeah, there should be an API for this, right? There probably is an API for it. So, make sure that when you're doing something like formatting any kind of data, to make sure to look to see if there's a formatter class. And also, make sure to look to see if there is an API for anything you're doing with text before trying to ruin your own implementation.
And lastly, ensure that nothing is lost in translation. So, the key thing to realize here is that every localization of your app is a unique experience. And you need to make sure that your intent that you specified in your development language actually makes it over to all the other languages that you support. And that nothing is lost along the way.
Thank you, very much. [ Applause ]
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.