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

ytg7zsuaysiof0wucp6zotzylce.jpeg

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

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


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

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

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

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

wlpvj8nv9mdmxm85l5-opmicdlg.jpeg

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


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

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

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


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

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

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


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


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

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

Hello world

bdubmddkc5royyuqo8hsjclkwc0.png

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


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

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


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

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

Вкратце:


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

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


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

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

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

6p7wwauepjxfiwhf7ndcacb0zfa.png

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

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

qkmgpb1q7v_yz3h6-usq9s1xp-y.png

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

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

lpgghhgl4g_mhfheei0z35n3fqc.png

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

wpwpm1avbacyil5plgbbfigrx3a.png

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

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

miinjoy4aq6cejzbanv-cqufl9u.png

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

6zd2kcp1a5k9baijp-mqzyduhrm.png

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

aqd4fuhsz6nmk974s8lqekgjuug.png


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

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

2qlj-hpo_wijuuj4ea1fg0zufpe.png

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

s-itum672yhou_d2cuc1uxihpfk.png


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

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

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

wphcfystnr_yonzj0slb31xnapg.png


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

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

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


Изоляция (isolate)

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

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

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

أنا أحب C++ و Java

tter8sn9wocazj-vqrcxmp8d994.png

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

{comment}

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

Landmark: {name} — {distance}

7lxmj0njbmtpoagkovykveddaco.png


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

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


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

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

Hello, world!

gxkv7h4tyv2nj0pygkqie5phs-e.png

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


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

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

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

Hello, world!‎

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


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

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

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


HTML или CSS?

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


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

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

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


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

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

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


Landmarks: دبي مارينا مول — 600 m, داماك العقارية — 1.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‭

gtxc5usn-c-uwknmskfw7-lsk2g.gif


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

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

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

50gl4-ruade4wc37ihwflzlp2ha.png

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

ncmxx9q4cmguxzxiwtx1yeegh4i.png

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

i6bgupig_og0p31ngi1q5ts1q4u.png


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

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

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

jhi9clu7baep5a5wfevsxmxox5a.jpeg


Итого

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

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


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

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


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

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


Что дальше?

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

© Habrahabr.ru