Localizing your app is a wonderful way to share your work with a worldwide audience and make it relevant to more cultures and languages. We'll show you how you can prepare for localization before ever translating a word by building thoughtful layouts for your app. Learn how to structure your UI in Xcode, identify common issues prevalent with more verbose and right-to-left languages, and easily adapt your interfaces to provide a great experience for everyone.
Hello my name is Paul Borokhov and I am a software localization engineer at Apple. Today I will discuss how to build localization friendly layouts using Xcode. We'll go over some common patterns to accomplish this goal and things to avoid and discuss the tools that are at your disposal to make your job easier. First let's take a look at some common design patterns that you want to follow in your apps to make them localization friendly.
Crucially, these patterns are applicable no matter what platform you develop on or whether you employ manual layout, auto layout, or SwiftUI to implement your designs. First and foremost, you want to avoid using fixed widths or frames on any controls containing text. In a practical sense, this means remembering to call sizeToFit with manual layouts, avoiding fixed width constraints with auto layout and not setting explicit frames in SwiftUI.
As an example, consider this button on macOS: while in English it looks good, when we translate it into Greek, which tend to use longer strings for the same words, you can see that we clip the word midway through a character, and the ellipsis is missing completely. In most cases, you can simply remove the fixed with or change it to a greater than or equal to constraint to allow the text to show fully. Next, you want to avoid having fixed spacing when it is between two distant objects. Consider the example below: there is a bunch of free space between the Publish and Cancel buttons. While this looks good in English, when we translate to Greek you can see that the entire window grows because the translations for Publish and Cancel are longer. But a more pleasant design would allow the wider buttons to simply eat into the space between them, keeping the window width the same.
We can accomplish this with stacks or greater than or equal to constraints.
On all platforms, you'll want to make sure that text is allowed to wrap to multiple lines when it makes sense to do so. Consider the following example on iOS where UILabel is restricted to one line of text by default. This looks fine in English on an iPhone 11 Pro. To get a sense of how it might look in other languages that are longer, we can use double strings mode. This feature which can be enabled both in the preview and at runtime simply doubles the text inside every control and label.
Testing out this UI and double strings mode shows that the text no longer fits on one line and truncates as a result. Setting the number of lines to zero will allow the text to wrap properly. Finally, try to avoid placing too many controls in a fixed space with no affordance for an alternate layout.
Consider this example where I have four buttons side by side. As you can see, in double strings mode they no longer fit on a single line and end up truncating. This can be particularly problematic in Bars where you cannot easily provide an alternate layout. We will see in the demo one of the ways we can work around this problem. Now let's jump in to Xcode to see these patterns in action and how we can apply them on a typical app. Here, I have a simple iOS app that prompts the user to pick their favorite food.
We have a title label, a regular label, and a bunch of buttons to make the choice.
I'm going to go ahead and open the document preview. I will then click English at the bottom right and choose double length pseudo language to see what my UI might look like when I go to localize my app. Immediately I see that my labels should have their number of lines set to zero so that they are allowed to wrap. Once I fix that In the attributes inspector, the labels look good but the buttons are still truncating. Before I go and address that, let me add a comment to my translator explaining the context for the title label. I will select the title, switch over to the Identity Inspector and provide a helpful comment in the localizer hint field. Now, to address the truncated buttons, I have a couple of options. Perhaps instead of using strings in these buttons I could use pictures of the food so that they're all the same size. The approach we will cover today will be to change the layout of the buttons from horizontal to vertical, when there is not sufficient space to display them all side by side. You can do this by placing the buttons in a horizontal stack view and then querying its layout fitting size to determine whether it has sufficient space to display all of its content.
If it doesn't we will change its orientation to vertical. Conveniently, I already have a custom class written to do just that. So I will simply set it in the storyboard and make a connection to the leading constraint of the stack view.
If you want to see the details of how this dynamic behavior is implemented, go ahead and download the companion sample project below this video. Since this is a code change, I will not be able to see its effects in the preview and I will need to build and run the app. First let's just try running it in English. As you can see the layout hasn't changed from what I have defined in Interface Builder. Now let's try running it again in double strings mode.
I will go into the Scheme Editor, go into the Options tab, and select Double Length Pseudolanguage from the app language pop up. Since I haven't changed the app, I will run it with Command-Control-R to relaunch the already built version.
You can see that the labels have wrapped as we expect and our stack view has also changed its orientation to vertical, to accommodate the longer buttons.
Now I have also set up a first generation iPhone SE here with the largest non-accessibility Dynamic Type set. You can see that even in English the layout gracefully adjust to accommodate the narrower screen width. I can open the Accessibility Inspector, from Xcode to change the Dynamic Type settings to the default, and as you can see the layout reverts back to the horizontal layout as expected. I can also use Xcode's environment overrides feature to do the same thing.
We just saw a lot of the design best practices applied in the demo, as well as some tools that Xcode provides for you to make it easy to test your app in other languages and validate the layout works well. Let's recap some of the tools that we used in the demo to achieve a localization-friendly layout and see additional ones that are at your disposal. As we saw in the demo the document preview allows you to preview your UI adjustments immediately without having to build and run your app. It supports the Pseudolanguages, as well as any localizations that you have in your application, as we will see shortly. Scheme options allow you to runtime test your app in specific languages, including the numerous Pseudolanguages. To quickly change Dynamic Type settings and verify that your app adapts to them accordingly, you can add the Dynamic Type widget to Control Center on iOS, as well as use the Accessibility Inspector and Xcode's Environment Overrides feature with devices that are attached to your Mac. There are additional tools that Xcode provides to make your job even easier when implementing your layout.
Just like in your source code, Interface Builder provides fix-its with quick solutions to common problems like sub-optimal or missing constraints when using Auto Layout. The "Embed in" feature in Interface Builder allows you to adopt modern container views for multiple controls that automatically provide the correct constraints for a wider variety of scenarios, including Stack Views and Grid Views. Now let's jump into Xcode to see auto layout fix-its and the Embed in functionality in action. Here I have a XIB for a Mac app in which I have not added any Auto Layout constraints yet.
You can immediately see that there is a warning icon in the document sidebar for the top level window. If I click it, I will see multiple localization issues being reported about missing constraints. If we open up the document preview and choose the Double Length Pseudolanguage, we can see that none of the controls grow to accommodate the longer text. If we build and run the app, we can also see that the UI does not flip in Arabic. Going back to the list of issues, perhaps seeing so many at once is a bit overwhelming, and I want a quick way to resolve them all. If I click the Resolve Auto Layout Options button in the bottom right of the canvas, there is an option to Add Missing Constraints for all views in the window. So let me try that. Now all the controls have constraints, but I still have a bunch of localization warnings.
Let's look at the one about the Publish button. We don't really need this Fixed width at all, and it was added because the button happened to be a little larger than its intrinsic content size. The natural size of a view that takes only its contents into account. I can go ahead and click the yellow triangle and choose the Remove Constraint fix-it to resolve this issue. Next, I have the OK button with a width of 70. In some languages, the word for "OK" could be much longer. So we do not want to restrict the with here. However for languages like English with very short translations we don't want a tiny button, either. Conveniently, the fix-it popover provides an option to do just what we want. Choose the "Set Constraint to Greater Than Or Equal To A Minimum Width" to get appropriate behavior for all languages.
Next, let's make sure that the Cancel and Publish buttons can't overrun each other.
This is highlighted by the trailing constraint is missing warning for the Publish button. We can choose the "Fixed Leading and Resizing Trailing Constraints" option here as we want the space to the right of the button to be flexible in this case. Finally, we can resolve all of the misplaced views by clicking the windows content view and clicking the update frames button in the lower right of the canvas. Now I'm down to just three localization warnings.
I could go ahead and resolve them here but I will take the opportunity to illustrate the Embed-in functionality in Xcode instead. The entire top part of this dialog looks a lot like a spreadsheet, so let's try embedding it in a Grid View, which is specifically tailored for these kinds of layouts. Before we start I will fix the width and height of the icon because I do not want it to change size. Let's also add a height constraint to the description input field, since we always want it to be two lines tall.
We will do this by control-click and dragging over the TextField and choosing Height from the pop-up menu. Finally the vertical constraint between the image and the Publish button is no longer needed so we can delete it.
Now let's select all the text fields and the checkbox. Click "Embed-in" in the bottom right and choose Grid View. The layout of the controls is now managed entirely by the Grid View, so they don't need any explicit constraints.
Of course, I must still constrain the Grid View to its sub-lengths, so let me do that by adding standard space constraints on the top, leading, and trailing edges and set the bottom space to 27. Using standard space gives me spacing recommended by the Human Interface Guidelines which automatically changes based on the context, avoiding the need for hardcoded values.
The bottom space of 27 was given to me by my designer, so that's what I will use. Our OK button used to be trailing aligned with the TextField above it and that went away when we put it in the Grid View. So let's add it back by control-click and dragging from the OK button in the canvas to the Grid View in the document sidebar, and choosing trailing from the menu that appears. Next, I need to merge the cells in the first row so the title can span both columns and move the Ignore alerts checkbox from the first column to the second.
First, let me select the top two cells in the document sidebar by holding down the command key as I click them. Then choose Merge Cells from the Row Options menu in the canvas. To move the checkbox, I will simply drag and drop it from one cell into the other. The basic structure is now done, but the lab doesn't quite match what I want. First, lets adjust the Grid View attributes to match our expectations. I will select the Grid View and then show the attributes inspector. Since we have text in our Grid View, we should set our rule alignment attribute to First Baseline. We also want to set the xPlacement to Fill so that all the available space is taken up by the content inside. The spacing should match standard spacing implying row spacing of 12 and column spacing of 8. And we still have an unnecessary width constraint on the name label. So let's go ahead and remove that.
We also don't want the first column to be any wider than it needs to be.
So let's set the horizontal hugging priority of the name and description labels to 749 causing the Grid Views column to hug them tightly. We choose 749 because it is lower than the labels default compression priority of 750, but higher than the windows holding priority of 500. Finally, set the vertical content hugging priority of the Grid View to 600 so that our window cannot be resized vertically. Looks like we have misplaced views again, but we can quickly resolve that by clicking the Windows Content View, and then clicking the update frames button in the lower end of the canvas. Now let's open up the document preview and see our outlooks in different languages. Greek. Chinese.
And Double Length Pseudolanguage. All look great. Now let's do a quick runtime test in Arabic as well. No issues. Looks perfect.
Before we wrap up, I wanted to call out the importance of user testing for delivering a high quality localized app.
Of course, no amount of automation and scrupulous testing can replace human driven testing by native language speakers. These testers will be able to immediately spot glaring issues in your application. Such as inconsistencies with OS terminology, truncated and clipped text, which might not be obvious to a non-native speaker as well as out-of-context translations which no automation or fix-its can determine. This testing is particularly critical if you're adding new localization or making big UI changes to your app. Customers will be very excited to see their app is finally localized into their native language, and making a good first impression will do wonders for building a good relationship and reputation for your brand in a new market. In summary, as international customers are the majority of your customers, it is important that you're aware of the impact localization will have on the design and layout of your app. By following the localization friendly design patterns that we have discussed and demonstrated, you can make non-disruptive adjustments to your app to make it truly world-ready. Finally, UI testing by native speakers of the localizations can make a significant impact in the quality of your localized apps and the impression they leave on users.
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.