Хабрахабр

Справа налево. Что такое dir=rtl и как приручить арабский язык

Мы недавно перевели на арабский язык 2ГИС Онлайн, и хотим поделиться своим опытом адаптации интерфейса под RTL (right-to-left). Привет, Хабр. Это будет актуально и для иврита, и для персидского языка.

Сегодня — больше про теорию. Я разделю этот опыт на две статьи — теоретическую и практическую. Особое внимание уделю алгоритму, по которому строится отображение текста смешанной направленности — unicode bidirectional algorithm. Я расскажу, зачем нам понадобилось переворачивать весь интерфейс, что для разработчика интерфейсов значит фраза «сделать арабскую версию» и как справиться с арабским языком, смешанным с английским.

Зачем это всё?

Кажется, ценность в адаптации интерфейса под «справа налево» такая же, как у адаптации на любой другой популярный язык, но это не совсем так.

Пользовательский опыт, за исключением редких мелочей, в целом не отличается. Различие между английской и русской версией небольшое — чаще всего, это просто переведёный текст. Различие же между арабской и английской версией огромное.

6% интернет-ресурсов в мире содержат арабский контент. Всего 0. Привычное направление чтения для них — справа налево. Однако, на арабском говорит больше 5% пользователей интернета, и эта доля стремительно растёт. Точно такие же, как у носителя русского языка при пользовании RTL–интерфейсом. Какие ощущения у них от современного веба? Или как зайти однажды в 2ГИС и увидеть, что карточки и поиск — справа: Выбери себе метафору сам — может, это как садиться за руль праворульной машины, когда постоянно водишь леворульную.

Если мы хотим, чтобы наш сервис был максимально удобным для всех пользователей, адаптировать его под RTL нужно обязательно.

В чём состоит задача?

С первого взгляда она мне показалась необъятной — нужно переделать весь интерфейс под требования, которые никто не сможет нормально объяснить.

Посмотрев несколько примеров арабских сайтов, я понимаю, что сделать арабскую версию — это:

  1. Перевести данные на арабский язык. Эта часть понятнее всего, но легче не становится — это огромные объёмы данных;
  2. Перевести интерфейс на арабский язык. Для нас это не так просто, потому что до этого мы переводили только с русского, а переводчиков с русского на арабский у нас нет. Придётся сначала переводить строки и комментарии на английский, а потом — с английского на арабский;
  3. Адаптировать весь интерфейс под «справа налево». Это вроде просто «перевернуть всё в другую сторону». Надо разобраться, как это происходит. И для этого точно есть какие-то готовые решения.

С переворачиванием интерфейса — ничего не понятно. С переводами вроде всё понятно. Остановимся на этом подробнее.

Первым делом я добавил тегу html атрибут dir="rtl":

<html dir="rtl">

Я осознал, что совсем не понимаю, что происходит. Всё изменилось, но не совсем так, как я ожидал. По какому принципу выстраиваются элементы друг за другом?

Базовое направление (base direction)

Он не очень осмысленный, но наглядный: Рассмотрим один и тот же простой кусок вёрстки в LTR и RTL.

<table> <tr> <td> Hello world </td> <td> <button>Hello</button> <button>world</button> </td> </tr>
</table>

Как видно на скриншоте, атрибут dir (как и css-свойство direction) задаёт:

  • Выравнивание контента (text-align);
  • Направление потока — в какую сторону друг за другом располагаются inline-block элементы;
  • Порядок элементов в таблице, флексах, гридах.

Потому что порядок символов в строке определяется другим алгоритмом. Элементы поменяли порядок, но символы в словах всё равно расположены как обычно.

Последовательность символов внутри строки

Физически в строке символы расположены последовательно, но за итоговое отображение этой последовательности на экране отвечает unicode bidirectional algorithm.

Вкратце:

  1. Для каждого символа в строке вычисляется направленность;
  2. Строка бьётся на блоки одинаковой направленности;
  3. Блоки выстраиваются в порядке, заданном базовым направлением.

На направленность каждого символа влияет его тип и направленность соседних символов.

Три типа символов

