iOS RTL Language Support: Resources, Traits, and the Read-Only App Direction
Deep understanding over rote memorization.
Supporting RTL (right-to-left) languages such as Arabic and Hebrew requires handling the resource language, layout environment, and content semantics at the same time.
This article first explains the relevant properties and their history, then discusses how to implement in-app instant switching and where its boundaries are.
For a self-built instant switcher, a clean division of responsibilities is: switch resources + override the environment direction + preserve control semantics. However, overriding the direction trait does not synchronously rewrite the app-level direction, and it cannot replace a full system language-environment switch.
Prefer the system-provided per-app language setting
Starting in iOS 13, users can choose a language for a single app without changing the device's overall language.
Apple's WWDC19 advice: if an app needs a language entry point, guide users to that app's settings in the system Settings app, let the system relaunch the app in the target language, and use state restoration to improve the return experience. WWDC24 revisited this per-app language setting.
If the product can tolerate jumping to Settings and relaunching, prefer this system route. If the product explicitly requires the language to take effect immediately inside the app, you need to manage resources and direction yourself and verify the behavior of system components and third-party components. The latter is an engineering solution that satisfies product constraints; it is not equivalent to Apple's recommended full language-switching flow.
References: WWDC19: Creating Great Localized Experiences with Xcode 11, around 2:32–3:30; WWDC24: Build multilingual-ready apps.
Related properties and history
A trait represents one feature of the interface environment, such as horizontal/vertical size classes, display scale factor, and layout direction.
Each of these is a trait; UITraitCollection is the collection of these traits.
- iOS 8 introduced the trait concept:
UITraitCollectionandUITraitEnvironment.- Common
UIView,UIViewController,UIPresentationController, andUIScreentypes conform toUITraitEnvironment.UIWindowinherits this capability throughUIView;UIWindowScenehas been available since iOS 13 and also conforms to the protocol.
- Common
- iOS 10 added
layoutDirectiontoUITraitCollection.- It also introduced
effectiveUserInterfaceLayoutDirection.
- It also introduced
- iOS 17 (WWDC23) unified trait inheritance paths for views and controllers and introduced the more direct
traitOverridesAPI; trait inheritance and controller-level override capability existed before this.
semanticContentAttribute
semanticContentAttribute is a read-write UIView property introduced in iOS 9. It represents the semantic characteristics the content wants to express.
| English value | Apple's English description | Plain Chinese label | Concrete meaning |
|---|---|---|---|
.unspecified |
The default value for views. | Default semantics, follow the environment | No special direction requirement; resolve according to the view's environment. For example, bold/italic button groups usually fall into this case. |
.playback |
A view representing the playback controls, such as Play, Rewind, or Fast Forward buttons or playhead scrubbers. | Media playback semantics, keep direction | Used for play, rewind, fast forward, playback progress, and similar controls; not mirrored when switching LTR/RTL. |
.spatial |
A view representing a directional control, such as a segment control for text alignment, or a D-pad control for a game. | Absolute spatial semantics, keep direction | Used for left/right alignment, game D-pads, and similar controls; physical left and right do not change with reading direction. |
.forceLeftToRight |
A view that's always displayed using a left-to-right layout. | Force left-to-right | No matter the environment direction, content always uses LTR layout. |
.forceRightToLeft |
A view that's always displayed using a right-to-left layout. | Force right-to-left | No matter the environment direction, content always uses RTL layout. |
UITraitCollection.layoutDirection
layoutDirection is a read-only trait property of UITraitCollection introduced in iOS 10. It represents the layout direction associated with the current environment.
enum UITraitEnvironmentLayoutDirection : Int {
case unspecified = -1
case leftToRight = 0
case rightToLeft = 1
}
layoutDirectionis a property ofUITraitCollection, andUITraitCollectionis part of theUITraitEnvironmentprotocol.- Common UIKit views and controllers expose
traitCollectionthrough that protocol, but you cannot conclude that all UI-related objects conform toUITraitEnvironment. ReadtraitCollectionfrom a specific object that conforms to the protocol, then accesslayoutDirectionon it.
Difference from UIApplication.userInterfaceLayoutDirection
typedef NS_ENUM(NSInteger, UIUserInterfaceLayoutDirection) {
UIUserInterfaceLayoutDirectionLeftToRight,
UIUserInterfaceLayoutDirectionRightToLeft,
} API_AVAILABLE(ios(5.0)) API_UNAVAILABLE(watchos);
userInterfaceLayoutDirectionis a property introduced in iOS 5 and also represents layout direction.UIApplication.userInterfaceLayoutDirectionandUITraitCollection.layoutDirectionare both read-only access points. The former represents the app-level interface direction; the latter represents the direction of the trait environment where the current object lives, and the override APIs can supply a different value for a specific scope. Manually loading Arabic resources or overriding a local trait does not write back toUIApplication's read-only property.
Modifying layoutDirection
The UITraitCollection.layoutDirection you read is a read-only value. When changing the environment direction, use the override API to provide a new trait value, rather than directly modifying the read collection or overriding its getter.
- On iOS 10–16, a parent container controller can use
UIViewController.setOverrideTraitCollection(_:forChild:). - Starting with iOS 17, use
traitOverrides.layoutDirection.
On iOS 17+,
traitOverridesis available onUIWindowScene,UIView(includingUIWindow),UIViewController, andUIPresentationController. The override applies to that environment and everything below it; an explicit override further down still affects the final result. The oldersetOverrideTraitCollection(_:forChild:)only overrides the specified child controller and its inherited scope, so it cannot be used to change the language environment of the scene or the entire process.
semanticContentAttribute vs layoutDirection
I originally had a question: why have two properties for layout?
Because the same environment can impose different layout requirements on different content.
The official example above: the left side is an English interface, the right side is Arabic. The bold/italic buttons at the top are the style group; the text-alignment buttons at the bottom are the alignment group.
| Group | Language | semanticContentAttribute |
layoutDirection |
effectiveUserInterfaceLayoutDirection |
|---|---|---|---|---|
| Style group | English | .unspecified |
LTR | LTR |
| Style group | Arabic | .unspecified |
RTL | RTL |
| Alignment group | English | .spatial |
LTR | LTR |
| Alignment group | Arabic | .spatial |
RTL | LTR |
- For the style group, set
semanticContentAttributeto.unspecified, meaning no special requirement for the content; follow the environment. So whenlayoutDirectionis set differently on the container view for different languages, the final rendered result (effectiveUserInterfaceLayoutDirection) followslayoutDirection. - The alignment group wants
.spatialsemantics, meaning the content should not follow the environment. So no matter howlayoutDirectionchanges, the final rendered result is LTR.
UIView.effectiveUserInterfaceLayoutDirection
effectiveUserInterfaceLayoutDirection is a read-only UIView property introduced in iOS 10. It represents the effective layout direction.
Environment direction and content semantics together affect view arrangement. When customizing layout or drawing a view's direct content, query that view's effectiveUserInterfaceLayoutDirection. Do not assume the whole subtree shares the same effective direction. For example, a spatial directional control in an RTL environment can still use LTR. Apple API documentation.
Source and boundaries of UIApplication.userInterfaceLayoutDirection
- iOS 5 introduced the
UIApplication.userInterfaceLayoutDirectionproperty. - iOS 9 introduced the
UIView.userInterfaceLayoutDirection(for:)class method.
The latter resolves using the passed-in content semantics and the app-level direction, and could be used for custom layout decisions at that time.
In the normal localization flow, the system combines the user's language preference and the app's supported localizations to choose the app's runtime language; when there is no match, it falls back to the development language.
The device's preferred language is not necessarily the language the app actually uses, and region format preferences do not equal interface direction. Apple QA1828: How iOS Determines the Language For Your App.
According to public documentation, the normal behavior can be understood as: user language preference + app-supported localizations → app runtime language → app-level interface direction → resolve local direction combined with control semantics. Apple has not published the specific implementation of the getters for each iOS 5–9 version, so a language query expression should not be treated as its internal source code.
In testing, the difference can be reproduced: after the system is changed to Arabic, the app-level direction (userInterfaceLayoutDirection) becomes RTL; when the system stays in Chinese and only the in-app business language is switched, it remains LTR. Switching resources, overriding the direction trait on a scene/window/controller, or setting content semantics will not synchronously rewrite the read-only userInterfaceLayoutDirection through the public API.
In-app language switching implementation strategy
In one sentence: switch resources, change the environment direction, preserve control semantics; after refresh, lay out according to each view's effective direction.
| Processing item | Specific approach | Notes |
|---|---|---|
| Switch resources | Uniformly choose text, images, and server-side content; update established formatting strategies. | Saved app language + unified resource entry. |
| Override direction | Decide LTR/RTL from the app language and set it at a suitable common ancestor. | Use the layoutDirection override APIs; see Modifying layoutDirection above. |
| Preserve semantics | Normal: .unspecified; playback: .playback; physical direction: .spatial. |
Usually no need to change semanticContentAttribute. |
Refresh and acceptance: Preserve navigation and editing state, and read the view's effectiveUserInterfaceLayoutDirection at the appropriate layout phase. Check new scenes, modal interfaces, and attached windows separately. WWDC23: trait updates and inheritance.
Boundaries: UIApplication.userInterfaceLayoutDirection will not update synchronously; system components and the safe area still need verification.
Known issues
Do not use semanticContentAttribute to implement language switching
- Some projects use something like setting
UIView.appearance().semanticContentAttributeto achieve an RTL effect. - Even if the effect looks correct, this does not match
semanticContentAttribute's definition of semantics. - Problems have already appeared on the iPhone Duo simulator: this setting triggered an incorrect
safeArea, causing layout errors.
References
- WWDC16-What's New in International User Interfaces
- WWDC23-Unleash the UIKit trait system
- WWDC15 Session 222: New UIKit Support for International User Interfaces (Apple official slides)
- Apple QA1828: How iOS Determines the Language For Your App
- WWDC19: Creating Great Localized Experiences with Xcode 11
- WWDC22: Get it right (to left)
- WWDC24: Build multilingual-ready apps
- UIView.effectiveUserInterfaceLayoutDirection
- Apple: Building an Adaptive Interface