RTL Isn't a Mirror: What Arabic and Urdu Taught Us About Layout
The first pass at right-to-left support on any site looks the same: swap left and right for margin-inline-start and margin-inline-end, add dir="rtl" to the <html> tag, and watch most of the layout correct itself for free. CSS logical properties really do handle the majority of it automatically. The remaining problems are the interesting ones.
Numbers don't reverse, but neighbors think they do
"150+" inside an Arabic sentence has a specific failure mode: the Unicode bidi algorithm treats the + as a neutral character and reorders it based on surrounding context, so it can render as "+150" — visually correct-looking, semantically backwards. The fix isn't a CSS rule; it's isolating the number from the surrounding paragraph direction with a <bdi> element, so the digits and their punctuation stay a self-contained LTR unit regardless of what direction wraps them. We wrap every rendered statistic, price, and percentage on this site in one.
transform doesn't know about dir
Direction-aware icons — an arrow pointing "forward" — need to visually flip in RTL. The natural instinct is a transform: scaleX(-1) under an [dir="rtl"] selector, which works for the icon itself but breaks the moment you also want a hover animation on that same element: transform can only hold one value, so the flip and the hover nudge fight over the same CSS property and the nudge either cancels the flip or vice versa. The fix was splitting concerns onto two different CSS properties entirely — the mirror stays on transform, and the hover motion moves onto translate, scaled by a --dir: 1 | -1 custom property that flips sign under [dir="rtl"]. Two properties that don't collide, instead of one property with two jobs.
A sliding carousel's math assumes a coordinate system
flex-direction: row under dir="rtl" doesn't just mirror how a row visually reads — it reverses the DOM order in which children are laid out along the main axis. That's usually the behavior you want. It is not the behavior a carousel's translateX(-page * 100%) math wants, because that formula assumes page 0 sits at physical position zero regardless of language. Locking the carousel's own sliding track to dir="ltr" — and only that one element — keeps the arithmetic direction-independent, while restoring the real document direction one level down so the text inside each slide still renders correctly. Isolating the mechanical layer from the content layer turned out to be the general pattern, not a one-off hack.
The takeaway
None of these are RTL-specific tricks so much as they're instances of the same idea: a UI has both content direction and mechanical/coordinate behavior, and code that conflates the two will work in the language it was built in and quietly misbehave in the other. Testing exclusively in a browser set to English will never surface any of this — the only reliable way to find it is to actually render the Arabic and Urdu pages and look.