Підтримка RTL-мов (арабська, іврит) у мобільному додатку
Уявіть: ви виходите на ринок ОАЕ, запускаєте арабську версію додатку — і отримуєте шквал негативних відгуків. Меню з'їхало, кнопки не натискаються, текст обрізається. Запустивши додаток арабською мовою, ви можете зіткнутися з тим, що кнопка «Назад» вказує вправо, а слайди приїжджають зліва. Користувачі звикли до зворотного порядку — і це викликає когнітивний дисонанс. Без коректної RTL-підтримки навіть функціонально однаковий додаток сприймається як зламаний, що призводить до зниження утримання на 30% (дані наших проєктів). Ми адаптували понад 20 проєктів під арабську та іврит і знаємо всі підводні камені. Наша команда готова взяти на себе повний аудит та реалізацію RTL-підтримки під ключ.
Проста зміна локалі та встановлення layoutDirection = rtl — це лише 20% роботи. Решта — ручна адаптація кожного компонента: іконок, анімацій, навігації, тексту і навіть жестів (наприклад, свайпи). Ось що насправді ламається:
| Компонент | iOS | Android | Flutter | React Native |
|---|---|---|---|---|
| Іконки | SF Symbols з RTL-варіантом працюють, але кастомні PNG — ні | autoMirrored="true" для VectorDrawable, BitmapDrawable не дзеркалиться |
Directionality не впливає на CustomPainter |
I18nManager.isRTL для умовного рендерингу |
| Анімації | UIView.animate з абсолютними координатами |
Animator з translationX вимагає перевірки layoutDirection |
SlideTransition з initialOffsetX — ручна логіка |
Animated.timing з useNativeDriver не дзеркалиться |
| Текст | NSTextAlignment.natural дзеркалиться, .left — ні |
textAlignment="viewStart" для TextView |
textDirection: TextDirection.rtl для Text |
textAlign: 'auto' замість 'left' |
| Навігація | UINavigationController дзеркалить кнопку «назад» автоматично |
ActionBar вимагає android:supportsRtl="true" |
Navigator автоматично, але кастомні переходи — ні |
ReactNavigation вимагає ручного налаштування |
Наш підхід з кастомними фліппінг-трансформами вдвічі швидший за традиційне переписування всіх екранів під RTL з нуля та скорочує час доопрацювання на 40%. Це дозволяє заощадити 30–50% бюджету порівняно з повною переробкою. Ми використовуємо автоматизовані тести для перевірки дзеркальності, що дозволяє виявляти помилки на ранніх етапах та знижувати витрати на QA.
Чому стандартне RTL-рішення не покриває всі кейси?
Детальна таблиця вище показує типові проблеми. Додамо, що на iOS кастомні draw() та Core Graphics вимагають conditional flipping, щоб дзеркалити вміст. На Android autoMirrored працює лише для VectorDrawable, але не для BitmapDrawable. У Flutter Directionality не впливає на CustomPainter, тому доводиться вручну застосовувати canvas.scale(-1, 1). У React Native важливо перевіряти версію: на Android I18nManager.isRTL працює лише в RN >=0.63, інакше потрібен forceRTL з перезавантаженням.
Як ми реалізуємо RTL-підтримку?
-
Аудит. Запускаємо додаток на арабській локалі через
adb shell setprop persist.sys.locale ar-AE(Android) таSettings → General → Language & Region(iOS Simulator). Це виявляє 80% проблем. Фіксуємо артефакти на кожному екрані — в середньому 10–15 на додаток з 50 екранів. -
Класифікація. Ділимо проблеми на 4 типи: layout, іконки, текст, анімації. Для кожного типу — свій рецепт виправлення. Наприклад, для іконок застосовуємо conditional flipping, для анімацій — заміну абсолютних координат на відносні.
-
Правки. На iOS переписуємо кастомні
draw()під conditional flipping, використовуємоUIView.appearance(whenContainedInInstancesOf:)для системного успадкуванняsemanticContentAttribute. На Android замінюємоleft/rightнаstart/endу XML та програмно черезMarginLayoutParamsCompat. Для Flutter дзеркалимоCustomPainterчерезcanvas.scale(-1, 1). Для React Native — перевіряємоI18nManager.isRTLу кожному компоненті. -
Тестування. Обов'язково на реальних пристроях: Samsung з One UI змінює поведінку
VectorDrawable, а на старих версіях iOSUIView.appearanceможе не спрацювати. Додаємо скріншотні тести з RTL-локалями в CI. Запускаємо на 3–4 фізичних девайсах, щоб покрити 95% кейсів.
Докладніше про тестування
Використовуємо XCTest з XCUIRemote для симуляції жестів і перевірки дзеркальності. На Android — Espresso з DeviceLocale для перемикання локалі. Інтегруємо Firebase Test Lab для тестування на реальних пристроях у хмарі.
Які послуги входять в RTL-адаптацію?
- Повний аудит коду та артефактів на всіх екранах
- Виправлення layout, іконок, тексту, анімацій
- Адаптація кастомних компонентів (Canvas, Core Graphics, CustomPainter)
- Налаштування бекенд-інтеграції для передачі RTL-контексту
- Ручне тестування на 4+ реальних пристроях
- Інтеграція RTL-скріншот-тестів у CI/CD
- Документація з підтримки RTL для команди
Скільки часу займає RTL-доопрацювання на кожній платформі?
| Платформа | Аудит (дні) | Правки (дні) | Тестування (дні) | Разом |
|---|---|---|---|---|
| iOS | 1–2 | 2–3 | 1 | 4–6 |
| Android | 1–2 | 2–4 | 1 | 4–7 |
| Flutter | 1 | 1–2 | 0.5 | 2–3.5 |
| React Native | 1 | 2–3 | 0.5 | 3–4.5 |
Які типові помилки допускають при RTL-розробці?
- Жорстко заданий
textAlign: 'left'у кастомних компонентах замість'auto'або'start' -
transform: [{scaleX: -1}]застосований до іконки без перевіркиI18nManager.isRTL— іконка дзеркалиться завжди - Локалізація чисел:
١٢٣(eastern arabic numerals) vs123— залежить від контексту відображення, потрібно явно задаватиNSLocale/Locale - Бідирекційний текст (арабська + англійська назва):
NSAttributedStringз явнимNSWritingDirectionAttributeName, інакше порядок слів ламається
Як закладати RTL на етапі проєктування?
Найкращий спосіб — використовувати start/end замість left/right з самого початку, уникати абсолютних координат в анімаціях і перевіряти кастомні Drawable на autoMirrored. Якщо проєкт уже готовий — ми проведемо аудит і усунемо всі невідповідності. Наші інженери гарантують коректну роботу на арабській та івриті на всіх підтримуваних пристроях з чистим супроводжуваним кодом.
Не дайте вашим користувачам загубитися в дзеркальному лабіринті — зв'яжіться з нами для консультації з RTL-аудиту. Замовте консультацію — ми розрахуємо терміни та вартість. Інвестиції окупляться протягом 2-3 місяців після виходу на ринок.
Apple рекомендує використовувати UIView.appearance() для успадкування семантичних атрибутів; докладніше див. RTL (Right-to-Left) на Wikipedia.







