Discover how to develop your app so that it can be localized into "right-to-left" languages such as Arabic and Hebrew. We'll take you through important considerations for these languages, share solutions to challenges, and provide best practices for delivering a great right-to-left experience in your app.
Rich Gillam: Hi, I'm Rich, and I'm here to help you get it right to left. So you’ve already localized your application for a bunch of languages, including the most common European languages, the most common Asian languages. And now you want to localize it for Arabic and Hebrew. This is a good choice; Arabic is one of the ten most used languages on our platform, but it brings with it some challenges you don’t face when developing for other languages. That’s what this talk is about, how to develop your application so that it can be localized into languages such as Arabic and Hebrew. Arabic and Hebrew are the most commonly used of the so-called “right-to-left languages.” Why are they called this? English, French, Chinese, Thai, and many other languages are written so that their characters run from left to right like this. In Hebrew, the characters run from right to left like this. They do the same thing in Arabic, and in Arabic, the characters are also cursively joined. The four letters in “salaam” look like this when they're written separately.
It’s not just Arabic and Hebrew, by the way. Apple actually has font and keyboard support for 15 right-to-left languages. Here’s a paragraph of Hebrew text. This one is from the Hebrew version of the “Formulas & Functions Help” page for Numbers. Notice that the text is aligned on the right and ragged on the left, and that many lines, including the last one, have punctuation on the left-hand side. There’s also a number in this paragraph. The number still goes from left to right. This one is telling us that Numbers supports over 250 functions. If we widen our view to include another paragraph, we see that this one has some English words, the names of iWork and its constituent applications, Pages, Numbers, and Keynote. These are also written from left to right, even in a Hebrew paragraph. So for many paragraphs, the text is bidirectional. This is an inherent property of Arabic and Hebrew, and it’s why they’re also often referred to as the “bidi” languages.
If we widen our view even further, we see the entire page is laid out from right to left. We have a table here, and the text is to the right of the images instead of to their left.
And if we widen it even further to see Safari’s window frame, we see that it doesn’t stop with the content. Lines of Arabic and Hebrew text begin on the right and progress to the left, so it’s natural to expect other UI elements to do the same. Just as readers expect things to start on the left and progress to the right, Arabic and Hebrew readers expect the opposite. So here, Safari’s toolbar runs from right to left with the traffic light buttons in the upper right corner and the buttons progressing to the left.
And if we widen our focus to include the entire screen, we see that it extends everywhere. This is Numbers’ help screen, and we see that all of the elements in Numbers have also flipped. The sidebar is on the left. The tab bar runs from right to left. Even the document itself is flipped. And the Mac menu bar and dock run from right to left as well. Getting all of this right can be complicated, but the great news is that we do most of the heavy lifting for you. Most support for right to left comes for free. But there are things to keep in mind. So that’s what we’re gonna talk about, what the system does for you, when you need to opt in or out, and what to think about when implementing your own right to left support. We’ll talk about text, images, control orientation, and UI layout. We'll also cover displaying numbers in Arabic. And finally, we’ll talk a little about how to test that your app is handling right to left correctly. So let’s dive in. First, we’ll talk a little bit more about how text works and introduce some terminology. Let’s start with the concept of writing direction. As we’ve already seen, English is written from left to right, and Hebrew is written from right to left. But what happens if we mix them? If you’ve got a multilingual sentence like these, the individual components still keep their writing direction, but this means that each of these sentences now consists of three components: Two separate snippets of text in the native language sandwiching one snippet in a different language. When we talk about the writing direction of a paragraph, we’re talking about the order of these individual snippets. When we say the English sentence has left to right writing direction, it's because these three boxes run from left to right, and when we say the Hebrew sentence has right to left writing direction, it’s because those three boxes run from right to left.
A different but related concept is text alignment. If you read a left to right language, your eye goes to the left-hand side of the page and progresses to the right. If you read a right to left language, it’s the opposite. So text gets aligned on the right-hand side in right to left languages. The good news is that most of the time, you don’t have to worry about either of these things. Not only does CoreText take care of arranging all of the characters properly on a line or paragraph of text, including when text of different directions is mixed on one line, but all of our UI frameworks automatically set the writing direction and alignment as well. All UI widgets default to something called “natural writing direction" and “natural alignment." The writing direction of a text widget defaults to match the normal writing direction for the user’s UI language. That is, if your UI is in Hebrew or Arabic, the writing direction of your text widgets defaults to right to left. Natural alignment follows the writing direction. That is, if the writing direction of a text widget is right to left, it’ll also be right-aligned. Most of the time, this is what you want, but you can override the defaults. We’ll talk more about this in the section on control orientation. Now seems like a good time to start a little chart of terms you’ll run across. What we’re saying here is that natural alignment corresponds to left alignment in left to right languages and to right alignment in right to left languages. We’ll add to this chart as we go. But of course, it’s not just text you have to worry about. Having text that’s read in the opposite direction has a profound effect on elements of your application other than text. Let’s talk about how it can affect icons and other pictorial elements.
This is the Pages toolbar in English and in Arabic. Let’s take a closer look at some of the icons in the toolbar. Many, such as the “Add page” and “Media” buttons, look the same in both languages. This is either because they’re symmetrical or because their directionality isn't tied to the language, and they look fine in both languages. Other buttons, such as the “View” and “Document” buttons, flip to their mirror images in Arabic. The “View” button brings up the Pages sidebar, which appears on the opposite side of the window in Arabic, so the icon has to change to reflect that. The “Document” button changes to show that if the user is writing in Arabic, the pages turn in the opposite direction. And you can have situations where the icon changes completely. The letter on the “Insert text box” button changes to a different letter to reflect the user’s language.
Once again, the great news is that a lot of this gets handled for you. You have to think about image orientation a little more than you generally have to think about text writing direction, but getting things right is fairly simple. This is the “View” menu in Mac Pages in English and Arabic. Notice that the “Show Ruler” icon reverses itself. In right to left languages, the vertical ruler is on the right-hand side. For a custom image like the ruler icon, right to left behavior is controlled in the Xcode imageset editor. If your image is the same in left to right and right to left, you don’t have to do anything special. The system can also algorithmically mirror your image for you when your app is running in a language with the opposite writing direction from your development language. You can ask for this feature in Xcode’s imageset editor. In the sidebar, you’ll find a control marked “Direction.” If you click on this, you get a menu with four choices. If your image is the same regardless of the UI language, you pick “Fixed.” If you want algorithmic mirroring, you opt in by picking one of the “Mirrors” options. Which one depends on the writing direction of your development language. If you have an image you can’t mirror algorithmically, either it has multiple elements that behave differently when the UI changes direction, or you’ve got shading you don’t want to move, you pick “Both," and three new wells get added to the image set editor, allowing you to create separate images to use for left to right and right to left contexts.
But if you use images from SF Symbols, as the Pages toolbar did, things are even easier. Almost all of the work has been done for you. The symbols that need to change for right to left languages do so automatically. Consider this bulleted list icon for example. For many images, SF Symbols’ sidebar has a "localization" section that shows localized versions of the image you chose. In this case, the bulleted-list icon has different left to right and right to left versions. It mirrors automatically.
The localization feature can go beyond just mirroring for right to left languages. Here’s the localization tab for the “insert text box” icon. It has localized versions not just for the Latin and Arabic scripts, but for a variety of others as well.
One case that’s specifically important for Arabic support is the question mark, which is often used as a “help” icon. In Arabic, the question mark is the reverse of the Latin question mark. Using icons from SF Symbols for help icons gets you this version without any extra work.
One class of images you have to think about carefully is arrows and other directional indicators. Here we have four arrow-in-a-circle icons. You’ll notice they’re in two pairs. You have two pointing to the left and two pointing to the right. If we look at just the two that point to the left, you’ll see that one is called “arrow.backward.circle.” This one flips to point to the right in right to left. The other one is called “arrow.left..circle” and does NOT flip for right to left. SF Symbols follows this naming convention throughout with icons that you may or may not want to have flip for right to left. The “forward” and “backward” ones flip, and the “left” and “right” ones don’t. If you’re using an arrow or other shape to convey the idea of “forward” or "backward," you want to use the flipping versions, and if you’re using the arrow to convey an absolute direction, you want to use the non-flipping versions.
And let’s add a row to our “Terminology” chart. When choosing images in SF Symbols, remember that “left” and “right” always point those directions and “forward” and "backward" point in different directions depending on the UI language. Now let’s talk about how controls and other UI widgets are handled in right to left. This is the Mac Keynote sidebar in English and Arabic showing the format inspector for a shape. Notice that everything has flipped its appearance for right to left. We have lots of pop-up menu buttons where the menu indicator moves to the left-hand side for right to left. We have a couple of checkboxes, where in Arabic, the checkbox is to the right of the label. The opacity slider has changed for Arabic so that the minimum is on the right and the maximum on the left. And so on throughout all the other controls in this inspector. The great news is that you get this behavior for free. All of the standard UI controls in all our UI frameworks automatically reverse their appearance for right to left languages. There are, however, situations where you may not want this or where you need to have some control over how it happens. Let’s take a look at a few of the interesting cases.
Let’s talk about buttons with both a textual label and an icon on them. This is the Keynote animation inspector showing the controls for the “Move In” animation. This inspector has two buttons with both a label and an icon. Notice that the arrow on the Preview button flips with the change in UI direction, but the arrow on the animation direction menu doesn’t. Both flip sides with the UI direction, but if the direction control was a group of buttons instead of a menu, you can image maybe not wanting it to change sides.
To show how to control this, I've isolated those two examples out into a small toy application.
Here’s the code to build that UI in SwiftUI. There are a few interesting things to note here. We’ll look at them one at a time. Let’s start with the image names. As we saw before, for images from SF Symbols, you choose either an icon that reverses or one that doesn’t. Here we’ve used “arrowtriangle.forward.fill” for the “Preview” button. The “forward” in the name tells you that it flips for right to left. We’ve used “arrow.left” and “arrow.right” for the direction buttons. The “left” and “right” in the name tell you that they don’t flip for right to left.
If you’re working in AppKit or UIKit, this works the same way. Here’s my app in Xcode’s storyboard editor with the “Preview” button selected. You control the button’s icon with the “Image” control in the Attributes inspector.
And in code, you set this with the button’s “image” property, and it works basically the same way in both AppKit and UIKit.
Coming back to our SwiftUI example, the next question is how you control which side of the label the icon goes on. You do this by setting a label style. The built-in TitleAndIconLabelStyle puts the icon before the label in the user’s reading direction. We can use this for the “Left” button. For the other two buttons, we want the icon to go after the label in the user’s reading direction. To do this, you need a custom label style, but that’s pretty easy to do. Your label style’s makeBody() method just has to make an HStack and add the title and icon into it. As with any HStack, the order you add them determines the order they display, and the order automatically reverses when appropriate for the UI direction. This technique works on any view that can take a Label, not just buttons. Of course, you don’t want the icon to change sides on the “Right” button. You want it to always be on the right regardless of the UI direction. That brings us to the last interesting thing in this code snippet. Views in SwiftUI pick up their directionality from the SwiftUI environment, which you can modify. You do this by adding an “environment” modifier to a view and giving it the key and new value for the property you want to change. Here we’re overriding the environment’s layoutDirection property to always be left to right regardless of what value we might be inheriting from our parent. Changing the environment in this way works on all SwiftUI views that respond to the user’s UI direction. Notice we applied the modifier to the HStack that contains the “Left” and “Right” buttons. Any changes you make to a view’s environment are inherited by its child views, so putting it here not only keeps the HStack from reversing the order of the buttons, but it keeps both buttons from reversing the layout of their labels. And of course, we didn’t apply our environment modifier modifier to anything in the parent chain for the “Preview” button, so it still reverses when appropriate, just as we want it to.
So to recap, the “Left” button has its icon on the left because we used the built-in TitleAndIconLabelStyle, and the “Preview” and “Right” buttons have their icons on the right because we used a custom label style we called IconOnRightLabelStyle. The “Left” and “Right” buttons don’t change their order or the internal arrangement of their labels because we added an environment modifier to the HStack that contains them, setting the layout direction to left to right. The “Preview” button reverses the internal arrangement of its label because it doesn’t have that modifier.
This works differently in AppKit and UIKit. In both of those frameworks, the position of the icon relative to the label is controlled with the “Position” control in Xcode’s Attributes inspector. If you click on this control, you’ll see that the menu has, among other options, two pairs of options that align the label and icon horizontally. You have “Leading” and “Left,” and you have “Trailing” and “Right.” “Leading” and “Trailing” change their meanings based on the UI direction, and “Left” and “Right” don’t. In AppKit, you control this with the button’s imagePosition property. In UIKit, it’s the imagePlacement property on the button’s configuration, which might mean you need to set your button’s configuration first. The icon on the “Preview” button changes sides because we set its position to “Trailing," and the icon on the “Right” button doesn't change sides because we set its position to “Right.” This also lets us fill in the last row in our “terminology” chart. The terms “leading” and “trailing” will come up a lot when discussing UI layout. Like “forward” and “backward,” you’ll often see them used in contrast to “left” and “right.” The “leading” edge of something is the edge closest to beginning of the line or to the side of the screen or window where the reader would begin reading, left for left to right and right for right to left. The trailing edge is the opposite side, closest to the end of the line, right for left to right and left for right to left. Most of the time, you want to use these instead of “left” and “right,” saving “left” and “right” only for things that are tied to an absolute direction.
Let’s look at another interesting case. This is part of the text format inspector in Keynote on the iPhone, in English and in Arabic. This particular screenshot has four segmented controls. The top two, the page selector for the inspector and the standard “bold/italic/underline” style buttons, reverse the order of their segments depending on the UI language. If you don’t read Arabic, you’ll have to trust me on the page selector. As with the other controls we’ve looked at, this is the default; you get this behavior for free. The other two segmented controls, the alignment controls, don’t reverse the order of their segments. This is because they move things in absolute directions. Left alignment is left alignment regardless of whether that’s the beginning or the end of a line. Let’s look at how we keep these controls from reversing. We already know how to do this in SwiftUI. You just apply an “environment” modifier that changes the environment’s layoutDirection property to left to right. Here, we’re using this technique to keep the alignment control from reversing itself, but letting the style control reverse itself as it normally does. In UIKit, this works differently. Here’s a toy application in Xcode designed to simulate that segmented control behavior. I have two segmented controls, one that mimics the bold/italic/underline behavior and a second one that mimics the alignment control. The alignment control is selected. In the attribute inspector, you’ll find a menu labeled “Semantic.” If you click on that menu, you get five choices. This menu controls something called the semantic content attribute. You use this to say what kind of control this is, and the system uses that to determine if it reverses its appearance based on the UI direction. The default is “Unspecified," which causes the control to reverse its appearance. "Playback" says the control is a media playback control or part of a group of playback controls. "Spatial" says the control is a spatial control or a part of a group of them. Spatial controls move things around in space in absolute directions. And finally, you can force the control to always lay itself out left to right or right to left. So the bold/italic/underline control reverses its segments for right to left because its semantic content attribute is set to “Unspecified,” and the alignment control doesn’t reverse its segments because its semantic content attribute is set to “Spatial.” The great thing about this is that it doesn’t just work for UISegmentedControl. All UIViews have a semantic content attribute, and it controls all of that control’s right to left behavior. For any standard UIKit view that has subcomponents, the semantic content attribute will determine whether the positioning of that view’s subcomponents reverses based on the UI language.
In AppKit, you do this kind of thing differently. For all NSControls, the Xcode attributes inspector contains two menus marked “Layout” and “Mirror.” The “Layout” menu corresponds to the control’s userInterfaceLayoutDirection property, which says whether the control should use left to right or right to left layout. You don’t normally change this when working in Interface Builder. Instead, you use the “Mirror” menu. Setting it to “Always” causes the userInterfaceLayoutDirection, and thus the control’s layout, to be flipped when the nib is loaded when the user’s UI language is right to left, and setting it to “Never” defeats this behavior, keeping the layout the same. You keep the layout of the alignment control the same by setting this value to “Never”. If you’re not working in Interface Builder, you accomplish the same thing in code by setting the control’s userInterfaceLayoutDirection back to left to right directly. userInterfaceLayoutDirection, by the way, is a property on NSView, but appears in Interface Builder only on instances of NSControl, so if you want to reverse something that’s not an NSControl, you need code like we’re showing here.
Before we move on, I want to talk about text a little more. This is the “Set document password” dialog in iWork for the Mac. In the Arabic version, you’ll see everything has reversed. But notice what’s happened to the labels. In English, they were right-aligned so that they’d be close to the edit-text fields. In Arabic, they’re left-aligned. In other words, you have the opposite of natural alignment, trailing-edge alignment, if you will. Getting this layout in SwiftUI on the Mac is trivially easy. Just use a Form to gather the text fields together. But this can get interesting if, as in our example, one of the labels is multiple lines. If we expand that last label out to be two lines, we get this. The two one-line labels are correctly right-aligned, but the two-line label isn’t. The thing is, the bottom label really is right-aligned. It’s just that its bounding box is right-aligned, not the individual lines of text within that bounding box. You fix this by adding a multilineTextAlignment modifier to the last label. Text alignment in SwiftUI only comes into play on text objects that are more than one line long. For single-line text objects, their bounding box tightly encloses the text itself, and you align it by aligning the entire text object. Also notice that whether you’re aligning the text’s bounding box or multiple lines of text inside the bounding box, you have a choice of leading and trailing alignment, which change meaning based on the user’s UI direction. To keep the alignment the same regardless of UI direction, you use an environment modifier to change the environment’s layout direction as we saw earlier.
In UIKit, text is naturally aligned by default, but you can change it to one of the absolute directions when necessary. In Interface Builder, the control looks like this and corresponds to the textAlignment property on UILabel and UITextView. The button on the far right with the dotted line gives you natural, or leading-edge, alignment. The alignment of the label will follow the label’s semantic content attribute. The other buttons give you fixed left, right, or center alignment regardless of the UI direction or the label’s semantic content attribute. There’s no built-in setting for trailing-edge alignment. You have to do that in code. In AppKit, it’s a little different. You still have the alignment control, and it works basically the same as in UIKit, but the way it interacts with userInterfaceLayoutDirection is different. If you have “Mirror” set to “Automatically,” and the system sets userInterfaceLayoutDirection to right to left, the meanings of all the alignment settings reverse. So if “Mirror” is set to “Automatically,” left alignment is really leading-edge alignment, and right alignment is really trailing-edge alignment. So we’ve talked about how all of the standard UI widgets automatically reverse their layouts to match the user’s writing direction, but it’s fairly easy to prevent that when necessary. This extends to arranging individual UI widgets on the screen. If you’re using one of the standard views or view controllers that handles the positioning of their subviews, all of them automatically reverse their layouts when necessary without you having to do anything. Table views and collection views handle scrolling correctly in right to left languages too. UINavigationController automatically changes the directions of its segue animations to reflect the user’s writing direction, and changes the “back” button to match it as well. And UIPageViewController automatically reverses the paging direction and the meanings of the swipe gestures automatically as well. You generally won’t want to override these things, except for when you’re using a stack view to position child views, but all of the views honor their semantic content attribute and use it to tell them how to lay out their subviews. The same goes for the standard AppKit views, and again, table and collection views handle right to left scrolling as well. The views all honor their userInterfaceLayoutDirection property in determining how to lay out their subviews, although Interface Builder doesn’t let you set it. You have to do that in code. The standard SwiftUI views also reflect the environment’s layoutDirection property.
If you’re using Auto Layout instead of stack and grid views to lay out your view, Auto Layout also automatically reverses things to account for the UI direction. If you have horizontal constraints, you’ll see that they automatically connect things to the leading and trailing edges, and as we’ve seen, "leading" and “trailing” have different meanings depending on the UI direction. You can set Auto Layout constraints to absolute left and right directions, though, if you need to. You do this by clicking on the direction for one side of the constraint and turning off “Respect language direction” in the menu that pops up. That’ll change the direction of both ends of the constraint from “leading” and “trailing” to “left” and “right.” There are a lot of different ways to set up Auto Layout constraints in code. Here is one of them. Pretty much however you do it, the thing to remember is is to use "leading" and “trailing” instead of “left” and “right,” except in the comparatively rare situations where you really want the same layout orientation regardless of the UI language’s writing direction. Okay, that was a lot to take in, so let's take a breath. The main takeaway is that we do most of the work of handling right to left languages for you and that when you need to override it, there are ways to do that. Let's take one more look at our terminology slide. Remember that "left" and "right" are always left and right and that the other terms reverse their meanings depending on the overall UI direction.
Before we wrap up, let’s take a look at one more important issue, and that’s how to display numbers. It’s not strictly a right to left issue, but for many developers, Arabic is the first language they’ll localize for that uses different digit characters than the ones used in English. Here’s what those digits look like. There are lots of different naming conventions for the digits, but I'll call the ones used with most European languages “Latin” digits and the ones used with Arabic “Arabic-Indic” digits. There are other languages that have their own digits. These are the Devanagari digits used with Hindi; Hindi is the other common language that uses different digits. One important thing to keep in mind is that neither Arabic nor Hindi always uses their native digits. For Arabic, it depends on the country, with some, such as Saudi Arabia, using native digits and others, such as the United Arab Emirates, using Latin digits. Individual users can also choose their preferred digits. For Hindi, we use Latin digits by default, but users can elect to use native digits instead.
You already know that constructing UI strings like this is a bad idea. The string is hard-coded and can’t be translated, the message doesn’t change to handle plurals, and so forth. But another reason this isn’t good is that the value of “peopleInChat” will always get rendered with Latin digits. You probably also already know that the solution to that is to use the “localized” init method on String, which will look up the actual string in your application’s bundle and handle plurals properly if you have a stringsdict file. The great news is that it also handles numbers correctly. The value of the “peopleInChat” interpolation here will be rendered with the correct localized digits for the user’s locale and preferences. This also works right with text views in SwiftUI. The text views initializer will also render any string interpolations with properly localized digits. Always use String(localized:) when constructing user-visible strings. Many of the other APIs on String that can format numbers, including +stringWithFormat: and the String init function that takes a number, always use Latin digits. One wrinkle to be aware of is static strings that contain numerals, such as this one. What’s the big deal? You send it off to the translators, they translate it, and you get this. This is correct in a lot of places, but in Saudi Arabia and some other countries, you want to see this . The text is the same except for the character used for the 3. You could, of course, have separate localizations for the Arabic-speaking locales that use Arabic-Indic digits and the ones that use Latin digits, but nobody does that, and it’d be wasteful. Worse, in both Arabic and Hindi, the user can choose the digits they want to use, so you’d be having to choose a localization based on the user’s preferences, not just on their locale. The solution is to still just have one Arabic or Hindi localization, but to substitute the number in at run time, even though you know the value at compile time. In Swift, you can just use a string interpolation to do this.
If you’ve got other elements that travel with a number, their placement relative to the number can also be challenging. It isn’t even the same for all right to left languages. Notice that the minus sign and the percent sign are on different sides of the number in Arabic and Hebrew. In fact, it doesn’t have to be a right to left language at all. Notice that in Turkish, which is a left to right language, the percent sign also goes on the left. And, of course, keep in mind that if you’re using native Arabic digits, they use a completely different percent symbol. In other words, you really don’t want to do this kind of thing, where you’re appending the percent sign, or currency sign, unit abbreviation, or whatever yourself. Instead, use a number formatter to add the percent sign, or currency symbol, or whatever. In Swift, this is easy to do with the formatted() method on all the numeric types. If it’s part of a larger string, as in this example, String(localized:) will also make sure that the formatted number-- or anything else substituted into the string at runtime, including other strings, is surrounded with markup that’ll keep the writing directions of the formatted number and the surrounding message from messing each other up. Finally, I want to leave you with one tip for testing your app to make sure that you’re doing the right thing for right to left.
You don’t have to have Arabic or Hebrew localizations in your executable to test your app in right to left. You can actually test right to left behavior in your development language. To do this, bring up the scheme editor in Xcode. Now go to the Options tab and look for the “App Language” menu. At the bottom of this menu are a bunch of “pseudolanguage” options. These are fake languages that transform your UI in various ways to allow you to check for localization problems without actual localizations. Pick the “Right-to-Left Pseudolanguage” option and click Run, and your app will still be in English or whatever your development language is, but the UI will all have been flipped for right to left. And that’s all I have. Localizing for right-to-left languages involves attention to some issues relating to the change in writing direction, but the system does most of the heavy lifting for you. There are cases, usually around absolute directions, where you may want to opt of this behavior, and that’s always possible. And remember that not all languages use Latin digits to render numbers. Keep these things in mind, and it shouldn’t be hard to get things right to left.
myLabel.string =String(localized: "There are \(peopleInChat) people in this chat.",
comment: "Label indicating number of chat participants")
Text("There are \(peopleInChat) people in this chat.",
comment: "Label indicating number of chat participants")