Привет. Мы недавно перевели на арабский язык 2ГИС Онлайн, и хотим поделиться своим опытом адаптации интерфейса под RTL (right-to-left). Это будет актуально и для иврита, и для персидского языка.
Я разделю этот опыт на две статьи — теоретическую и практическую. Сегодня — больше про теорию. Я расскажу, зачем нам понадобилось переворачивать весь интерфейс, что для разработчика интерфейсов значит фраза «сделать арабскую версию» и как справиться с арабским языком, смешанным с английским. Особое внимание уделю алгоритму, по которому строится отображение текста смешанной направленности — unicode bidirectional algorithm.
Зачем это всё?
Кажется, ценность в адаптации интерфейса под «справа налево» такая же, как у адаптации на любой другой популярный язык, но это не совсем так.
Различие между английской и русской версией небольшое — чаще всего, это просто переведёный текст. Пользовательский опыт, за исключением редких мелочей, в целом не отличается. Различие же между арабской и английской версией огромное.
Всего 0.6% интернет-ресурсов в мире содержат арабский контент. Однако, на арабском говорит больше 5% пользователей интернета, и эта доля стремительно растёт. Привычное направление чтения для них — справа налево. Какие ощущения у них от современного веба? Точно такие же, как у носителя русского языка при пользовании RTL–интерфейсом. Выбери себе метафору сам — может, это как садиться за руль праворульной машины, когда постоянно водишь леворульную. Или как зайти однажды в 2ГИС и увидеть, что карточки и поиск — справа:
Если мы хотим, чтобы наш сервис был максимально удобным для всех пользователей, адаптировать его под RTL нужно обязательно.
В чём состоит задача?
С первого взгляда она мне показалась необъятной — нужно переделать весь интерфейс под требования, которые никто не сможет нормально объяснить.
Посмотрев несколько примеров арабских сайтов, я понимаю, что сделать арабскую версию — это:
- Перевести данные на арабский язык. Эта часть понятнее всего, но легче не становится — это огромные объёмы данных;
- Перевести интерфейс на арабский язык. Для нас это не так просто, потому что до этого мы переводили только с русского, а переводчиков с русского на арабский у нас нет. Придётся сначала переводить строки и комментарии на английский, а потом — с английского на арабский;
- Адаптировать весь интерфейс под «справа налево». Это вроде просто «перевернуть всё в другую сторону». Надо разобраться, как это происходит. И для этого точно есть какие-то готовые решения.
С переводами вроде всё понятно. С переворачиванием интерфейса — ничего не понятно. Остановимся на этом подробнее.
Первым делом я добавил тегу 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) Сильно направленные (или строго типизированные, strongly typed) — например, буквы. Их направленность заранее определена — для большинства символов это LTR, для арабских и иврита — RTL.
Слова на картинке целиком строго типизированы:
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">{comment}</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)
Вставка управляющих символов — неприятный способ, но он полезен, когда у нас нет доступа к разметке, но есть доступ к контенту. Например, это могут быть просто невидимые сильно направленные символы, ‎
и ‏
(
/
или u200e
/u200f
). Они помогают задать нужное направление нейтральному символу.
Например, в этом случае, чтобы восклицательный знак в конце строки принял направление LTR, нужно, чтобы он находился между двумя LTR символами:
<span dir="rtl">Hello, world!‎</span>
Также любая описанная выше логика реализуется через управляющие символы. Для изоляции — LRI/RLI, для переопределения — LRO/RLO, и т.д. — смотри подробное руководство по управляющим символам.
Поддержка браузерами
К сожалению, в IE тег <bdi>
, dir="auto"
и соответствующие им правила CSS не поддерживаются. Кроме того, спецификация этих правил всё ещё на стадии Editor's Draft.
Если нужен аналог dir="auto"
, работающий в любом браузере, можно парсить контент регуляркой и выставлять атрибут dir
самостоятельно. Но лучше, конечно, так не делать.
HTML или CSS?
Однозначно, управлять направлением текста по возможности нужно через HTML–атрибут dir
и тег <bdi>
, а не через правила CSS. Направление текста — это не стилизация, это часть контента. Страница может быть вставлена через какой-нибудь instant view или быть прочитана через RSS–reader.
Перед заключением: немного боли
Мы познакомились с теорией. Но знание теории не освобождает от необходимости страдать.
Главная проблема, с которой я столкнулся на первых же минутах разработки под RTL–язык, это его чужеродность. Мы пишем код слева направо. Моя система, браузер и редактор работают слева направо, все наши внутренние продукты — слева направо. Поэтому, как только в это пространство попадает арабский язык, всё плохо и больно:
Манипуляции с текстом
Если символы на экране расположены не в том порядке, в каком они на самом деле располагаются в строке, что будет, если попытаться редактировать двунаправленный текст? Или хотя бы выделить и скопировать его часть?
Ничего хорошего. Попробуйте сами:
Landmarks: دبي مارينا مول — 600 m, داماك العقارية — 1.2 km
azbycxdwevfugthsirjqkplomn
Манипуляции с кодом
И то же самое при правке кода в редакторе и код-ревью — боль.
Даже в порядке элементов в массиве нельзя быть уверенным:
Или того хуже, код вообще не выглядит валидным:
Можно довести до абсурда:
Снова берём всё под контроль
В любой ситуации есть только один источник правды — логическое расположение символов в строке. Символы могут быть расположены визуально правильно в редакторе или в почтовом клиенте, но на итоговой странице правильно их расположить не получится, потому что логический порядок нарушен.
Посмотреть, как изначально расположены символы в строке и почему они визуально расположились именно так, позволяет инструмент на сайте Юникода: http://unicode.org/cldr/utility/bidi.jsp
Итого
Мы познакомились с правилами, по которым определяется порядок элементов и порядок символов в строке, и узнали, как можно на него влиять.
Что нужно обязательно помнить:
dir="rtl"
задаёт направление потока, выравнивает текст какtext-align: right
, меняет порядок ячеек таблицы, флексов и гридов. За последовательность символов в строке отвечает unicode bidirectional algorithm;- Каждый символ имеет тип направленности — строго типизированные (буквы), слабо типизированные (цифры) и нейтральные (знаки пунктуации и пробелы);
3.Проблемы возникают чаще всего на границе между разными типами символов.
На практике вся эта теория выливается в одно простое правило:
При смешанной направленности нужно явно изолировать уровни встраивания с помощью атрибута
dir
, а если контент неопределённой направленности — использовать<bdi>
иdir="auto"
.
Также нужно готовиться к тому, что в редакторе текст может выглядеть не совсем так, как будет выглядеть в браузере. И это обязательно причинит тебе боль.
Что дальше?
В следующей статье я расскажу о нашем практическом опыте. Как быстро сделать прототип RTL-версии и какие выбрать решения для продакшна. Как быть заранее готовым, но какие моменты невозможно предусмотреть.
Автор: Vladislav Sapozhnikov