Your Arabic site looks fine because you're checking the wrong thing

Bilingual failures cluster in three places, and only one of them is visible to a reviewer who reads a single language. Here's how to find the other two.

Last updated

A review in one language finds one of the three failures

Open the Arabic site, scroll it, decide whether it looks right. That review has one instrument, a pair of eyes reading one language, and it reports what those eyes register: the navigation on the right, the text running the correct way, the headings in Arabic. A site can fail in ways it never touches and still come back clean.

Bilingual sites fail in three places, and they sort by who can see them.

Direction is the visible one. All of it shows up in a screenshot, which is the one thing an eyeball review is equipped to find. The seam is the second: the points where Latin content sits inside Arabic content, or the reverse. A phone number, a product code, a brand name, an English error message on an Arabic form. Those only read as wrong to someone fluent in both languages who is paying attention to the order of characters. The third class is declaration, the machine-readable statement of what language this page is in and which other versions of it exist. Nothing about it appears on screen for a reviewer to look at. Its readers are a crawler, a screen reader and the browser’s own typography.

They also sort by build order, which is what makes the ranking hold. A site whose English version was designed and whose Arabic version was fitted to it afterwards comes out strongest in the class you can see and weakest in the two you can’t, because that is the order the work got reviewed in. That build order is what a business produces when it works in English and sells into a market that reads, buys and regulates in Arabic. In the UAE that regulating half sits in consumer law. The pattern below is really a pattern about which version was the afterthought.

So stop auditing the Arabic page. Audit the three classes, each with the reader who can actually detect it. That reordering is the whole method.

Direction belongs in the HTML

Put dir="rtl" on the <html> element of the Arabic version, next to lang="ar". MDN’s reasoning for preferring the attribute over the CSS direction property is worth internalising: directionality is a property of the content, not of its presentation, so the attribute keeps the text readable on a browser where the stylesheet failed to load. Set direction in CSS alone and the stylesheet becomes the only thing holding the page the right way round. Arabic letters carry strong directionality of their own, so they still run right to left without it. What goes is the base direction of the paragraph: text aligns left, punctuation lands at the wrong end of a sentence, and any run mixed with Latin is ordered against the wrong baseline.

Underneath that, stop writing physical directions. CSS logical properties map to the correct physical side from the document’s direction: margin-inline-start resolves left in an LTR context and right in an RTL one, and padding-inline, border-inline-end and inset-inline-start follow the same rule. They’ve been safe across browsers without fallbacks since 2021. Build the layout in logical properties once and the RTL version is the same layout, not a second one.

The alternative is an override sheet full of [dir="rtl"] rules undoing the left-and-right decisions made everywhere else. That sheet is a second site you maintain in parallel, and it starts drifting the first time a component ships without it.

Then decide deliberately what mirrors. Icons describing movement through the interface flip: back and forward arrows, next-step chevrons, progress fills, carousel controls, indent and outdent. Material Design’s bidirectionality guidance covers the exceptions, and its reasoning is the part worth carrying. A media playback control holds its direction because the triangle points the way the media runs, which has nothing to do with reading order; mirroring it draws a rewind button. Clock faces and circular refresh arrows hold theirs on the same logic. So does anything depicting a physical object, since a keyboard looks the same everywhere in the world. Numerals are governed elsewhere: the bidirectional algorithm gives them their own internal order inside an Arabic sentence, whichever way the page runs.

One trap gets past a code review by looking correct: flex-direction: row-reverse inside an RTL container reverses an order that RTL already reversed. The row comes out in the original left-to-right sequence, which is what the LTR layout asked for. If a row-reverse exists in your codebase for visual reasons, it is wrong in one of your two languages right now.

The seam: Latin text sitting inside Arabic sentences

This is the class a one-language review cannot reach, and it produces a bilingual bug that changes what a string says instead of how it looks.

