уязвимость со скриптом справа налево арабский
Наш канал на Youtube
Здравствуйте все, кто работает в Индизайне!
Извещаем вас о том, что на youtube.com начал работу канал «InDesign Мастерская вёрстки». Там уже размещены более 60 видео с полным описанием как работать с программой DoTextOK. Другие интересные темы, касающиеся работы …
Хитрости и секреты, приемы работы, уроки
Особенности новой версии Индизайна
Описание плагинов, информация о плагинах для работы с Adobe InDesign
Готовые к использованию скрипты для Adobe InDesign
Описание языка, приёмов и методов программирования для Adobe InDesign
Скрипты для арабского текста
На форумах достаточно часто возникает вопрос: — как в Индизайне повернуть арабский текст наоборот (не зеркально повернуть, а поменять порядок букв) и затем следует типичный ответ — «воспользуйтесь Indesign ME (Middle Eastern) с поддержкой написания справа налево». Ответ верный, но где же найти этот Индизайн-МЕ? Не выкладывать же за него многие сотни зеленых… Если такого Индизайна нет, то можно воспользоваться коллекцией скриптов для обычного InDesign CS4, которую качать надо отсюда: http://www.thomasphinney.com/2009/01/adobe-world-ready-composer/ (Блог Tомаса Финни ). Скрипты переворачивают символы и абзацы справа-налево (и наоборот) и создают paragraph-стили для текста. На этой же странице также содержится интереснейшая информация по данному вопросу.
Еще один маленький скрипт по созданию стиля для арабского текста написал Thomas Silkjær. Его можно скачать здесь: http://indesigning.net/right-to-left-arabic-hebrew-hindi-in-indesign-cs4-none-me
Чтобы этот скрипт у вас работал, надо будет в одной из последних строк: appliedFont:»Batang» заменить Batang на имя вашего шрифта.
Кстати, интересную информацию об особенностях версии InDesign CS4 ME можно найти на этом сайте: http://www.fontworld.com/me/indesignme.html
Спасибо Максиму Белецкому за предоставленную информацию по этому вопросу.
Справа налево. Что такое dir=rtl и как приручить арабский язык
Привет, Хабр. Мы недавно перевели на арабский язык 2ГИС Онлайн, и хотим поделиться своим опытом адаптации интерфейса под RTL (right-to-left). Это будет актуально и для иврита, и для персидского языка.
Я разделю этот опыт на две статьи — теоретическую и практическую. Сегодня — больше про теорию. Я расскажу, зачем нам понадобилось переворачивать весь интерфейс, что для разработчика интерфейсов значит фраза «сделать арабскую версию» и как справиться с арабским языком, смешанным с английским. Особое внимание уделю алгоритму, по которому строится отображение текста смешанной направленности — unicode bidirectional algorithm.
Зачем это всё?
Кажется, ценность в адаптации интерфейса под «справа налево» такая же, как у адаптации на любой другой популярный язык, но это не совсем так.
Различие между английской и русской версией небольшое — чаще всего, это просто переведёный текст. Пользовательский опыт, за исключением редких мелочей, в целом не отличается. Различие же между арабской и английской версией огромное.
Всего 0.6% интернет-ресурсов в мире содержат арабский контент. Однако, на арабском говорит больше 5% пользователей интернета, и эта доля стремительно растёт. Привычное направление чтения для них — справа налево. Какие ощущения у них от современного веба? Точно такие же, как у носителя русского языка при пользовании RTL–интерфейсом. Выбери себе метафору сам — может, это как садиться за руль праворульной машины, когда постоянно водишь леворульную. Или как зайти однажды в 2ГИС и увидеть, что карточки и поиск — справа:
Если мы хотим, чтобы наш сервис был максимально удобным для всех пользователей, адаптировать его под RTL нужно обязательно.
В чём состоит задача?
С первого взгляда она мне показалась необъятной — нужно переделать весь интерфейс под требования, которые никто не сможет нормально объяснить.
Посмотрев несколько примеров арабских сайтов, я понимаю, что сделать арабскую версию — это:
С переводами вроде всё понятно. С переворачиванием интерфейса — ничего не понятно. Остановимся на этом подробнее.
Первым делом я добавил тегу html атрибут dir=»rtl»:
Всё изменилось, но не совсем так, как я ожидал. Я осознал, что совсем не понимаю, что происходит. По какому принципу выстраиваются элементы друг за другом?
Базовое направление (base direction)
Рассмотрим один и тот же простой кусок вёрстки в LTR и RTL. Он не очень осмысленный, но наглядный:
Как видно на скриншоте, атрибут dir (как и css-свойство direction ) задаёт:
Элементы поменяли порядок, но символы в словах всё равно расположены как обычно. Потому что порядок символов в строке определяется другим алгоритмом.
Последовательность символов внутри строки
Физически в строке символы расположены последовательно, но за итоговое отображение этой последовательности на экране отвечает 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 позволяет избежать почти всех проблем форматирования смешанного текста:
Если направленность контента неизвестна заранее, можно указать auto в качестве значения атрибута dir. Тогда направление содержимого определится с помощью «некоторой эвристики» — оно просто возьмётся у первого попавшегося строго типизированного символа.
Аналогично работает тег и css-правило unicode-bidi: isolate :
Встраивание (embed)
Можно открыть новый уровень встраивания без изоляции — правило unicode-bidi: embed в комбинации с нужным значением правила direction определяют и направление внутри элемента, и его направленность снаружи. Но это на практике не нужно почти никогда.
Переопределение (override)
Управляющие символы (marks)
Вставка управляющих символов — неприятный способ, но он полезен, когда у нас нет доступа к разметке, но есть доступ к контенту. Например, это могут быть просто невидимые сильно направленные символы, и ( / или \u200e / \u200f ). Они помогают задать нужное направление нейтральному символу.
Например, в этом случае, чтобы восклицательный знак в конце строки принял направление LTR, нужно, чтобы он находился между двумя LTR символами:
Также любая описанная выше логика реализуется через управляющие символы. Для изоляции — LRI/RLI, для переопределения — LRO/RLO, и т.д. — смотри подробное руководство по управляющим символам.
Поддержка браузерами
HTML или CSS?
Перед заключением: немного боли
Мы познакомились с теорией. Но знание теории не освобождает от необходимости страдать.
Главная проблема, с которой я столкнулся на первых же минутах разработки под RTL–язык, это его чужеродность. Мы пишем код слева направо. Моя система, браузер и редактор работают слева направо, все наши внутренние продукты — слева направо. Поэтому, как только в это пространство попадает арабский язык, всё плохо и больно:
Манипуляции с текстом
Если символы на экране расположены не в том порядке, в каком они на самом деле располагаются в строке, что будет, если попытаться редактировать двунаправленный текст? Или хотя бы выделить и скопировать его часть?
Ничего хорошего. Попробуйте сами:
Landmarks: دبي مارينا مول — 600 m, داماك العقارية — 1.2 km
azbycxdwevfugthsirjqkplomn
Манипуляции с кодом
И то же самое при правке кода в редакторе и код-ревью — боль.
Даже в порядке элементов в массиве нельзя быть уверенным:
Или того хуже, код вообще не выглядит валидным:
Снова берём всё под контроль
В любой ситуации есть только один источник правды — логическое расположение символов в строке. Символы могут быть расположены визуально правильно в редакторе или в почтовом клиенте, но на итоговой странице правильно их расположить не получится, потому что логический порядок нарушен.
Посмотреть, как изначально расположены символы в строке и почему они визуально расположились именно так, позволяет инструмент на сайте Юникода: http://unicode.org/cldr/utility/bidi.jsp
Итого
Мы познакомились с правилами, по которым определяется порядок элементов и порядок символов в строке, и узнали, как можно на него влиять.
Что нужно обязательно помнить:
На практике вся эта теория выливается в одно простое правило:
Также нужно готовиться к тому, что в редакторе текст может выглядеть не совсем так, как будет выглядеть в браузере. И это обязательно причинит тебе боль.
Что дальше?
В следующей статье я расскажу о нашем практическом опыте. Как быстро сделать прототип RTL-версии и какие выбрать решения для продакшна. Как быть заранее готовым, но какие моменты невозможно предусмотреть.
Справа налево. Как перевернуть интерфейс сайта под RTL
Настало время приступить к практике — перевернуть с минимальными усилиями весь интерфейс справа налево так, чтобы даже настоящий араб не почувствовал подвоха.
В этой статье я расскажу, как быстро сделать прототип, что сделать со сборкой CSS и какие костыли разложить в JS, замечу немного об особенностях перевода и локализации, напомню про логические свойства CSS и затрону тему RTL в CSS-in-JS.
Переворачиваем стили
Когда я применил к тегу атрибут dir=»rtl», поменялся только неявный порядок элементов — например, порядок ячеек таблицы или flex-элементов. С явно заданными в стилях значениями ничего не произошло.
Возьмём стили какой-нибудь нотификации, расположенной снизу справа:
dir=»rtl» никак не повлияет на эти стили — в RTL-версии тултип будет так же справа, хотя ожидается он слева.
Быстрый прототип
Для начала можно, не задумываясь, поменять все вхождения left на right и немножко поколдовать с shorthand значениями:
После этого нужно только задать dir=»rtl» и автоматически применятся только нужные правила. Всё работает и кажется, что почти всё готово к продакшену, но это решение годится только для быстрого прототипа:
Есть вариант лучше?
Нужно собирать стили для LTR и RTL раздельно. Тогда не нужно будет трогать селекторы и размер css почти не изменится.
Для этого я выбрал:
RTLCSS позволяет использовать директивы, чтобы контролировать обработку конкретных правил, например:
Какие ещё есть решения?
Все существующие решения почти не отличаются от RTLCSS.
Когда нужны директивы?
Когда стили не должны зависеть от направленности
Например, угол поворота стрелки, указывающей направление ветра:
Или фейд у номера телефона — числа всегда пишутся слева направо, значит, и градиент должен быть всегда справа:
Когда нужно центрировать иконку
Это частный случай предыдущего пункта. Если центрируем несимметричную иконку через отступы/позиционирование, мы смещаем её блок в сторону, и если это смещение отразить, иконка «съедет» в другую сторону:
Лучше в таких ситуациях центрировать иконку в самой svg:
Когда нужно изолировать целый виджет, который не должен реагировать на RTL
Есть ли вариант ещё лучше?
Решение с переворачиванием правил прекрасно работает, но возникает вопрос — а не костыль ли это? Зависимость стилей от направления — естественная задача для современного веба, и её актуальность с каждым годом растёт всё больше. Это должно было найти отражение в современных стандартах и подходах. И нашло!
Logical properties
Для адаптации вёрстки под разную направленность существует стандарт логических свойств в css. Он касается не только направлений слева направо и справа налево, но и направления сверху вниз, но его мы рассматривать не будем.
А ещё появляется долгожданный шортхенд для позиционирования:
Это уже доступно в firefox без флагов и в вебкитовых браузерах под флагом.
Минусы вытекают из плюсов — без плагинов код в большинстве браузеров невалиден, нужно проделать много работы, чтобы перевести большой существующий проект.
Как подключить?
Самый простой способ — postcss-logical. Без параметра dir собирает стили для обоих направлений аналогично postcss-rtl, с заданным параметром dir — только для указанной направленности:
Как убедить команду начать писать offset-inline-start вместо left?
RTL + CSS-in-JS = ️️ < paddingInlineStart: '4px' >(или < paddingLeft: '4px' >, если не удалось перейти на логические свойства) в объекты типа < paddingRight: '4px' >:
Пример с реактом
Оборачиваем каждый стилизуемый компонент в HOC, принимающий стили:
Он берёт из контекста направленность компонента и выбирает конечные стили:
А направленность в контекст прокидывает провайдер:
Похожий подход используют airbnb: https://github.com/airbnb/react-with-styles-interface-aphrodite#built-in-rtl-support, если на проекте уже используется aphrodite, можно воспользоваться этим готовым решением.
Для JSS всё ещё проще — нужно только подключить jss-rtl:
styled-components
Что делать, если мы работаем с шаблонными строками, а не с объектами? Всё сложно, но выход есть — вычислять название свойства из направления, заданного в props :
Но кажется проще перейти со строк на объекты, styled-components умеют это с версии 3.3.0.
Особенности перевода и локализации
Мы разобрались с технической частью. Изолировали контент неопределённой направленности, отзеркалили стили, расставили исключения в нужных местах, перевели тексты на арабский. Кажется, всё готово — при переключении языка весь интерфейс оказывается с другой стороны экрана, никакая вёрстка не едет и всё выглядит лучше, чем на любом арабском сайте.
Показываем настоящему арабу, и.
Оказывается, не каждый носитель арабского языка знает, что такое Twitter. Это касается почти всех слов на английском. Для такого случая есть арабский транслит: «تويتر».
Оказывается, в арабском свои запятые, и то, что мы везде по коду конкатенировали через «,», на арабском нужно конкатенировать через «،».
Оказывается, в некоторых мусульманских странах официальный календарь — исламский. Он лунный и обычной формулой перевода не обойтись.
Оказывается, в Дубае не бывает отрицательной температуры и знак «плюс» в прогнозе «+40» не имеет никакого смысла.
Не просто взял и отзеркалил стили
Иконки тоже автоматически не отзеркалятся. А без этого направленные иконки, такие как стрелки в галерее, могут смотреть в неправильную сторону. Представь себе, это единственный случай, в котором стрелки, сделанные через border, оправдали себя!
Для отражения иконок поможет незамысловатая трансформация:
Она, правда, не поможет, если в иконке содержатся буквы или цифры. Тогда придётся сделать две разные иконки и вставлять их условно:
А ещё, оказывается, не все элементы интерфейса нужно зеркалить. Например, в нашем случае мы решили оставить чекбоксы обычными:
Как из всего интерфейса выбрать такие элементы — не знаю. Тут может помочь только носитель языка, который скажет, что ему привычно, а что нет.
Пользовательский ввод
прикольная_картинка*U+202E*gnp.js → прикольная_картинкаsj.png
В таких случаях стоит отфильтровать неуместные utf-символы из строки.
Переворачиваем скрипты
В RTL-джаваскрипте синтаксис немного меняется. Цикл, который выглядел так:
Теперь нужно писать так:
Всё, что нужно делать — избегать понятий «left» и «right» в коде. Например, мы столкнулись с проблемами в подсчёте координат центра экрана — раньше все карточки висели слева, а теперь справа, но код приложения об этом не знал. Все расчёты и инлайн-стили нужно проводить, учитывая базовую направленность.
Смекалочка
В некоторых ситуациях сложно внедрить поддержку RTL в какой-то компонент системы. Тогда нужно попробовать адаптировать этот компонент под RTL снаружи, но оставить LTR внутри.
Например, у нас есть слайдер. Он поддерживает только положительные упорядоченные значения. Он может быть линейным и логарифмическим (в начале плотность значений меньше, чем в конце). Нужно отразить его с сохранением поведения шкалы.
Есть другой вариант — развернуть ось в другую сторону, изменив только передачу и получение значений из слайдера. Если это линейная шкала, вместо набора значений [10, 100, 1000] передадим набор [N-1000, N-100, N-10], а в хендлере преобразуем обратно. Для логарифмической шкалы вместо набора [10, 100, 1000] передадим [1/1000, 1/100, 1/10]:
Вот так слайдер стал поддерживать RTL, хотя он сам об этом не знает.
Storybook
В отличие от проверки вёрстки под каким-нибудь IE9, для проверки вёрстки под RTL не нужно запускать отдельный браузер. Можно даже верстать и видеть вёрстку LTR и RTL одновременно в одном окне. Для этого можно, например, сделать декоратор в сторибуке, который рендерит сразу две версии истории:
На скриншоте видно, что без изоляции text-overflow: ellipsis ведёт себя не так, как хотелось бы — лучше сразу пофиксить.
Намного проще поддерживать RTL сразу при вёрстке, чем потом тестировать абсолютно весь проект.
Нерешаемые проблемы
Знание теории не помогает решить абсолютно все задачи. Приведу один пример.
Подсказчик, появляющийся по направлению текста при вводе. Когда мы говорим о мультиязычном вводе, мы не можем заранее знать, с какой стороны выводить эту подсказку:
Нужно стараться избегать таких проблем на этапе дизайна и иногда отказываться от очевидных для LTR решений, никак не применимых в RTL. В данном кейсе можно при навигации по подсказкам подставлять весь текст целиком (как, например, делает Яндекс или Google).
Выводы
RTL — это не просто «перевернуть всё»
Нужно учитывать особенности языка, что-то переворачивать не нужно, что-то нужно адаптировать иначе. Где-то в логике совсем нужно отказаться от «право»/«лево».
Очень сложно что-то сделать без знания языка
Ты будешь думать, что всё готово, пока не покажешь свой проект настоящему носителю языка. Разрабатывай с точки зрения человека, не знающего никакого языка. Ведь даже такие очевидные для тебя слова, как, например, «Twitter», возможно, придётся переводить. И знаки препинания, оказывается, не на всей планете одинаковые.
Итоговый рецепт
Сложно описать в одном списке всё, о чём шла речь в двух статьях. Пошагового руководства не будет, но вот главное, что нужно сделать:
Готовься к тому, что больше всего времени придётся потратить на самые мелкие детали.
Быть заранее готовым — не сложно
И немного советов для тех, кто пока что не собирается адаптировать сайт под RTL, но хочет подстелить соломку:
Эти правила не должны доставить хлопот. Но если внезапно придёт желание запустить RTL-версию, это достанется намного дешевле, чем могло бы.