Как стать автором
Обновить
55.33
iSpring
Платформа для корпоративного обучения

Переводим пользовательский интерфейс на RTL: быстро, качественно, недорого

Уровень сложностиПростой
Время на прочтение9 мин
Количество просмотров899

Всем привет! Меня зовут Арина, я - frontend-разработчик в отделе разработки конструкторов iSpring. Наша команда занимается разработкой и развитием продукта iSpring Page - облачной платформы для создания курсов.

С помощью наших инструментов многие компании обучают своих сотрудников по всему миру. Наши продукты уже локализованы на 33 языка. Большинство языков, которые мы внедряли в проект, были на латинице и кириллице, из интересных есть, например, тайский, каталонский и узбекский языки. В 2023 году наша компания начала активно развиваться на рынке Ближнего Востока, и поэтому возник вопрос поддержки арабского языка! А это означало, что нам нужно не только внедрить переводы, но и развернуть или “отзеркалить” пользовательский интерфейс всех наших продуктов.

Сразу обозначу пару аббревиатур, которые буду использовать в статье. Они описывают, как текст и элементы интерфейса должны быть организованы на веб-странице:

  • LTR (Left to Right) - это стандарт для языков, где чтение и написание текста происходят слева направо. Например, русский и английский.

  • RTL (Right to Left) - используется для языков, где текст читается и пишется справа налево. Например, арабский и иврит.

Ниже представлен пример того, как одно из наших приложений выглядит в LTR:

Плеер лонгрида в LTR
Плеер лонгрида в LTR

В RTL тот же самый продукт выглядит следующим образом:

Плеер лонгрида в RTL
Плеер лонгрида в RTL

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

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

Поэтому в данной статье я расскажу вам:

Какими могут быть варианты реализации поддержки RTL языков?

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

Отдельные CSS для LTR и RTL

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

Вся суть подхода заключается в разделении CSS на 2 файла: для LTR и RTL версии. При этом на самой странице подключается только 1 вариант (в зависимости от языка сайта). Такой подход будет хорошо работать, если язык страницы статичный, т.е. у пользователя нет возможности его изменить.

Из хорошего:

  • быстро реализуется

  • на странице будут грузиться только нужные стили

Из плохого:

  • все стили нужно дублировать в оба файла, что значительно увеличивает сложность и длительность разработки такого продукта

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

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

Дополнительный CSS для RTL

Можно пойти дальше и немного усложнить первый вариант - избавимся от дублирования стилей.

При таком подходе мы все еще делим CSS на 2 файла: файл с основными стилями страницы и файл для RTL-версии. В файле с основными стилями мы собираем стили для LTR-версии (позиционирование, отступы и прочие красивости), а во втором файле переопределяем исключительно позиционирование и отступы под RTL-вариант отображения.

Такой вариант по-прежнему быстрый и дешевый + мы избавляемся от дублирования стилей в оба файла, но также есть ряд минусов, которые хочется улучшить:

  • все еще приходится поддерживать 2 файла стилей вместо одного

  • часть стилей (связанная с позиционированием и отступами) в RTL-версии будет постоянно переопределяться

RTL и LTR стили в одном CSS

То есть поддерживать только один файл стилей и подключать на обе версии сайта только его:

Такой вариант реализации тоже имеет место быть, но:

  • он достаточно неоптимальный (т.к. мы постоянно грузим лишние стили)

  • его сложно поддерживать (т.к. в одном файле будет находиться большой объем кода)

Как реализовали мы?

Атрибут dir

Атрибут dir устанавливает, в каком направлении будет выводиться текст внутри элемента: слева направо или справа налево. 

Суть в том, что на основании выбранной локали пользователя мы задаем базовое направление текста для всего документа. Проще говоря, применяем атрибут dir для тега html.

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

После того, как мы меняем значение атрибута dir, браузер автоматически изменяет не только направление текста, но и его выравнивание: для LTR версии текст выравнивается по левому краю, для RTL - по правому.

При изучении направленности символов, управляющих символов и атрибута dir в частности мы вдохновлялись статьей ребят из 2ГИС, которую можно найти тут. Ребята, если вы это читаете, мы шлем вам плюсик в карму за крутую и полезную статью!

Изменения в блочной модели