Browsers resolve mixed-direction text with the Unicode bidirectional algorithm. The part that matters in practice: an element with dir="auto" takes its base direction from the first character with strong directionality, and everything else arranges around that decision. So an Arabic paragraph containing a phone number, an order reference or a Latin brand name is a negotiation the browser settles with a rule that knows nothing about your content.

A Kuwaiti number stored as 965 2221 6656 renders as 6656 2221 965, because each group of digits keeps its own order while the groups themselves get laid out right to left. A trailing question mark lands at the wrong end of a sentence. An order code comes apart at its hyphen and reassembles in a different sequence. A display glitch is what that looks like. It isn’t one: a reversed order number is a different order number, and a customer reading it back to your support team is reading back something that doesn’t exist.

Three fixes, and they apply in different places.

Anything whose direction you don’t control at build time wants dir="auto". MDN is explicit that this is what it’s for: data with unknown directionality, like user input or content from an external source. Product names off a supplier feed, customer-submitted reviews, search queries echoed back on a results page.

Wrap embedded runs of the other language in <bdi>. The element works out its own direction from its content, as though dir="auto" were set on it, and it isolates that content so it can’t renegotiate the direction of the sentence around it. A Latin brand name inside an Arabic product title belongs in one. Where you can’t reach the markup, unicode-bidi: plaintext is the CSS equivalent: it reads the base direction off the first strong character and seals the run off from its surroundings. unicode-bidi: isolate alone does the second half and takes its direction from the direction property, so it detects nothing.

Then there are the form fields. Email, telephone, URL, password and anything taking a reference code all need dir="ltr" set on them explicitly, whatever the page direction says. The label stays in Arabic and right-aligned. The field contents run left to right, because that is the direction the data is in. Leave those to inherit the page and a customer types an email address, watches the cursor do something strange, and retypes it twice.

Who actually reads your lang attribute

Not Google. Google’s own multi-regional documentation says it doesn’t use code-level language information such as lang attributes or the URL, and that it determines the language of a page from its visible content.

That has a consequence when a translation budget runs out: the navigation, the footer and the buttons get translated while the body copy stays English. Google warns about exactly that pattern. If the language verdict comes from visible content, a page of Arabic chrome wrapped around English prose is an English page with some decoration. Whatever you declared about it is beside the point.

So lang is not an SEO tag. It earns its place because of who does read it. WCAG 3.1.1 Language of Page is Level A. Its sibling, 3.1.2 Language of Parts, is Level AA, and requires that the human language of each passage or phrase can be programmatically determined, with four exceptions: proper names, technical terms, words of indeterminate language, and words or phrases that have become part of the vernacular of the immediately surrounding text. Screen readers use that to switch pronunciation rules. Browsers use it to select a language-appropriate font and to apply the right hyphenation, line-breaking and quotation marks.

3.1.2 is the one to check first, because what it governs is the switched passage, and switched passages are what bilingual sites are made of. An Arabic testimonial on an English page. An English legal clause inside Arabic terms. A bilingual product spec table. Mark every one of them with lang on the containing element. If your site targets WCAG 2.1 AA, and Bracketworks builds to that bar, this isn’t optional polish.

Hreflang is reciprocal or it is ignored

Hreflang is the other half of the declaration class: lang states what this page is, hreflang states which other versions exist. Google’s rule for it is unusually blunt for a search documentation page. Each language version must list itself as well as all other versions, and if two pages don’t both point to each other, the tags will be ignored. Not downweighted. Ignored.

Which turns the build-order asymmetry into a null operation. The English page carries a full set of annotations, because it is the one the build was designed around. The Arabic version, fitted afterwards against a template that was never part of that design, points at nothing. Inspect the English source and the annotations look correct. They do nothing.

The syntax rules that bite after that one:

Only ISO 639-1 language codes and ISO 3166-1 alpha-2 region codes are supported, and you can’t specify a country code on its own. The first code is always the language. ar targets Arabic speakers anywhere; ar-AE targets Arabic speakers in the UAE. Pick based on whether the content genuinely differs by country. Run one Arabic version for the whole Gulf and ar is the honest declaration; a country code is a claim you’ll have to keep making good on.

