How do you support right-to-left languages: leading and trailing, semantic content attributes, flipping images, and testing?
Predict first
A view is pinned 10 points from the leading edge, and another from the left edge, in left-to-right and right-to-left containers. Where does each land?
import UIKit
@MainActor func frameX(_ attribute: UISemanticContentAttribute, useLeft: Bool) -> CGFloat {
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
container.semanticContentAttribute = attribute
let child = UIView()
child.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(child)
NSLayoutConstraint.activate([
useLeft ? child.leftAnchor.constraint(equalTo: container.leftAnchor, constant: 10)
: child.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 10),
child.widthAnchor.constraint(equalToConstant: 50),
child.heightAnchor.constraint(equalToConstant: 20),
child.topAnchor.constraint(equalTo: container.topAnchor),
])
child.semanticContentAttribute = attribute
container.layoutIfNeeded()
return child.frame.minX
}
@MainActor func run() {
print("leading:", frameX(.forceLeftToRight, useLeft: false), frameX(.forceRightToLeft, useLeft: false))
print("left:", frameX(.forceLeftToRight, useLeft: true), frameX(.forceRightToLeft, useLeft: true))
print(UIView.userInterfaceLayoutDirection(for: .forceRightToLeft) == .rightToLeft)
}
MainActor.assumeIsolated { run() }
- leading: 10 then 140; left: 10 then 10
- leading: 10 then 10; left: 10 then 140
- All four are 10
Predict first
Does a SwiftUI row with a flipping chevron, leading text alignment, leading padding and a forced RTL environment compile?
import SwiftUI
struct Row: View {
@Environment(\.layoutDirection) var direction
var body: some View {
HStack {
Image(systemName: "chevron.forward").flipsForRightToLeftLayoutDirection(true)
Text("Details").multilineTextAlignment(.leading)
Spacer()
Image(systemName: "play.fill")
}
.padding(.leading, 16)
.environment(\.layoutDirection, .rightToLeft)
}
}
- Yes
- No, SwiftUI cannot force a layout direction
Mental model. Think of a book printed for readers who open it from the other cover. The pages, the page numbers and the arrow “next” all have to move to the other side, but a photograph of a clock must not turn backwards.
In a right-to-left (RTL) language such as Arabic or Hebrew the whole interface mirrors: the back button, the navigation flow, the order of tabs and the alignment of text all start on the right. The system does this if you use directional-neutral terms. In Auto Layout use leading and trailing anchors, never left and right; the check below pins a view 10 points from the leading edge, which lands at x = 10 in a left-to-right container and at x = 140 (10 from the right edge) in a right-to-left one, while a constraint written with left stays at 10 and does not mirror. In SwiftUI HStack, padding(.leading) and .multilineTextAlignment(.leading) mirror automatically, and layoutDirection is an environment value; in UIKit use NSTextAlignment.natural so text aligns to the reading direction. semanticContentAttribute controls whether a view mirrors (.playback and .spatial opt out for media controls and diagrams that must not flip).
Decide what to flip. Flip things that express direction of flow or progress: back and forward chevrons, list disclosure arrows, progress bars, sliders, swipe directions. Do not flip things that are not directional: logos, clocks and analog gauges, play and pause and other media controls (they follow recording convention), phone keypads, charts and maps, most photos, and digits in a number. SF Symbols have directional variants that flip on their own; for your own images set flipsForRightToLeftLayoutDirection (or .flipsForRightToLeftLayoutDirection(true) in SwiftUI) or provide separate RTL assets. Test with the Right-to-Left pseudolanguage in the scheme (it mirrors the app without translating) and with a real RTL language, looking for clipped or misaligned text, icons pointing the wrong way, animations and gestures that slide in the wrong direction, and any left or right left in the code. Do this early: retrofitting RTL is far more work than building with leading and trailing.
Leading mirrors with the layout direction; left is always the physical left.
The environment can be overridden for previews and tests, and the flipping modifier exists for directional images.
Common traps
- Using
leftandrightconstraints or paddings. They do not mirror, so the UI is wrong for RTL users. - Flipping everything, including media controls and numbers. Play arrows and digits keep their orientation.
They might follow up with
- How do you test without knowing Arabic? The Right-to-Left pseudolanguage in the scheme, which mirrors the layout and shows you the problems.
- What does
naturaltext alignment do? Aligns to the start of the language’s reading direction.
Related questions
- 24.3.2 How is bidirectional text handled, why should you isolate user-generated strings, and how do different…
- 24.4.1 How do you test internationalisation before translations exist: pseudolanguages, launch arguments, text…
- 24.2.1 How does Dynamic Type work, how do you support it in UIKit and SwiftUI, and how do layouts adapt at…
- 7.7.1 What is a trait collection, and how do size classes and trait changes drive adaptive layout?
- 2.6.5 How does the app pick its language at runtime, and how is that different from Locale?
How is bidirectional text handled, why should you isolate… →