Translating your app is easy. Localizing your User Experience is the real challenge.
When we talk about Internationalization (i18n), most developers think about JSON files and translation keys. But for millions of users speaking Arabic, Hebrew, or Persian, translation is only half the battle. The entire UI needs to "flip."
In this article, I’ll show you how to build a robust, production ready RTL (Right to Left) system using i18next, Tailwind CSS, and Radix UI.
1. The Source of Truth: Centralizing Language Data
Don’t just store language codes. Your language configuration should dictate the visual direction of your application. By adding a dir property to your supported languages, you create a single source of truth for your UI.
export const SUPPORTED_LANGUAGES = [
{ code: "en", label: "English (US)", flag: "🇺🇸", dir: "ltr" },
{ code: "tr", label: "Türkçe", flag: "🇹🇷", dir: "ltr" },
{ code: "ar", label: "العربية", flag: "🇸🇦", dir: "rtl" },
{ code: "he", label: "עברית", flag: "🇮🇱", dir: "rtl" },
] as const;
2. Syncing i18next with the DOM
To make your CSS work globally, the browser needs to know the direction of the document. We can automate this by listening to i18next events. When the language changes, we update the dir attribute on the <html> element.
i18n.on('languageChanged', (lng) => {
const currentLang = SUPPORTED_LANGUAGES.find(l => l.code === lng);
const direction = currentLang?.dir || 'ltr';
document.documentElement.dir = direction;
document.documentElement.lang = lng;
});
3. Tailwind CSS: The Power of Logical Properties
If you are still using left-0 or pl-4 (padding-left), you'll have a nightmare manually overriding styles for RTL. Modern Tailwind (and CSS) uses Logical Properties. These properties automatically adjust based on the text direction.
- Instead of
left-0/right-0: Usestart-0andend-0. - Instead of
pl-4/pr-4: Useps-4(padding-start) andpe-4(padding-end). - Instead of
border-l: Useborder-s.
By using start and end, your layout "just works" when the dir="rtl" attribute is applied. No extra classes needed.
4. The Secret Sauce: Radix UI DirectionProvider
Standard CSS flips your text, but complex UI components like Dropdowns, Popovers, and Sliders often calculate positions based on fixed coordinates.
If you use Radix UI, simply wrapping your app in the DirectionProvider ensures that all primitives behave correctly. A dropdown that opens to the right in English will intelligently open to the left in Arabic.
import * as Direction from "@radix-ui/react-direction";
function App() {
const { i18n } = useTranslation();
const currentDir = i18n.dir();
return (
<Direction.Provider dir="{currentDir}">
<div dir={currentDir}>
<YourAppContent/>
</div>
</Direction.Provider>
);
}
5. Real World Implementation & SEO
I implemented this exact architecture in my latest project, Barcode Generator. When dealing with global tools that handle asset creation and labeling, ensuring that the UI respects the user's local direction isn't just a feature, it's a necessity for professional workflows.
From an SEO perspective, this is vital. Search engines analyze your Semantics. Using the correct dir attribute helps screen readers and crawlers interpret your content order correctly, reducing bounce rates from international users who expect a native experience.
Conclusion
Building a truly global application means making every user feel at home. By shifting from fixed directions (left/right) to logical ones (start/end) and integrating directionality into your i18n workflow, you move beyond mere "translation" and into true "localization."
Are you handling RTL in your React projects? Have you made the switch to logical properties yet? Let’s discuss in the comments!

Top comments (0)