Add x-default for the version that serves anyone the other annotations don’t match. And keep every URL in the set reachable. Google doesn’t document what becomes of a set containing a URL that redirects, 404s or sits behind a robots.txt disallow, so treat it as the reciprocity rule extended one step: a page nobody can fetch cannot carry a return link, and the pair it belonged to breaks.

Keep the mechanism’s limit in view too. Annotations are a signal about which version to show which searcher, weighed against everything else Google knows. They don’t assert what language a page is in; the visible content settles that, which is the same rule reaching hreflang from a different angle. So hreflang can’t rescue a half-translated page, and the effort is better spent finishing the translation. This is also the point where bilingual work stops being a language problem and becomes the same crawl-and-canonical discipline as everything else, which is a broader set of foundations worth getting right together.

The switcher that drops you on the homepage

The switcher is where the declaration class meets an actual person, and Google gives three instructions about it that are easy to break. Don’t automatically redirect users from one language version to another. Don’t use IP analysis to adapt content, because the location read is unreliable. Don’t use cookies or browser settings to change the language a single URL serves. Do add crawlable links to the other language versions so a person can choose.

One reason sits behind all three. Every version needs a stable URL returning the same content to everybody, including a crawler whose request carries no language preference at all. Googlebot sends no Accept-Language header, so a page deciding what to serve from one is deciding on a blank.

Then there’s the part costing you conversions instead of impressions. A switcher should map the current URL to its counterpart. A reader four levels deep in your product catalogue who clicks the Arabic link and lands on the Arabic homepage has been handed a chore instead of a translation, and that chore is what reading the other version costs. On a bilingual site it is charged at every crossing, and a reader who checks a specification in one language and the return policy in the other crosses more than once in a session. The same journey runs straight into the checkout, where the Arabic path needs its own review cycle.

Build a test the site can actually fail

Looking at the Arabic page passes by construction. Replace it with five checks that have a defined failure state.

Render a page carrying a Latin brand name, a phone number and a reference code inside an Arabic paragraph, then read the character order out loud. The Arabic form wants submitting with a deliberately invalid email, so the validation message appears and you can confirm it is in Arabic and on the correct side of the field. Language switching gets tested from the deepest page you have. Return tags need a crawl of both versions, not an inspection of the English source and an assumption about the other one. And a screen reader run over a passage that changes language mid-page tells you in seconds whether the pronunciation changes with it.

Two more things to build in.

Budget vertical space, not horizontal. The widely repeated figure that Arabic runs around a quarter longer than English doesn’t appear in W3C’s own work on text size in translation. What that work does say is that Arabic words are built from compact, pattern-based roots and resist abbreviation, and that it lists Arabic among the scripts needing more height than Latin, most of all in Nastaliq faces. So the component that breaks is the one with a fixed height or a tight line clamp, and the fix is generous line-height with no clamping on Arabic text. Arabic webfonts carry their own cost, and it lands on your loading metrics instead of your layout, which puts it in the speed diagnosis.

And if you sell to consumers in the UAE, move the Arabic transactional path to the front of the queue. The UAE government’s own guidance on the federal consumer protection law states that the invoice must be in Arabic, with any other language added at the supplier’s discretion, and that e-commerce businesses must give consumers information in Arabic about the product or service. That takes a half-finished Arabic checkout out of the backlog of conversion improvements and puts it somewhere with a harder deadline.

The declaration class is machine work: crawl both versions and compare what each one declares. Our Site Audit runs that findability pass in a few minutes. The seam is the part you have to read yourself, in both languages, knowing what to look for. We’d spend an hour there before spending a week anywhere else.

Standing offer

The hard part is deciding what to do first

Bring us a URL, an audit report, or a proposal you're not sure about. We'll tell you which problems deserve budget this quarter, and you'll leave with an order of operations you can hand to whoever does the work, whether that's us or not.

Book twenty minutes