How We Translated the Dodo Pizza App into Arabic
Kirill-Orloff
Posted on May 17, 2023
What do you know about adding the support of right to left (RTL) languages in iOS apps? You have to use leading and trailing instead of left and right, and also… We didn’t know anything else either, but we had to puzzle it out.
We are making the Dodo Pizza App ready for localization in Arabic. In this article we would like to share our findings and tell you, why we need RTL support in the app, why it is not enough just to adapt the layout in code to support RTL, why we had to redraw the illustrations, and in what way the Arabic percent sign is different from the European one. Besides, we will show a lot of screenshots and will share some tips on RTL support in code.
Breaking news! Dodo has opened a pizzeria in Dubai. For the developers it means that they have to make the app ready for launch in a new country. The major part of work here is usually for the back-end developers, and we just have to add a new language in the app.
Localization has already been set up for a long time, but the launch in Dubai is very special. App localization for Dubai is different from the localization in all previous countries because in Arabic words are written from right to left. It means that people read from right to left. And all the content is perceived from right to left. Their gaze direction changes. Which means that in the app…
First, enable RTL
We began with googling how to do it with examining the problem and searching for possible solutions.
Apple provides good material on this topic to begin with:
- Design for Arabic WWDC22,
- Get it right (to left) WWDC22,
- Right to left Apple HIG,
- Apple Documentation Supporting Right‑to‑Left Languages.
Then we decided to get the feel of RTL ourselves and we began to poke our app. If you want to see what your app will look like with the enabled RTL, this is how you can do it:
In Xcode go to Edit Scheme (option+click on current target) → Go to Options tab → In App Language select Right-to-Left Pseudolanguage instead of System Language → Restart the App
In the Xcode dropdown menu there will be two pseudolanguages: Right-to-Left and Right-to-Left With Right-to-Left Strings.
The first one will simply align all the local strings of the app to the right. The second one will reverse the letters.
Please note that if you choose Right-to-Left With Right-to Left Strings, only local strings will change. The strings which come from the backend will be displayed as they are sent.
It’s a bad idea to look only at your own app because you lack visual experience of RTL-apps. To develop this skill, we looked at various adapted apps and websites.
After we got used to RTL a bit and studied everything in our app, we started drafting a document with screenshots of all the screens. We took two screenshots of each screen (a regular one and one with enabled RTL), and then for each screen we described what wasn’t functioning properly. When we had done it, we realized that a lot of problems appeared repeatedly from screen to screen. We grouped them together. Further we will talk about each group of problems.
Surprise at Xcode work with RTL-strings
Bonus section. Check it out, what Xcode does with RTL-strings:
On the video we are always pressing the right arrow key.
For comparison, in Android Studio the way of behavior is quite different. In the studio the cursor always moves in the direction of the arrow key you press on the keyboard. Maybe this can be tuned somewhere, but we didn’t check.
Checking what’s broken in the app
Let’s go over the groups of problems.
Layout of views and cells
When adapting the app to RTL-languages, iOS does most of the work for us. There are screens in the app which already look the way they should when RTL is enabled.
The major group of problems arouses when the views are laid out in such a way that iOS cannot reverse them correctly itself.
You have to go to each of such views, clarify why the problem occurs, and fix it manually.
For example, have a look at the purple widget of an active order:
The title and the main text need to be aligned to the right; the picture and the main text need to be swapped around.
What the mistake is: the widget of an active order is made up on the frames which do not take RTL-orientation into account. We prefer not to use the layout on frames, but in this case it was justified by the requirements to the widget.
For the widget to be displayed correctly, you need to know the current orientation with the help of property view.effectiveUserInterfaceLayoutDirection, and to recalculate the frames for RTL-orientation.
The views with nutrition value and the ingredients that can be excluded got completely messy:
What the mistakes are:
- in the nutrition value views all the titles had left alignment, and all the values had right one. You need to take RTL into account when setting textAlignment;
- in the ingredients views the button “Close” had alignment = .left, and the list of ingredients was laid out on frames.
The adaptation of the app for RTL has become a good reason to take a look at all the screens and to make sure that all the used components are in line with the design system. Thus, for example, we have completely changed the dialogue of ingredients deletion.
The checkout doesn’t look very good either:
Where the mistake is: it seems that the screen is completely out of order, but in fact it is not so bad at all. It’s just that there was a wrong textAlignment for UILabel, and the pictures weren’t mirrored.
On the list of countries the flag should be shown first, and then the name of the country. It means, that in RTL the flag should be to the right of the country.
What the mistake is: flag is an emoji. In the code it was like this:
var nameWithFlag: String {
"(isoCode.emoji) (name)"
}
Pay attention to the button “Show nearest pizzerias”:
What the mistake is: the button had hardcoded left and right imageEdgeInsets on the storyboard. We changed them to NSDirectionalEdgeInsets, for which you can set leading and trailing insets instead of left and right.
Besides that, there were lots of places with repeated errors in the code:
- for UILabel it was set: textAlignment = .left or textAlignment = .right. Depending on the scenario, you have to use textAlignment = .natural or set textAlignment in accordance with the value of effectiveUserInterfaceLayoutDirection. In case you use .natural, if LTR-orientation is set for the user, the text will be aligned to the left, and if RTL – to the right;
- in some places constraints left and right were used instead of leading and trailing;
- a couple of screens were made up with the help of PinLayout and they did not support RTL.
Custom UI elements
It happens that the developers have done everything right: they used leading and trailing so that everything works right if suddenly there is RTL. In some places of the project we have faced the bugs connected with it. This is what happened to the field of phone number input:
What the mistake is: iOS functioned right – it automatically reversed the field for phone number input because we used leading and trailing constraints in the layout. But we need this field to be always displayed from left to right, because phone numbers are always written from left to right. The easiest way was to fix the fields with semanticContentAttribute = .forceLeftToRight.
It’s funny how the input for entering SMS code broke. As you enter, the numbers appear in it as usual (from left to right), but the dots in the background disappear from right to left. On the screenshot, 3 numbers have been entered and one dot remains visible – under number one.
What the mistake is: placeholder dots contained in UIStackView. The stackView automatically reverses when RTL is enabled, but you can also set up semanticContentMode = .forceLeftToRight for it.
On the Half & Half pizza, rounded scroll indicators have been mirrored and now they are in the center of the pizza:
What the mistake is: the scroll indicators had leading and trailing constraints. iOS inverted them. But the screen of the pizza made from halves is universal for RTL and LTR, that’s why there’s no need to reverse anything in it. We changed the constraints to left and right.
In the tooltip, the arrow should move to the left:
What the mistake is: sourceRect, from which the arrow is drawn, was calculated manually. Now we make different calculations for LTR and RTL.
Our SegmentedControl is custom. It looks alright, but the segments in it must change places.
What the mistake is: for each segment, the frames were calculated. We added alternative calculations for RTL.
Collections
Some collections, for example, toppings for the product, were not mirrored. On the screenshot, empty slots in the collection should be at the bottom left, not right.
Other collections, for example, categories in the menu, were mirrored (they became aligned to the right). But the elements in them were displayed in the wrong order. On the screenshot, “Pizza” category should be the first, i.e. the rightmost.
On the screen with the products to be bought for dodocoins, the elements in the collection are at the right places, but it is the last element that is shown by default. In this collection, the first category of goods is drinks. If you scroll the categories sideways, the drinks will be at the right place.
For all the problems with collections we had one solution – to use UICollectionViewFlowLayout which supports RTL. For UICollectionViewFlowLayout to be mirrored, you need to create a class inherited from UICollectionViewFlowLayout, and to redefine the property of flipsHorizontallyInOppositeLayoutDirection. By default, it is false, while it has to be true. Then collections with this layout will be mirrored.
class RTLSupportedCollectionViewFlowLayout: UICollectionViewFlowLayout {
override var flipsHorizontallyInOppositeLayoutDirection: Bool {
true
}
}
Icons with a direction
Another topic is pictures. Some of them have to be mirrored, and some of them don’t. It’s a science!
Let’s have a look at the screen of delivery method selection. The courier on the first icon should be mirrored because the icon shows the direction of motion. That is, the courier should be going towards the user, and not away from him.
The knife and fork don’t need to be mirrored. We have checked, in Arab countries they hold a knife in their right hand.
On the next screenshot, near to the delivery address, the arrow wasn’t reversed. It needs reversing.
The star on the widget needs reversing too, because people who choose Arabic, will be looking at the widget from right to left. And the star should meet the users’ view with its face and not with its back.
There’s no need to reverse the pizzas as it is a copyrighted photo. The photographer created a composition and things like that. Such photos should be left as they are.
At this stage, it is necessary to have a look at every single picture in the app and to decide if it needs reversing or not.
For the icons which need inverting you can set a special setting:
let image = UIImage(named: "some-image")
imageView.image = image?.imageFlippedForRightToLeftLayoutDirection()
This code checks the selected orientation and tells imageView to reverse the picture.
In Apple's documentation you can find a code which mirrors the picture itself via NSAffineTransform. But we decided not to use this option.
Not every picture can be automatically mirrored. For example, we have pictures with Dodo’s logo (orange letter D and a bird fitted into it). If you mirror it, the logo will be reversed – which is wrong.
The pictures which cannot be automatically mirrored, have to be redrawn and put into the project. For this asset you can set up the localization:
Badges on the photos in the menu
Badges on the photos should be moved to the other corner. In RTL they should be located in the left upper corner of the photo.
Now the badges are a part of the photo. We are planning to make them drawn in the app. Then we will be able to change their location when RTL is enabled. In the meantime, this is another reason not to reverse the photos.
Animations
All animations have to be doublechecked too. We have few of them, but the ones we have function incorrectly in RTL.
Add to Cart Animation
One of the things which changes its direction when RTL is enabled is UITabBar. The first tab will be located on the right, and the last one – on the left.
Let’s take a look on how Add to Cart animation works. The item always goes to the rightmost tab of the tab bar. While it should go to the cart.
Order Status Animation
The loading animation should go the other way.
Third party
Don’t forget to check third-party services. This is what we have in our case.
There is a text on the broadcast from Ivideon. This text is center aligned, and the text itself can be changed in the broadcast settings. So, there are no problems.
But the chat and captcha are not at all adapted for RTL. We won’t be able to fix it ourselves, which means we will have to discuss potential improvements with the SDK developers.
WebView
We have WebViews in the project. They open links to our website, Google docs, social networks, etc.
You don’t need to do anything with the WebViews as such, but you need to remember to attach links to the localized pages.
Trying to understand, what is a bug, and what is a feature
When we enabled RTL in our app for the first time, our brains broke.
It is very difficult and unusual to perceive. Everything looks weird and you don’t understand if it looks bad because it is unusual or because it is broken.
And something looks fine, and you don’t understand if you just got used to RTL, or if it looks OK because the element is displayed in a wrong way and it is aligned from left to right.
There were a few places where we first tensed up, but then it turned out that everything was fine. In this section, we will talk about such places.
Input fields
The first one is input fields.
We switched the app to RTL and started clicking on the input fields to write something. The text appeared on the wrong side of the caret, and the caret itself didn’t move. We got upset that we would have to fix it.
It turned out that if the input field is in RTL mode, and you start typing English letters, they appear to the left of the caret, while the caret itself remains in place.
But if you start typing Arabic letters, they appear to the left of the caret, and the caret itself moves to the left.
That’s the way it should be.
Promo-code input field
Our promo-codes contain only English letters. English letters are spelt from left to right. This means, that the input field has to be aligned to the left.
Plurals (.stringsdict)
On top of that, all the plural strings got broken in our app.
At the beginning of this article we told about two kinds of pseudolanguage in the project. It turned out that the one reversing the letters was breaking plural strings.
The bug occurs only during debugging. In production with Arabic lines everything works fine.
We are glad it’s not all broken
We didn’t need to modify anything in:
- UINavigationBar (in Android, for instance, all “Back” arrows in the navigation bar got broken);
- UITabBar;
- UIStackView;
- UIPageViewController;
- Swipe to delete in tables;
- separators in tables (in our case some separators have an inset from the screen border only on one side. It is this inset that should be mirrored. On Android it didn’t work automatically);
- input fields (UITextField, UITextView, UISearchBar);
- animations of UINavigationController (push/pop) and swipe to go back.
All the above mentioned elements are reversed automatically when switching to the RTL language: changes appear in the order of tabs, stack elements, the direction of UINaviationController animations, etc.
It was a nice surprise for us to find out that these didn’t require any modifications either:
- stars to evaluate the order;
- SDK for stories.
Dealing with obscure things
During the localization of an app into Arabic, it’s not enough to adapt the UI so that it is displayed correctly for RTL languages. It is also important to take into account some other features of the language and culture.
Eastern Arabic numerals
In Arabic, Eastern Arabic numerals are used to represent numbers. They are different from what we are used to:
There are a lot of places in the app where numbers are used: prices, grams, phone numbers, etc. In almost all the places you need to use Eastern Arabic numerals, but there are exceptions. For example, in price tags and phone numbers you need to use Eastern Arabic numerals, but for bank card numbers only Western digits are used.
You can convert the numbers with the help of formatters:
extension String {
var toLatnDigits: String? {
let numberFormatter: NumberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "en_US")
guard let latnNumber = numberFormatter.number(from: self) else { return nil }
return numberFormatter.string(from: latnNumber)
}
}
The numbers themselves are always written from left to right, including phone numbers.
By the way, in some cases, iOS proactively converts Western numbers to Eastern Arabic ones. For example, if this string was in Arabic:
"resendCodeInDSecWithParam" = "Resend code in %d sec";
Then in the formatted string you would get the number of seconds written in Eastern Arabic numerals:
let string = String.localizedStringWithFormat(
NSLocalizedString(
"resendCodeInDSecWithParam",
bundle: bundle,
comment: "Signup screen"
),
Int(self.seconds)
)
Remember to check the input fields which require the input of numbers. In our case, for example, in the field for change at the checkout you can’t enter anything but Western digits.
Starting to use Eastern Arabic numerals in the app is a big problem, which concerns not only the apps, but also the backend. We have decided not to support Eastern Arabic numerals in the first release of the app with Arabic language support. To fix the use of Western digits in the application, we use a special locale format: Arabic with Western digits (ar-u-nu-latn).
What to write on which side
Don’t forget to check what is where: units of measurement are written to the left of the number ( ١٠٠ غ), and the currency sign is written to the right (١٠٠$).
If you specify the time interval, then the end time is written on the left, and the start time on the right. For example, if a pizzeria is open from 10:00 to 18:00, it is written as follows: 18:00 - 10:00.
Words in another language
If there are foreign words in the Arabic text, they do not need to be written backward. No need to reverse the names in Russian and English. For example, PayPal, Apple Pay.
Punctuation
In Arabic, some punctuation marks and other symbols differ. For example, they use a different percent sign ٪. In our app, we have a picture with a percent sign, which we use on the screens with discounts and promotions. This is exactly the case when the picture cannot be just mirrored, it needs to be replaced with another one, with a different percent sign.
The European sign % will be understandable, but using the Arabic one is preferable.
Localization of the percent sign can be done via strings localization or using a formatter:
Not like this:
label.text = String(localized: "(percentComplete)% complete")
But like this:
label.text = String(localized: "(percentComplete.formatted(.percent)) complete")
Also, it is not customary to use signs denoting the number (№ and #) in Arabic. We, for example, use such signs to show the number of order in the app.
Familiar signs are reversed: ، ؛ ⸮.
How to adapt pictures
To make pictures of people work the way you expect them to, i.e. attract attention or arouse warm feeling, users should associate themselves with the characters in these pictures.
If you want to achieve this effect, consider using images of Middle Eastern audiences rather than Europeans. For example, you’d better avoid using characters in winter clothes and ear-flapped hats.
We are used to the idea that localization means adaptation of an app to a specific language. Recently, we have added the option of changing the language manually in the Dodo Pizza app. And while we were discussing images for the UAE, we came to the conclusion that we need to be able to show different illustrations depending on the selected country, and not on the selected language. And this is what we did for the UAE.
The code which inserts the desired image depending on the current country can be reused in other countries, too. For example, it can insert winter images where they are relevant:
Deciding on how to test
There are a lot of changes, and all of them are interface ones. We will test the RTL orientation via snapshot tests. For snapshot tests, we use the SnapshotTesting library. There you can take a snapshot by setting the desired orientation through traits:
let rtlTrait = UITraitCollection(layoutDirection: .rightToLeft)
assertSnapshot(
matching: sut,
as: .image(traits: rtlTrait),
testName: QuickSpec.current.name
)
For convenience, we have written a helper to check several snapshots for LTR and RTL with one test:
itShouldSnapshot(
configs: [.default, .rightToLeft],
matching: sut,
configuration: { sut in
let product = OrderHistoryViewModel.OrderProduct(
name: "BBQ Wings",
size: "8 pcs",
imagePlaceholder: .imgPizzaGift,
category: .pizza
)
var viewModel = OrderHistoryViewModel.PastOrderItem()
viewModel.name = "BBQ Wings"
viewModel.totalPriceString = "AED 26"
viewModel.products = [product]
sut.configure(viewModel: viewModel)
},
perceptualPrecision: precision,
size: { sut in
sut.sizeFitting(width: 320)
}
)
A bit later, we upgraded it to check the dynamic type and the dark mode.
Making conclusions
iOS makes the process of an app localization for RTL languages much easier. We can say that all we need to adapt in the app is the technical debt which has shot.
Conclusions:
- If you make the layout correctly from the start, the rest will be done for you by iOS.
- The fewer custom elements you have, the better it looks in RTL.
- The work doesn’t end with layout and translation. There are a lot of cultural peculiarities which are important to remember, too.
- The direction of the text is a feature of the selected language, but there are things that depend not on the language, but on the country. For example, illustrations in the app.
- Not all pictures can be mirrored automatically. Something will have to be redrawn.
- We are very used to LTR, so it is better to show the finally adapted app to some native speakers of Arabic, so as not to miss anything.
- Snapshot tests speed up the development and help you make sure that you haven't broken anything along the way.
- RTL support is not a one-off event, but a continuous process. Every new feature has to support RTL. So, you have to tell your team what they need to pay attention to when designing, make checklists for testing and instructions for developers.
- The UAE is a multinational country. You can’t have the app only in Arabic, English needs to be supported as well.
To discover more about Dodo IS and top QSR innovations in Dodo Brands, follow us on LinkedIn and Medium!
Posted on May 17, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.