Наше следующее изменение коснулось восприятия блочной модели. При упоминании словосочетания “блочная модель” все мы привыкли представлять что-то подобное:

Блочная модель, построенная на физических свойствах CSS
Блочная модель, построенная на физических свойствах CSS

При написании CSS-свойств для элементов страницы мы всегда оперировали физическими свойствами. Например, направлениями “left” и “right” - это прекрасно работало и работает до сих пор. Но такие свойства не дадут вам желаемого эффекта при смене направления текста, т.е. при отзеркаливании интерфейса пользователя.

Дело в том, что физические свойства CSS будут добавлять отступы и бордеры с фиксированной стороны. Например, если мы добавляем padding-left, то мы явно задаем отступ слева и для LTR, и для RTL версий страницы. Конечно, мы можем переопределить стили, и задать для RTL версии свойство padding-right, но это не то, что нам нужно и поддерживать такой вариант не сильно приятно, т.к. подобных изменений будет большое количество.

Поэтому мы пришли к использованию логических свойств CSS вместо физических. Что это значит? Вообще блочная модель, построенная на логических свойствах, выглядит следующим образом:

Блочная модель, построенная на логических свойствах CSS
Блочная модель, построенная на логических свойствах CSS

Если кратко, то:

  • Мы перестали думать терминами право-лево и верх-низ

  • Заменили их на inline-start, inline-end и block-start, block-end

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

  • позиционирование

  • выравнивание текста

  • обтекание

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

Подробно про логические свойства написали ребята из Веб-стандартов, ознакомиться можно тут. А тут ребята из Skillfactory подробно написали про свой опыт использования логических свойств с примерами и картинками.

Автоматизация

Объем кодовой базы большой, поддержать RTL нам нужно во всех продуктах и переводить физические свойства CSS на логические вручную - казалось бы вечностью. Поэтому мы сразу задумались об автоматизации.

Для ускорения нашей работы и повышения ее надежности мы пришли к доработке конфига линтера и использованию плагина stylelint-use-logical:

{
  "plugins": [
    "stylelint-use-logical"
  ],
  "rules": {
    "csstools/use-logical": [
      "always",
      {
        "except": [
          "float"
        ]
      }
    ]
  }
}

После прогона линтера наш код существенно изменился и большинство физических свойств CSS были заменены на логические. Ниже представлен пример физических свойств:

.block {
  margin-top: 20px;
  padding-left: 10px;
}

А это то, что получилось после прогона линтера:

.block {
  margin-block-start: 20px;
  padding-inline-start: 15px;
}

Казалось бы, фича готова и львиная часть работы позади! Давайте посмотрим на поддержку логических свойств.

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

А теперь самое интересное 🙂 В пользу быстрой и качественной поддержки RTL мы отказались от:

  • IE

  • Safari 14

Потому что доля пользователей продукта с браузера IE и Safari 14 крайне низкая (мы проверяли), а поддержка логических свойств выглядит следующим образом:

Скриншот для ленивых
Скриншот для ленивых

Как же мы были счастливы, наконец-то отказавшись от поддержки IE!

Какими правилами руководствовались?

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

Мы используем Григорианский календарь

Рабочая неделя все так же начинается с понедельника, несмотря на то, что ряд стран начинает ее с воскресенья.

Знак % всегда справа

Здесь, думаю, понятнее будет с примером:

Телефон, email, дата, время всегда пишутся слева направо

Иконки воспринимаем как  “имя собственное”, а по правилам мы их не зеркалим

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

С какими трудностями столкнулись и как их решили?

Как отображать компонент?

Если с направлением текста браузер справляется самостоятельно, то иконки за нас уже никто не отзеркалит.

Поэтому если есть необходимость развернуть иконки или при разработке нам не удалось обойтись только логическими свойствами, то мы решили прокидывать в компоненты пропс direction:

type Direction = ‘ltr’ | ‘rtl’

type ComponentProps = {
  ...
  direction: Direction,
  ...
}

А внутри компонента уже в зависимости от значения пропса условно рендерим LTR или RTL иконку.

Двунаправленный текст

Все понятно, когда весь текст написан на одном языке: LTR или RTL. Начинаются танцы с бубном, когда текст смешанный:

Тут вариантов решения 2:

  • Доработать разметку с использованием dir=”<direction>”

  • Добавить управляющие символы: &lrm; и &rlm;

