Let us bring it all together in one task. A technical book card in an online store has to be available in Russian, English, and Arabic, be indexed under separate URLs, work from the keyboard, and format the price and date correctly. This is a single product where three qualities meet - accessibility, search robustness, and internationalization - and it is easier to carry them together than as three separate projects.
It all starts from clean source HTML: it has a title, a description, one h1, meaningful text, and links that name the object. The card is an article, the purchase is a button, the jump to the catalog is a link; every element serves its purpose rather than being a div with a handler. Focus is visible when you arrive by keyboard, and the language menu opens and closes with keys, not with the mouse alone. With that, the page is already clear both to a person using a screen reader and to the crawler - both read the same semantics.
Next the same page acquires a search and language identity. Every language URL has a self-canonical and reciprocal hreflang: the Russian version points to the English and the Arabic, and all three reference one another without gaps. The document root gets the correct lang and dir - for the Arabic version that is dir=rtl, which turns the interface flow around. The price and the delivery date are formatted through Intl by the rules of the locale rather than assembled from strings by hand. The same meaning now carries across three audiences and three locales without breaking the markup or the address.
This is not verified by eye. A generator sets lang and dir from the locale code automatically, so no one forgets to switch the direction. In CI, on every commit, axe runs for accessibility and SEO smoke tests run - the presence of a title, a canonical, the validity of hreflang. And once a cycle a manual scenario is run: go through the card with the keyboard alone and, separately, with a screen reader, because an automated test catches broken rules but not the feel of the navigation.
This is where the line runs between a one-off heroic effort and a system. Quality is held not by a heroic audit the day before release but by constraints built into four places: the design system, the page templates, the content process, and CI. An audit at the end finds the same defects again and again because every new card is built from scratch; a constraint, by contrast, keeps the defect from appearing at all.
Let us lay this out by layers as an algorithm. The design system carries accessible primitives, visible focus states, contrast tokens, RTL support, and rules for localizable text - the developer gets correct behavior by default. The content process requires alt text on images, correct headings, a title and description, context for translation, review, and an explicit owner for every text. CI/CD runs lint, unit tests, axe, crawl checks, broken links, and hreflang validation, and keeps a budget for regressions so that yesterday's wins are not rolled back silently.
| Layer | What it holds |
|---|---|
| Design system | accessible primitives, focus states, contrast tokens, RTL, localizable-text rules |
| Content process | alt, headings, title, description, translation context, review, owner |
| CI/CD | lint, unit, axe, crawl checks, broken links, hreflang validation, regression budget |
A single model comes together. An accessible site expresses meaning correctly and lets you act in different ways - with a mouse, a keyboard, a voice. A search-robust site gives that meaning a stable URL and a clear structure by which it is found. An international site carries the same meaning between languages and markets without destroying the interface, the URL, or the data. These are not three add-ons on top of the site but three projections of one architecture, and they rest on the same official sources - WCAG 2.2, the WAI-ARIA APG, the Google Search Central documentation, the MDN reference for Intl, and the web.dev materials on internationalization.
const RTL_LOCALES = new Set(['ar', 'he', 'fa', 'ur']);
function htmlLangDir(locale: string) {
const lang = locale.split('-')[0];
const dir = RTL_LOCALES.has(lang) ? 'rtl' : 'ltr';
return { lang: locale, dir };
}
// <html lang="ar" dir="rtl"> for the Arabic version