- Русские Блоги
- Адаптация макета Android RTL
- Что такое макет RTL
- Адаптация макета RTL
- Тест макета RTL
- RTL Support on Android. Here is all you need to know
- Getting Started
- XML Layouts
- Drawables
- 📌 Small Tip :
- res/values/integers.xml
- res/values-ldrtl/integers.xml
- Animations
- Java Files
- Extras
- Conclusion
- Локализация приложения и поддержка RTL. Доклад Яндекс.Такси
Русские Блоги
Адаптация макета Android RTL
Мои мужи очень близки друг другу в мире. — Цао Чжи, «Подарок белой лошади Ван Бяо»
Что такое макет RTL
Как показано на рисунке выше, RTL (справа налево) — это форма представления, которая начинается справа и заканчивается слева. Мы больше привыкли к просмотру LTR (слева направо) каждый день, но в арабоязычной семье Ближнего Востока их визуальные привычки прямо противоположны нашим. Они больше привыкли к визуальной форме справа налево.
В последнее время продукты, разработанные компанией, просто нуждаются в адаптации RTL. В этой статье кратко излагаются технические аспекты процесса адаптации RTL.
Адаптация макета RTL
1. Сначала добавьте поддержку в файл AndroidManifest.
2. Замените xxxLeft / xxxRight в файле ресурсов на xxxStart / xxxEnd.
В среде разработки AS вы можете передать Refactor—->Add RTL support where possible Быстрая замена. Следует отметить, что после завершения замены лучше всего найти xxxLeft / xxxRight с помощью команды grep, чтобы увидеть, не проскользнула ли рыба через сеть. Например, некоторые атрибуты, определенные в файле стилей, могут не заменить.
3. Переверните некоторые значки
Некоторые значки необходимо инвертировать в макете RTL, например похожие Значок должен отображаться как —> , Во-первых, позвольте художнику вырезать набор иконок специально для макета RTL и поместить его в каталог drawable-ldrtl. Или установив android:autoMirrored=»true» К сожалению, первое вызовет избыточность ресурсов изображения и увеличит размер apk, а второе требует уровня API 19 или новее.
Вот более общая схема. Для настройки через xml можно добавить в свойствах иконки
Затем объявите в value / integer.xml следующее:
Наконец, он объявлен в value-ldrtl / integer.xml следующим образом:
4. Проверьте, что Margin / Padding динамически установлен в коде.
похожий rightMargin Это необходимо заменить на setMarginEnd(end)
Gravity.LEFT/RIGHT Заменить Gravity.START/END
setPadding(left, top, right, bottom) Заменить setPaddingRelative(start, top, end, bottom)
может использовать команды
Быстро отфильтруйте места в коде, которые могут потребоваться исправления, а затем проверьте, нужно ли его изменять в соответствии с определенной логикой.
5. Специальная адаптация управления
- ViewPager
может использовать элементы управления с открытым исходным кодомhttps://github.com/duolingo/rtl-viewpager , Очень удобно заменить.
Однако на практике я обнаружил, что при объединении TabLayout для достижения верхней метки индикатора в представлении RTL метки будут выглядеть сжатыми вместе. Причина в следующем
- TextView
В случае отображения RTL, когда текстовое содержимое TextView является арабским, его можно хорошо адаптировать в большинстве случаев, но если текстовое содержимое не арабское, возникнут проблемы с выравниванием. Следовательно, необходимо добавить следующие атрибуты.android:textAlignment=»viewStart»
Эффект отображения, когда этот атрибут не установлен:
Эффект после настройки:
Тест макета RTL
Ближневосточные языки похожи на писания для большинства разработчиков, поэтому может быть неудобно наблюдать за эффектом адаптации. Существует возможность принудительно использовать макет RTL в параметрах разработчика системы Android. Даже если вы не переключите ближневосточный язык, вы можете легко увидеть эффект.
Источник
RTL Support on Android. Here is all you need to know
Apr 14, 2017 · 4 min read
There comes a point in an app’s lifecycle when you decide to widen your audience. Although your app works great and you have a huge number of daily downloads, not everyone is happy. Why 🤔? No RTL (Right to Left) Layout support.
Few languages such as Arabic, Hebrew, or Persian are written from Right to Left. To handle them, Android supports RTL layouts from API 17+ i.e., Android 4.2 (Jelly Bean) .
Getting Started
- In order to support RTL in your app, you first need to add android:supportsRtl=»true» to the element in your manifest file.
Woohoo… 🎉Done. Your app now supports RTL. But lets see how it looks. You can check your app in RTL mode by doing one of two things.
- Select one of the RTL languages as your device language.
- Or from Developer Options in Settings , check Force RTL layout Direction . I find this option more convenient but, do note, this does not change your System language.
XML Layouts
Yo u ’ll need to make the following changes in all your Layouts
- If your app only supports API ≥ 17 , replace all the layout_marginLeft/layout_marginReft/paddingLeft/paddingRight or any other Left and Right layout property with Start and End equivalent. For example android:paddingLeft will be replaced with android:paddingStart .
- If your app supports API then instead of replacing the Left and Right layout properties, add their Start and End layout property equivalent alongside.
There is a much easier way to do the above changes without going through all the files and then doing it manually. Android Studio can do this for you. Just go to Android Studio > Refactor > Add RTL support where possible…
I would recommend you checking your app once after applying this change as you might not want all your Layouts/Views to be RTL. If you want to force any layout to LTR then just add android:layoutDirection=»ltr» to that view.
Drawables
Time to search for all those drawables that you would want to be mirrored for RTL. Since resources can have variants based on device aspects like Orientation, Screen Density, API version, you can also have variants for different Layout Directions i.e., RTL or LTR. All you need to do is add the RTL variants of the drawables that you want to be mirrored.
📌 Small Tip :
If your Toolbar defines back arrow nav icon like this
app:navigationIcon=»?attr/homeAsUpIndicator»
This back button is consistent with Layout Directions.
But only works on API 23+ 😣, below that it just points Left.
And what if you don’t have RTL variant for your drawables? You can use android:autoMirrored=»true» . But this works on api 19+ only. If you want to mirror Drawables or any View on API android:rotationY=»@integer/locale_mirror_flip» to the View you want to be mirrored. And define
res/values/integers.xml
res/values-ldrtl/integers.xml
I use this trick to mirror one of the Progress Indicator that doesn’t support RTL. This works on API 11+.
Animations
Animations that use X axis for transitions will not work properly. For example TranslateAnimation — You will need to add a RTL variant for these as well.
For example: android:fromXDelta=»-100%p» will become android:fromXDelta=»100%p» in RTL variant of this animation,
android:pivotX=»25%» will become android:pivotX=»75%» .
Java Files
I would recommend defining all animations, layout, etc as xml resource only. Because RTL support for those programmatically defined layouts and animations is painful. Also, the compilation will take more time if you modify a Java file instead of XML.
If you still find these defined in your java files then this is what you’ll need to do
- Programmatically defined Animations with X axis values need to have different values for RTL, as the X axis always starts from Left in both layout directions. Basically any X anywhere in android means from left.
- If you have setMargin(left, top, right, bottom) then also add setMarginStart(start) and setMarginEnd(end) .
- setPadding(left, top, right, bottom) will be replaced with setPaddingRelative(start, top, end, bottom) .
- setGravity(Gravity.LEFT/RIGHT) will be replaced with setGravity(Gravity.START/END) .
- Any other layout property like LayoutParams rules for RelativeLayout will also need to be changed.
- All those Horizontal RecyclerView, ListView, ViewPager , etc will need your attention as well.
For Example : A Horizontal RecyclerView can use setStackFromEnd(true) on it’s LayoutManager in case of RTL layout. - If you want Layout Direction of a View on runtime then use
ViewCompat. getLayoutDirection(view)
Note : This always returns LTR if called from the Constructor of a View. - If you want Layout Direction from a Context then use
Configuration config = context.getResources().getConfiguration();
if (config.getLayoutDirection() == View. LAYOUT_DIRECTION_RTL) <
//RTL
>
Extras
If you have completed all of the above then I believe you should have pretty good RTL Support. There are few more things you can look at for your final touch.
Conclusion
Support for RTL is not that difficult on Android. I wish I had supported it from day 1 instead of doing it at a later stage in my app. If you found something missing or have suggestions then do comment.
Источник
Локализация приложения и поддержка RTL. Доклад Яндекс.Такси
При локализации сервиса важно внимательно отнестись к согласованию переводов между собой. Руководитель группы клиентской Android-разработки Яндекс.Такси Александр Бонель рассказал, какие практики и инструменты упрощают локализацию. Во второй части доклада Саша поделился опытом поддержки языка RTL в приложении: что хорошо, а что не совсем работает у Андроида из коробки, какие проблемы возникают из-за поддержки RTL и как их минимизировать в будущем.
— В своем докладе я хочу рассказать, какие основные идеи и практики мы используем в командах разработки мобильных приложений Такси для решения вопросов, связанных с локализацией и актуализацией перевода в наших приложениях. Затем расскажу, как мы внедряли в приложение поддержку работы в режиме отрисовки справа налево.
Начну с локализации. В первую очередь хотелось бы внести ясность в терминологию. По моим наблюдениям, большое количество людей считают, что локализация ограничена всего-навсего переводами, хотя на самом деле она также решает проблемы, связанные с форматированием чисел, дат, времени, работу с текстом и символами, правовые аспекты, подачу контента потребителю таким образом, что он имел для него смысловую ценность и не был воспринят неоднозначно. И многое другое. (Общение с залом о том, у кого сколько языков поддерживается в приложениях — прим. ред.)
Хочу рассказать вам историю, которая произошла у нас в 2014 году. Мы тогда сделали крупный редизайн приложения Такси. В одном из очередных релизов мы предоставили пользователям возможность указывать номер подъезда, а также смотреть на эту информацию на экране информации о поездке и, что самое главное, на экране оценки заказа, которую мы показывали всем пользователям, потому что мы обязательно хотели получить оценку за поездку.
Мы тогда достаточно фривольно относились к процессу локализации и к переводам. По большей части, весь процесс был ручной. Обновление текстов в нужных XML-файлах, добавление новых переводов проводилось вручную. И сыграл свою роль человеческий фактор. Разработчик определил не в дефолтной локале очередной перевод. Это приводило к тому, что у пользователей с отличной от русского языка системной локалью, если они задавали подъезд, приложение по окончанию поездки крэшилось. И люди не могли сделать еще один заказ.
На тот момент у нас в приложении поддерживалось два языка: русский и английский. И мы работали только в России в трех городах: Москве, Екатеринбурге, Санкт-Петербурге.
Сейчас мы работаем в 16 странах и переведены на 18 языков. Я думаю, вы понимаете всю сугубость проблемы, если мы в таком состоянии находились на тот момент. Мы уже тогда поняли, что нужно что-то менять.
В Яндексе бо́льшая часть разработчиков мобильных приложений для решения проблем, связанных с локализацией, пользуется разработанным внутри сервисом. У него очень удобный UI-интерфейс, есть возможность определить свой проект, задать набор ключей, набор языков, на которые необходимо переводить проект. И после того, как переводы появляются, их можно выгрузить в любом удобном формате. У него также очень хорошая интеграция с нашими внутренними сервисами. Есть версионированность. Но лично для меня как для разработчика самое главное то, что у него есть API, потому что это уже наводит на мысль о том, что вся ручная работа, связанная с переводами, может быть автоматизирована. Что мы и сделали. Мы разработали плагин для Gradle. По сути, актуализация перевода в приложении теперь сводится к тому, что разработчик выполняет один-единственный task: updateTranslations.
В первом приближении он ходит в сервис локализации. Проверяет наличие актуальных переводов, закачивает их в проект, раскладывает по нужным файлам. Но помимо этого он также проводит верификацию состояния переводов в сервисе локализации по отношению к тому, что сейчас есть в проекте.
При очередном выполнении git status разработчик видит у себя в рабочей директории, какие файлы поменялись.
Также он может посмотреть в сгенерированном HTML-отчете о том, какой ключ на какое значение в каком языке поменялся.
И также он может посмотреть, какие проблемы на данный момент присутствуют в переводах его проекта.
Плагин конфигурируемый. Мы можем определить количество языков, которые наше приложение поддерживает. Очень актуально, если локализаторы заводят новый язык, и он нечаянно просачивается в проект, будучи не пройденным через корректуру и вычитку.
Также есть специфическая возможность ремаппинга. Я расскажу о ней чуть позже более подробно, когда буду рассказывать про RTL.
Какие проблемы этот плагин сейчас анализирует? Та проблема, с которой мы столкнулись в 2014 году — банальная, отсутствие перевода. Теперь для того, чтобы добавить новую строчку, новый перевод в проект, разработчику недостаточно просто определить его в XML-файле.
Во-первых, если он сделает это не в дефолтной локале, то его вновь заведенный ключ просто пропадет при очередной синхронизации, и он узнает об этом на этапе компиляции.
Если же он определит ключ в дефолтном файле, но забудет это сделать в сервисе локализации, то при очередном выполнении таска updatetranslations он получит ошибку от плагина, которая скажет ему: «Этот ключ есть в вашем проекте, но он отсутствует в сервисе локализации, его там нужно завести».
Вторая типичная проблема — это отсутствие перевода. Мы получаем список ссылок на те ключи, для которых нет перевода, и, как правило, относим их нашему менеджеру для того, чтобы они настроили впоследствии коммуникацию с переводчиками. Третий момент часто встречается, когда решили пересмотреть использование формат-аргументов в каком-то ключе, и, например, вместо целочисленного формата стали использовать строковый формат.
Но при этом в каком-то из переводов забыли поменять чиселку на строку.
И четвертая проблема также обусловлена человеческим фактором, в основном при переводе. Если в переводе используются Unicode-символы, очень легко запутаться в позиционировании символов и вместо неразрывного пробела получить перенос на другую строку — то, от чего мы пытаемся избавиться.
Это всё про локализацию.
Теперь я хочу рассказать про опыт внедрения поддержки отрисовки справа налево в приложении на примере Израиля. Мы запустились в 2018 году под брендом Yango в Израиле. Как оказалось, в Израиле люди читают не так, как мы с вами. Они читают справа налево.
С чего хочется начать? Что в Android в принципе поддержка отрисовки справа налево реализована из коробки достаточно хорошо.
Начинается она с того, что в манифесте приложения, в элементе Application вам необходимо объявить атрибут supportRtl=”true”. Но хочу вас обрадовать: если вы у себя в приложениях интегрируете Facebook SDK для социальной авторизации, заботливые разработчики Facebook это уже сделали за вас. И если вы не валидируете манифест при мерджинге, у вас этот атрибут со значением true встанет в ваше приложение, и оно уже будет уметь в Rtl. Это то, о чем можно задуматься.
Думаю, что большинство из сидящих в аудитории наверняка минимально поддерживают версию Android 4.4. Кому-то больше повезло — это 5.0 и выше. Кому-то могло меньше повезти, и они поддерживают 4.0, или, упаси, господи, 2.3. В этом случае у вас будут большие проблемы, потому что полноценная поддержка Rtl в Android появилась начиная с версии 4.2. Поэтому с minSdk 17-м вы на порядок упростите себе жизнь.
Перевод проекта на работу с Rtl начинается с refactoring tool в Android Studio — Add RTL Support Where Possible. Надо отдать должное, работает достаточно хорошо. Он проходит по XML-файлам ваших разметок, смотрит на наличие атрибутов со значениями left и right у gravity, у paddings, margins, и заменяет их на start и end. Если вы хотите посмотреть, как ваше приложение по умолчанию работает при отрисовке в режиме справа налево, вам необязательно иметь на руках контент с так называемыми RTL strong символами — на иврите или на арабском языке. Вам достаточно в настройках разработчика включить опцию Force RTL Layout Direction.
Какие возможности для кастомизации UI и рендеринга текста Android предоставляет для RTL?
Первый момент — это атрибут layoutDirection, который использует описанная опция Force RTL Layout Direction. У нее всего четыре возможных значения. То есть она по умолчанию наследуется от родителя. Можно сказать о том, чтобы layout отрисовывался, исходя из выбранной локале. Можно сказать четко, чтобы он отрисовывался справа налево или слева направо.
Второй элемент, которые многие наверняка по привычке обходят стороной, когда занимаются выравниванием текста — это textAlignment. Многие до сих пор продолжают использовать gravity. Для выравнивания текста не используйте gravity, а используйте textAlignment.
Третий атрибут — это направление рендеринга текста, textDirection. У него достаточно гибкая настройка. Она позволяет определить, как текст рендерится в зависимости от того, какой первый strong символ попался в тексте, будь то из латинского набора символов strong LTR или из иврита strong RTL. Но если вы поддерживаете полноценно RTL в приложении, его не нужно обходить стороной, потому что при игнорировании происходит такой забавный артефакт.
То ли это пасхалочка, то ли это такой side effect в реализации editText: у вас начинает hint у editText разъезжаться. Поэтому какое-никакое значение вы ему в любом случае должны задать.
Ссылка со слайда
Если вам нужно соблюсти определенную отрисовку каких-то графических ресурсов или разметки в режиме RTL, то Android предоставляет возможность задать qualifier ldrtl в вашей папочке, в которую вы можете положить любой ресурс, и он в режиме отрисовки RTL будет отрисован так, как вы задали.
Иногда бывает потребность в runtime приходящий с бэкэнда текст отрисовывать как есть, как он пришел, игнорируя то, в каком режиме сейчас работает ваше приложение. По сути, в text renderer это осуществляется за счет того, что текст обрамляется так называемыми Bidi Unicode control символами. Логику для работы с этими символами в себе содержит класс BidiFormatter, предоставляя только один-единственный метод unicodeWrap. Он принимает СharSequence на вход, и возвращает вам строку, обрамленную этими символами. Там их исчерпывающее количество, и, в принципе, он должен решать бо́льшую часть ваших проблем.
Но если есть что-то такое специфическое, у w3.org есть хорошая статья на тему того, как этими символами пользоваться.
Какие проблемы мы приобрели, когда начинали поддержку RTL в нашем приложении? В первую очередь это конфликт стандартов локализации.
BCP47 — это более новый стандарт локализации, который в частности поменял коды некоторых локалей. И дело в том, что есть проблема с классом locale Java. Он по умолчанию работает в режиме обратной совместимости, и приходящие ему locale коды из BCP конвертирует в ISO. То есть мы это увидели, когда на наш бэкэнд в заголовке Accept-Language вместо кода he стал уходить код iw.
Ссылка со слайда
Если вы помните слайд с конфигурацией нашего плагина, remapping был нужен как раз таки для этого — для того, чтобы переводить контент из he в iw.
Если вам необходимо использовать BCP формат, это в полной мере реализовано в проекте Apache Cordova. У них есть класс Globalization и реализация localeToBcp47Language.
Еще один момент, который «доставляет» при поддержке RTL в приложении — это анимации. Здесь, благо, по сути, аффектятся только анимации по оси X, и, пожалуй, единственное решение или подход, который вам поможет избежать большего количества проблем — это возможность пересмотреть анимации абсолютные в стороны анимаций относительных.
Одна проблема, которая решается, пожалуй, только reflection, потому что она жестко реализована так в самом компоненте — это выравнивание hint у TextInputLayout. Дело в том, что если hint задан в RTL strong символах на иврите, он будет четко выровнен по правому краю, даже несмотря на то, что контент, который вы вводите в edit text, может быть из латинских символов. И наоборот. Если hint у вас на латинице, он будет четко выровнен по левому краю. Это решить можно только с помощью reflection.
В конечном итоге, после того, как вы внедрили RTL у вас в приложении, имеет смысл пересмотреть, severities для RTL-issues, которые анализирует Lint. Их всего четыре. Если вы не поддерживаете RTL в вашем приложении, но явно где-то используете RTL specific атрибуты, то это RtlEnabled (Lint вам об этом скажет).
Ссылка со слайда
Если вы поддерживаете RTL в приложении, но minSdkVersion вашего приложения ниже 17, то вы обязаны также продолжать использовать атрибуты типа left и right, вы не можете использовать только атрибуты start и end в вашей разметке. Это то, что анализирует RtlCompat. RtlSymmetry ругается, когда видит асимметричные paddings. Вообще, мое мнение такое, что aapt при процессинге ресурсов должен крэшиться, если он натыкается на симметричные paddings. Если вам нужны какие-то асимметричные доступы, пользуйтесь margins как полноценным layout параметром.
Наконец, четвертая проблема — захардкоженый RTL. Это как раз то, что в атрибутах указаны только значения left и right. По сути, это сводит на нет refactoring tool Add RTL Support Where Possible. Это всё грепается из Lint по вхождению RTL. Также реализация анализа доступна в AOSP в классе RtlDetector. То есть если вам фантазия позволяет, вы можете придумать что-то свое, подсмотрев то, как анализ проходит сейчас.
Мой доклад подходит к концу. Я хочу привести несколько тезисов на тему выстраивания правильной локализации у нас в приложении, к которым мы со временем пришли.
В первую очередь, на старте проекта задумайтесь об интернационализации. Я о ней толком не обмолвился. Это совсем не то же самое, что локализация. По сути, интернационализация делает возможной локализацию в приложении, в продукте. Это у Java из коробки работает за счет того, что есть полноценная поддержка Unicode, а у Android — за счет богатой системы ресурсов. Пожалуй, единственное, что здесь может случиться — наша фиксация как разработчиков на тему того, что «Я приложение пишу только для русского, значит, я буду проверять его только с русским языком». А когда вы начинаете использовать в приложении армянский, грузинский, иврит или арабский язык, картина выглядит совсем иначе и ломается об вашу привычную парадигму.
Второй момент. Подумайте над встраиванием локализации в ваш CI. Очень обидно узнать о проблемах, связанных с переводами, на этапе тестирования релиз-кандидата, к которому вы могли готовиться несколько недель. То есть у нас сейчас на каждый пул-реквест прогоняется этот таск, чтобы мы заранее были уверены в том, что с переводами все хорошо и мы можем замерджить новый код в основную ветку проекта.
Третий момент. Уважайте труд переводчиков и свой бюджет. Во-первых, удаляйте неиспользуемые ресурсы, потому что люди могут продолжать тратить время и деньги на то, чтобы переводить строки, которые, в конечном итоге, вы у себя в проекте не используете. Во-вторых, следите за переводами, которые есть в вашем хранилище в сервисе локализации, но которых нет в вашем коде.
В идеале, если API вашего сервиса локализации предоставляет информацию о том, когда тот или иной ключ был заведен, и вы можете использовать эвристики вроде «Если ключ заведен больше месяца назад», проверьте его присутствие в последней ревизии мастер-бранчи. Если его там нет, скорее всего, это явный кандидат на то, чтобы его удалить и чтобы локализаторы не тратили на него время.
Более-менее универсальный кандидат на серебряную пулю — хранить строки на бэкэнде, а не на клиенте. По крайней мере, это даст вам возможность их всегда держать актуальными на стороне приложения.
У нас очень разнообразная аудитория пользователей, каждый воспринимает окружающий мир и контент приложения по-своему. Наша задача как разработчиков — помочь соблюсти это восприятие. Мой доклад на этом окончен. Спасибо большое!
Источник