При работе с переводами мы придерживались второго варианта.

Шорткаты в CSS

Есть нюанс при работе плагина stylelint-use-logical с шорткатами padding и margin. Дело в том, что padding и margin - это тоже физические свойства, и плагин не конвертирует их в логические (внезапно).

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

{
  ...
  "property-disallowed-list": [
    [
      "padding",
      "margin"
    ],
    {
      "message": "Use logical properties: margin-block, margin-inline, padding-block, padding-inline"
    }
  ],
  ...
}

А для уже существующих в кодовой базе шорткатов мы подготовили скрипт, который заменил текущие шорткаты на соответствующие логические свойства CSS. Ну не красота ли?

CSS-переменные не работают

Мы отказались от IE и старых версий Safari, но уже в процессе тестирования обнаружили, что CSS-переменные в шорткатах не работают в Safari 14.1. А значит - нужно править! Для начала мы запретили использовать переменные в шорткатах:

{
  ...
  "declaration-property-value-disallowed-list": {
    "/^(margin|padding)-(block|inline)$/": ["/var/"]
  },
  ...
}

А если есть необходимость использования CSS-переменной при определении отступов - мы используем полные версии свойств, например, margin-block-start или padding-inline-end. С этими свойствами проблем с поддержкой нет.

Прозрачность текста

Да, на это мы теперь тоже обращаем внимание. Если с кириллицей и латиницей проблем с полупрозрачным текстом не возникает, то с арабским все интереснее!

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

  • Скорректировать letter-spacing и изменить расстояние между символами

  • Использовать цвет без прозрачности

Долго не думая, мы выбрали второй вариант 🙂

Как сделать еще лучше?

К сожалению или к счастью, не все наши проекты реализованы с помощью известных фреймворков. Часть наших продуктов написана на самописном реактоподобном движке (и тем интереснее, а если вам тоже интересно - дайте знать и мы вам расскажем). И нам приходилось прокидывать в некоторые компоненты настройку направления текста в качестве пропса. Но если вы используете, например, React, то для реализации подобного функционала логично было бы использовать React Context.

Ведь в таком случае:

  • значение контекста наследуется дочерними компонентами

  • контекст поддерживает вложенность

  • выглядит это элегантнее, чем пропсы компонентов

Использование контекста могло бы значительно упростить поддержку RTL для мультиязычных аккаунтов. Это когда язык приложения LTR, а пользователь просматривает или редактирует RTL-документы и наоборот.

Например так один из наших редакторов выглядит, если язык приложения русский, а контент создают на арабском:

И наоборот, если в арабском аккаунте создают контент, например, на русском:

Как поддержка RTL повлияла на работу других команд?

Что мы имеем в результате:

  • 3 месяца разработки

  • 140 закрытых задач

  • 300+ страниц, переведенных на RTL

  • 2500+ измененных файлов

  • очень много сэкономленного времени мануальной работы 💪

  • полная готовность к внедрению иврита и персидского языка 😎

Как мы и планировали, реализовали фичу так, что для ее поддержки не нужно предпринимать каких-либо дополнительных усилий. Ниже расскажу, как изменилась работа в командах:

  • При разработке мы теперь всегда используем логические свойства (спасибо линтеру)

  • Дизайны по-прежнему собираются в LTR (отдельно RTL версию нам не готовят)

  • При подготовке дизайна в случае необходимости сразу добавляются иконки для LTR и RTL версий (тут придерживаемся правил, описанных выше)

  • При подготовке переводов появился нюанс с добавлением управляющих символов

  • При тестировании теперь проверяем каждую фичу и в LTR, и в RTL версии

Полезные ссылки

Ниже оставила ряд полезных ссылок, которые помогли нам при поиске своего варианта реализации:

С удовольствием отвечу на ваши вопросы! Если вы тоже реализовывали поддержку RTL в своих проектах, поделитесь, какие у вас были трудности и как их решили? :)

Теги:
Хабы:
Всего голосов 9: ↑9 и ↓0+10
Комментарии2

Публикации

Информация

Сайт
www.ispring.ru
Дата регистрации
Дата основания
2001
Численность
201–500 человек
Местоположение
Россия
Представитель
Приёмко Андрей