Их направленность заранее определена — для большинства символов это LTR, для арабских и иврита — RTL. 1) Сильно направленные (или строго типизированные, strongly typed) — например, буквы.

Слова на картинке целиком строго типизированы:

Их направленность не задана явно, они направлены так же, как соседние сильно направленные символы. 2) Нейтральные — например, знаки пунктуации или пробелы.

Запятая между направленными слева направо «o» и «w» в строке «Hello, world» принимает их направленность и при базовом LTR, и при RTL:

Такой символ принимает базовую направленность. Но что, если нейтрально направленный символ попадает между двумя сильно направленными символами разной направленности?

Вот тут расположение «++» в одном случае между однонаправленными «C» и «a», а в другом — между разнонаправленными «C» и арабским «و», приводит к разному результату:

То же самое случается с нейтральными символами в конце строки:

Они имеют свою направленность, но никак не влияют на окружающие символы. 3) Слабо направленные (или слабо типизированные, weakly typed) — например, числа.

Непрерывные слова из цифр выстраиваются слева направо, но два числа подряд, разделённые нейтральным символом, будут идти друг за другом справа налево, если задана базовая RTL–направленность:

Ещё более наглядный случай — число, в котором разряды разделены пробелом:

При этом допускается разделять числа точкой, запятой, двоеточием — эти разделители тоже слабо направлены (подробнее можно посмотреть в спецификации):

Направленные блоки (directional run)

Эти блоки выстраиваются друг за другом в порядке, определённым базовым направлением: Последовательные символы одинаковой направленности объединяются в блоки (directional run).

Слабо направленные числа, несмотря на то, что имеют свою направленность, не влияют на формирование блоков, что может приводить к такому результату — они продолжают предыдущий направленный блок:

Зеркальные символы

Некоторые символы в разных контекстах имеют разную форму — например, открывающая скобка в RTL будет выглядеть как закрывающая в LTR (что логично, ведь контент в скобках будет идти после — то есть, слева от неё).

Например, если скобка висит в конце строки: В большинстве случаев это не создаёт проблем, но если скобки случайно окажутся разной направленности, визуально они будут смотреть в одну сторону.

Берём порядок под контроль

Как мы увидели выше, часто текст по этим правилам форматируется не так, как нам хотелось бы.

В этом случае нам пригодятся инструменты для встраивания желаемого направления в существующий контекст или переопределения направлений конкретных символов.

Изоляция (isolate)

Это глобальный атрибут, он применим к любому элементу. С заданием базового направления мы уже познакомились выше: это делает атрибут dir.

Контент внутри направлен согласно значению атрибута, а внешняя направленность самого контейнера становится нейтральной. dir создаёт новый уровень встраивания (embedding level) и изолирует содержимое от внешнего контекста.

Явная установка атрибута dir позволяет избежать почти всех проблем форматирования смешанного текста:

أنا أحب <span dir="ltr">C++</span> و Java

Тогда направление содержимого определится с помощью «некоторой эвристики» — оно просто возьмётся у первого попавшегося строго типизированного символа. Если направленность контента неизвестна заранее, можно указать auto в качестве значения атрибута dir.

<p dir="auto"></p>

Аналогично работает тег <bdi> и css-правило unicode-bidi: isolate:

<span>Landmark: <bdi>{name}</bdi> — {distance}</span>

Встраивание (embed)

Но это на практике не нужно почти никогда. Можно открыть новый уровень встраивания без изоляции — правило unicode-bidi: embed в комбинации с нужным значением правила direction определяют и направление внутри элемента, и его направленность снаружи.

Переопределение (override)

Переопределяет направление каждого символа внутри элемента. <bdo dir="rtl"> или unicode-bidi: bidi-override; direction: rtl. Нужно использовать крайне редко (например, если нужно поменять местами два конкретных символа) и не забывать изолировать дочерние элементы.

<bdo dir="rtl">Hello, world!</bdo>

Чтобы он вёл себя как isolate снаружи, но как bidi-override внутри, нужно использовать unicode-bidi: isolate-override. При этом снаружи элемент трактуется как сильно направленный.

Управляющие символы (marks)

