Building a bilingual website is not the same as translating a few strings. The direction of the interface changes, typography behaves differently, components need to survive longer text, and search engines need a clear relationship between language versions.
This guide presents a practical architecture for websites that support both English and Arabic without duplicating the entire frontend.
1. Treat language and direction as document state
Set both lang and dir on the root element. Do not rely on CSS classes alone.
<html lang="ar" dir="rtl">
For English:
<html lang="en" dir="ltr">
This helps browsers, screen readers, form controls, punctuation, and bidirectional text behave correctly.
In a React application, derive both values from the active locale:
const direction = locale === "ar" ? "rtl" : "ltr";
return (
<html lang={locale} dir={direction}>
<body>{children}</body>
</html>
);
2. Use CSS logical properties
Directional declarations such as margin-left and padding-right create avoidable RTL bugs. Logical properties describe the purpose of the spacing instead of a physical side.
.card {
padding-inline: 1.25rem;
margin-block-end: 1rem;
border-inline-start: 4px solid #ff6b00;
}
.icon {
margin-inline-end: 0.5rem;
}
The same component now adapts automatically when the document direction changes.
Use these replacements whenever possible:
-
margin-inline-startinstead ofmargin-left -
padding-inline-endinstead ofpadding-right -
inset-inline-startinstead ofleft -
border-start-start-radiusfor direction-aware corners
3. Do not mirror everything
Layout direction should change, but not every visual element should flip. Logos, photographs, media controls, phone numbers, code snippets, and many charts should preserve their original orientation.
For directional icons, flip only the icons that communicate movement:
[dir="rtl"] .directional-icon {
transform: scaleX(-1);
}
Avoid applying scaleX(-1) to a whole container. It may reverse images and text rendering in surprising ways.
4. Keep translation content separate from components
A component should receive content rather than contain language-specific copy.
const messages = {
en: {
heroTitle: "Build a website around real customer demand",
cta: "Start a project",
},
ar: {
heroTitle: "ابنِ موقعك بناءً على طلب العملاء الحقيقي",
cta: "ابدأ مشروعك",
},
};
For production systems, validate message keys during the build so a missing translation cannot silently reach the interface.
5. Design for text expansion
Arabic and English strings rarely occupy identical widths. Navigation labels, buttons, cards, and validation messages should grow naturally.
Avoid fixed widths for text controls:
.button {
min-inline-size: 9rem;
inline-size: fit-content;
max-inline-size: 100%;
white-space: normal;
}
Test components with deliberately long labels. A layout that survives difficult content is more reliable than one tested only with short placeholder text.
6. Choose and load fonts carefully
A font may look excellent in Latin characters but offer weak Arabic glyphs. Choose a family with deliberate Arabic support, or define a compatible pair.
:root {
--font-latin: "Inter", system-ui, sans-serif;
--font-arabic: "Noto Sans Arabic", system-ui, sans-serif;
}
html[lang="en"] body { font-family: var(--font-latin); }
html[lang="ar"] body { font-family: var(--font-arabic); }
Subset font files, preload only the critical weight, and use font-display: swap. Loading five weights for two writing systems can quickly become one of the page's largest performance costs.
7. Give each language a stable URL
Use separate, indexable URLs such as:
/services/web-design/
/ar/services/web-design/
or:
/en/services/web-design/
/ar/services/web-design/
Do not change visible language only through JavaScript while keeping one URL. Separate URLs make sharing, analytics, caching, and search indexing more predictable.
Connect equivalent pages with hreflang:
<link rel="alternate" hreflang="en" href="https://example.com/en/service/" />
<link rel="alternate" hreflang="ar" href="https://example.com/ar/service/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/service/" />
Each page should also use a self-referencing canonical URL. Do not canonicalize the Arabic version to the English version; they are alternate language pages, not duplicates.
8. Test mixed-direction content
Arabic interfaces often contain English product names, email addresses, URLs, and numbers. Wrap isolated fragments when the browser may infer the wrong direction.
<p>راسلنا على <bdi>hello@example.com</bdi></p>
Test at least:
- Arabic text containing an English brand name
- phone numbers and prices
- form validation messages
- breadcrumbs and pagination
- icons inside buttons
- mobile navigation
- copied URLs and email addresses
9. Make direction part of automated testing
A small end-to-end test can catch many regressions:
test("Arabic page uses RTL direction", async ({ page }) => {
await page.goto("/ar/");
await expect(page.locator("html")).toHaveAttribute("lang", "ar");
await expect(page.locator("html")).toHaveAttribute("dir", "rtl");
});
Add visual snapshots for a few shared components in both languages. The goal is not pixel-perfect mirroring; it is consistent hierarchy, readable text, and correct interaction order.
Final checklist
- Set
langanddirat document level. - Prefer CSS logical properties.
- Mirror only directional UI elements.
- Keep translated content outside components.
- Allow labels and controls to expand.
- Load fonts with strong Arabic support efficiently.
- Give every language version a stable URL.
- Implement reciprocal
hreflanglinks. - Test mixed-direction content and keyboard navigation.
A bilingual website becomes much easier to maintain when direction is treated as a core system property instead of a late visual patch.
Kreetive applies these principles when planning and building multilingual service websites as a Digital marketing company in Qatar.``
Top comments (0)