Соберём всё в одной задаче. Карточка технической книги в интернет-магазине должна быть доступна на русском, английском и арабском, индексироваться отдельными URL, работать с клавиатуры и правильно форматировать цену и дату. Это один продукт, на котором сходятся три качества - доступность, поисковая устойчивость и интернационализация, - и удобнее вести их вместе, а не тремя отдельными проектами.
Начинается всё с чистого исходного HTML: в нём есть title, description, один h1, осмысленный текст и ссылки, которые называют объект. Карточка - это article, покупка - button, переход в каталог - ссылка; каждый элемент по назначению, а не div с обработчиком. Фокус виден при переходе с клавиатуры, а меню выбора языка открывается и закрывается клавишами, а не только мышью. Тем самым страница уже понятна и человеку с экранным диктором, и роботу - оба читают одну и ту же семантику.
Дальше та же страница получает поисковую и языковую идентичность. У каждого языкового URL есть self-canonical и взаимный hreflang: русская версия указывает на английскую и арабскую, и все три ссылаются друг на друга без разрывов. Корень документа получает верные lang и dir - для арабской версии это dir=rtl, разворачивающий поток интерфейса. Цена и дата поставки форматируются через Intl по правилам локали, а не собираются строками вручную. Один и тот же смысл теперь переносится между тремя аудиториями и тремя локалями, не ломая ни разметку, ни адрес.
Проверяется это не на глаз. Генератор ставит lang и dir по коду локали автоматически, чтобы человек не забыл переключить направление. В CI на каждый коммит запускаются axe для доступности и SEO smoke tests - наличие title, canonical, валидность hreflang. А раз в цикл проходит ручной сценарий: пройти карточку только с клавиатуры и отдельно - со screen reader, потому что автотест ловит нарушенные правила, но не ощущение от навигации.
Здесь и проходит граница между разовым подвигом и системой. Качество держится не героическим аудитом за день до релиза, а ограничениями, встроенными в четыре места: design system, шаблоны страниц, процесс работы с контентом и CI. Аудит в конце находит те же дефекты снова и снова, потому что каждая новая карточка создаётся с нуля; ограничение же не даёт дефекту появиться.
Разложим это по слоям как алгоритм. Design system несёт доступные примитивы, видимые состояния фокуса, токены контраста, RTL и правила для локализуемого текста - разработчик получает корректное поведение по умолчанию. Контентный процесс требует alt к изображениям, правильных заголовков, title и description, контекста для перевода, ревью и явного владельца у каждого текста. CI/CD прогоняет lint, unit-тесты, axe, проверки обхода, битые ссылки и валидацию hreflang и держит бюджет на регрессии, чтобы вчерашние победы не откатывались молча.
| Слой | Что удерживает |
|---|---|
| Design system | доступные примитивы, состояния фокуса, токены контраста, RTL, правила локализуемого текста |
| Контентный процесс | alt, заголовки, title, description, контекст перевода, ревью, владелец |
| CI/CD | lint, unit, axe, проверки обхода, битые ссылки, валидация hreflang, бюджет на регрессии |
Складывается единая модель. Доступный сайт корректно выражает смысл и позволяет действовать разными способами - мышью, клавиатурой, голосом. Поисково устойчивый сайт даёт этому смыслу стабильный URL и понятную структуру, по которой его находят. Международный сайт переносит тот же смысл между языками и рынками, не разрушая ни интерфейс, ни URL, ни данные. Это не три надстройки над сайтом, а три проекции одной архитектуры, и опираются они на одни и те же официальные источники - WCAG 2.2, WAI-ARIA APG, документацию Google Search Central, справочник MDN по Intl и web.dev по интернационализации.
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"> для арабской версии