Текст в цифровых системах не существует в едином линейном формате. Он подчиняется направлению письма, которое определяется письменностью языка. Существуют два основных направления:
При этом направление текста не является простым свойством строки. Оно определяется комбинацией языка, скрипта и контекста отображения. Особенно сложным становится поведение при смешанном содержимом: числа, латиница, символы пунктуации и вставки из других языков.
Основой отображения текста с разными направлениями является Unicode Bidirectional Algorithm (Bidi Algorithm). Он решает, как визуально упорядочить символы, если в одной строке присутствуют разные направления письма.
Ключевые особенности алгоритма:
Пример проблемной строки:
hello 123 שלום
В зависимости от контекста отображение может меняться, особенно если отсутствует явное указание направления контейнера.
Библиотека Intl API в JavaScript не управляет направлением текста напрямую. Её задача — локализация данных:
Intl.DateTimeFormatIntl.NumberFormatIntl.ListFormatIntl.CollatorОднако все эти операции косвенно связаны с направлением письма, потому что результат форматирования зависит от локали.
Пример:
const ar = new Intl.NumberFormat('ar-EG');
const en = new Intl.NumberFormat('en-US');
console.log(ar.format(1234567.89));
console.log(en.format(1234567.89));
В арабской локали цифры и разделители могут отображаться иначе, но направление строки остаётся задачей рендеринга, а не форматирования.
Локаль в Intl API задаёт не только язык, но и культурные правила отображения данных. Однако направление текста традиционно не было частью стандартного API. Это приводило к тому, что разработчик обязан был отдельно учитывать RTL-локали при построении интерфейса.
Примеры RTL-локалей:
ar — арабскийhe — ивритfa — персидскийur — урдуЭти языки требуют не только перевода строк, но и изменения логики компоновки интерфейса.
В веб-разработке направление текста задаётся через атрибут:
<div dir="rtl">مرحبا بالعالم</div>
или:
<div dir="ltr">Hello world</div>
Intl API не управляет этим атрибутом напрямую, но часто используется для определения текущей локали:
const locale = navigator.language;
const rtlLocales = ['ar', 'he', 'fa', 'ur'];
const isRTL = rtlLocales.some(code => locale.startsWith(code));
document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
Таким образом, Intl становится частью системы принятия решений о направлении интерфейса, хотя формально не отвечает за него.
Одной из наиболее сложных проблем является смешение LTR и RTL в одной строке.
Пример:
Order № 123 من المتجر
Проблемы, возникающие при рендеринге:
Особенно критично это проявляется при отсутствии корректного
контейнера с dir.
Хотя Intl не задаёт направление письма, он влияет на компоненты, которые визуально зависят от него:
const dt = new Intl.DateTimeFormat('ar-SA', {
dateStyle: 'full'
});
console.log(dt.format(new Date()));
Результат будет соответствовать арабским правилам отображения, включая порядок компонентов даты.
const list = new Intl.ListFormat('he', {
style: 'long',
type: 'conjunction'
});
console.log(list.format(['אחד', 'שניים', 'שלושה']));
Порядок элементов и соединители адаптируются к языковой норме, что влияет на восприятие текста в RTL-среде.
Современные реализации расширяют объект Intl.Locale дополнительными метаданными, включая информацию о тексте:
const locale = new Intl.Locale('ar-EG');
console.log(locale.textInfo?.direction);
В некоторых средах это может возвращать:
"rtl""ltr"Однако поддержка этого свойства зависит от реализации и может отсутствовать в стандартных окружениях.
В реальных приложениях направление письма формируется как результат нескольких уровней:
Определение локали
const locale = navigator.language;Использование Intl для локализации данных
Определение направления интерфейса
const isRTL = new Intl.Locale(locale).textInfo?.direction === 'rtl';Применение к DOM
document.documentElement.dir = isRTL ? 'rtl' : 'ltr';В RTL-окружении числовые данные сохраняют LTR-структуру, но визуально могут перестраиваться:
1234 остаётся логически тем жеПример:
new Intl.NumberFormat('fa-IR').format(1234567.89);
Поддержка направлений письма требует, чтобы интерфейс:
Intl API в этом контексте выступает источником культурных правил, а не механизмом управления рендерингом, но его результаты определяют, как данные будут вписаны в RTL или LTR структуру.
При работе с международными интерфейсами критично сохранять согласованность:
Особенно это важно для:
В архитектурном смысле роль Intl API сводится к следующему:
Направление письма при этом остаётся отдельным уровнем, зависящим от: