Last fall, I spent three hours troubleshooting why a client’s Arabic language switcher looked like it had a seizure when Arabic text showed up in English containers. The real kicker? The issue had nothing to do with the language switcher code itself. More on that mess later.
This wasn't just any project. A real estate platform I'd been building for a UAE client needed full bilingual support - Arabic as the primary language, English as the secondary. They wanted users to switch seamlessly between languages, with proper right-to-left (RTL) styling for Arabic and standard left-to-right (LTR) for English. Simple enough on paper, right?
The Setup: Next.js i18n and Directionality
The core tools were straightforward:
next-i18nextfor translation handling- A custom hook to manage document direction (
dirattribute) - Component-level RTL adjustments using
styled-jsx
Here's how I structured the Next.js config:
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'ar'],
defaultLocale: 'en',
},
}For styling RTL components, I used the :dir() pseudo-class in CSS instead of duplicating stylesheets. This kept the codebase clean and avoided doubling the CSS bundle size:
// Header component styles
export default function Header() {
return (
<style jsx>{`
:dir(rtl) .title {
font-family: 'Almarai', sans-serif !important;
}
`}</style>
)
}The First Big Pitfall: Date Formatting
Midway through the project, I realized the platform needed to display dates in both languages. I assumed moment.js would handle Arabic dates out of the box. I was wrong.
What seemed like a straightforward moment.locale('ar') turned into an 8-hour debugging session when Arabic dates appeared in the "Dhu al-Qi'dah 14, 2023" format instead of the required "١٤ ذو القعدة ١٤٤٤ هـ". The fix? Ditching moment.js completely and using Hijri.js with custom formatting.
Language Direction and HTML Attributes
The document's tag needed to switch between dir="rtl" and dir="ltr" based on locale. This had to work server-side for SEO and client-side for dynamic switching. Here’s how I managed it:
// _document.tsx
export default function Document() {
const locale = useRouter().locale
const dir = locale === 'ar' ? 'rtl' : 'ltr'
return (
<Html dir={dir} lang={locale}>
...
</Html>
)
}For dynamic language switching without page reloads, I created a context provider that updated the HTML dir attribute and handled the transition animation between pages. This required patching the Next.js router to prevent full reloads when switching languages.
The Accordion That Broke Everything
One night, I deployed what should've been a simple UI update: adding an FAQ accordion section. When the QA team tested it in Arabic mode, the entire content layout shifted 40px to the left. Turns out I'd forgotten that :dir(ltr) .class specificity outweighs :dir(rtl) .class in styled-jsx. The fix? Swapping to [dir="rtl"] .class selectors and doubling font size in dev tools to catch hidden alignment issues.
Real-World Example: Reach Home Properties
The bilingual requirements for this real estate platform were intense. UAE clients expect:
- •Arabic as the default language (but not everywhere - Sharjah government sites sometimes prefer English first)
- •RTL styling that works inside HTML emails (we had to build a separate email template engine for that)
- •Proper Arabic date formatting that accounts for Hijri calendar differences
We handled these with a mix of server-side rendering for SEO and a language switcher that stored preferences in a cookie with 24-hour expiry. The solution got reused across three other GCC real estate platforms after this project.
Deployment Gotchas
When we moved from dev to production, the CSS purging in Tailwind started stripping RTL utilities like *:dir(rtl) because they weren't in our purge content patterns. This showed up as missing RTL styles only in production builds. Fixed it by:
- Adding regex pattern for
dir(rtl)in Tailwind config - Creating a shared CSS file just for RTL overrides
- Testing in incognito mode with cookie injection to simulate fresh users
The Secret Sauce: Testing Like Real People
Automated tests only caught 60% of the visual issues. The other 40% required:
- •Having Arabic native speakers test in actual browsers
- •Using Chrome DevTools' "text-orientation: sideways" to simulate vertical writing (even though we didn't need it here)
- •Cross-checking font rendering in iOS Safari vs Android Chrome
For the client's team in Abu Dhabi, I built a language testing portal that showed side-by-side comparisons of components in both languages. They loved having a place to report alignment problems without sifting through 80-page QA docs.
This work gets trickier every time you add another language, but the fundamentals stay the same: control document direction, handle font choices carefully, and never trust default date libraries to do the right thing with Arabic formats.
If you ever get stuck on RTL issues in Next.js or need help making bilingual websites feel natural for UAE users, hit me up at sarahprofile.com/contact. I’ve probably already screamed into the void about your exact problem.