Например, это могут быть просто невидимые сильно направленные символы, &lrm; и &rlm; (/ или \u200e/\u200f). Вставка управляющих символов — неприятный способ, но он полезен, когда у нас нет доступа к разметке, но есть доступ к контенту. Они помогают задать нужное направление нейтральному символу.

Например, в этом случае, чтобы восклицательный знак в конце строки принял направление LTR, нужно, чтобы он находился между двумя LTR символами:

<span dir="rtl">Hello, world!&lrm;</span>

Для изоляции — LRI/RLI, для переопределения — LRO/RLO, и т.д. Также любая описанная выше логика реализуется через управляющие символы. — смотри подробное руководство по управляющим символам.

Поддержка браузерами

Кроме того, спецификация этих правил всё ещё на стадии Editor's Draft. К сожалению, в IE тег <bdi>, dir="auto" и соответствующие им правила CSS не поддерживаются.

Но лучше, конечно, так не делать. Если нужен аналог dir="auto", работающий в любом браузере, можно парсить контент регуляркой и выставлять атрибут dir самостоятельно.

HTML или CSS?

Направление текста — это не стилизация, это часть контента. Однозначно, управлять направлением текста по возможности нужно через HTML–атрибут dir и тег <bdi>, а не через правила CSS. Страница может быть вставлена через какой-нибудь instant view или быть прочитана через RSS–reader.

Перед заключением: немного боли

Но знание теории не освобождает от необходимости страдать. Мы познакомились с теорией.

Мы пишем код слева направо. Главная проблема, с которой я столкнулся на первых же минутах разработки под RTL–язык, это его чужеродность. Поэтому, как только в это пространство попадает арабский язык, всё плохо и больно: Моя система, браузер и редактор работают слева направо, все наши внутренние продукты — слева направо.

Манипуляции с текстом

Или хотя бы выделить и скопировать его часть? Если символы на экране расположены не в том порядке, в каком они на самом деле располагаются в строке, что будет, если попытаться редактировать двунаправленный текст?

Попробуйте сами: Ничего хорошего.

2 km
a‮z‭b‮y‭c‮x‭d‮w‭e‮v‭f‮u‭g‮t‭h‮s‭i‮r‭j‮q‭k‮p‭l‮o‭m‮n‭ Landmarks: دبي مارينا مول — 600 m, داماك العقارية — 1.

Манипуляции с кодом

И то же самое при правке кода в редакторе и код-ревью — боль.

Даже в порядке элементов в массиве нельзя быть уверенным:

Или того хуже, код вообще не выглядит валидным:

Можно довести до абсурда:

Снова берём всё под контроль

Символы могут быть расположены визуально правильно в редакторе или в почтовом клиенте, но на итоговой странице правильно их расположить не получится, потому что логический порядок нарушен. В любой ситуации есть только один источник правды — логическое расположение символов в строке.

Посмотреть, как изначально расположены символы в строке и почему они визуально расположились именно так, позволяет инструмент на сайте Юникода: http://unicode.org/cldr/utility/bidi.jsp

Итого

Мы познакомились с правилами, по которым определяется порядок элементов и порядок символов в строке, и узнали, как можно на него влиять.

Что нужно обязательно помнить:

  1. dir="rtl" задаёт направление потока, выравнивает текст как text-align: right, меняет порядок ячеек таблицы, флексов и гридов. За последовательность символов в строке отвечает unicode bidirectional algorithm;
  2. Каждый символ имеет тип направленности — строго типизированные (буквы), слабо типизированные (цифры) и нейтральные (знаки пунктуации и пробелы);
  3. Проблемы возникают чаще всего на границе между разными типами символов.

На практике вся эта теория выливается в одно простое правило:

При смешанной направленности нужно явно изолировать уровни встраивания с помощью атрибута dir, а если контент неопределённой направленности — использовать <bdi> и dir="auto".

И это обязательно причинит тебе боль. Также нужно готовиться к тому, что в редакторе текст может выглядеть не совсем так, как будет выглядеть в браузере.

Что дальше?

Как быстро сделать прототип RTL-версии и какие выбрать решения для продакшна. В следующей статье я расскажу о нашем практическом опыте. Как быть заранее готовым, но какие моменты невозможно предусмотреть.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть