RTL языки

Особенности работы автодополнения в RTL-контексте

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

RTL-языки включают арабский, иврит, персидский и ряд других систем письма, где направление текста влияет на:

  • выравнивание input-поля;
  • направление курсора и выделения;
  • порядок визуального отображения подсказок;
  • позиционирование dropdown-списка;
  • поведение автозамены и фильтрации.

Базовая настройка RTL в HTML и влияние на Awesomplete

Основной механизм переключения направления текста задается через атрибут:

<input class="awesomplete" dir="rtl" />

или на уровне контейнера:

<div dir="rtl">
    <input class="awesomplete" />
</div>

Awesomplete не требует специальной конфигурации для работы с dir="rtl", однако визуальное поведение компонентов меняется.

Ключевой момент: библиотека не управляет направлением текста, она опирается на браузерный rendering engine. Это означает, что корректность RTL во многом зависит от CSS и поведения input-элемента.


Проблемы позиционирования dropdown в RTL

В стандартной реализации Awesomplete dropdown позиционируется относительно input слева направо. В RTL-сценариях это может приводить к следующим эффектам:

  • список визуально “съезжает” вправо за пределы контейнера;
  • стрелка или подсветка не совпадает с ожидаемой стороной;
  • ширина dropdown не учитывает зеркальную модель layout.

Решение заключается в переопределении CSS:

.awesomplete [role="listbox"] {
    left: auto;
    right: 0;
    text-align: right;
}

Дополнительно может потребоваться:

.awesomplete {
    direction: rtl;
}

.awesomplete ul {
    direction: rtl;
}

Важно учитывать, что Awesomplete использует абсолютное позиционирование, поэтому left/right должны быть строго согласованы.


Выравнивание текста и подсказок

В RTL-интерфейсах критично обеспечить единое направление для:

  • вводимого текста;
  • элементов списка подсказок;
  • выделенного совпадения.

Базовая настройка:

.awesomplete input {
    text-align: right;
    direction: rtl;
}

.awesomplete li {
    text-align: right;
}

При этом внутреннее форматирование подсветки (например, <mark> или кастомные шаблоны) также должно наследовать направление:

.awesomplete mark {
    background: transparent;
    font-weight: bold;
}

Фильтрация и работа с Unicode в RTL

Awesomplete использует строковые операции для фильтрации списка. В RTL-языках это приводит к дополнительным нюансам:

  • символы могут иметь сложные формы (лигатуры в арабском);
  • порядок символов в памяти не совпадает с визуальным отображением;
  • поиск по подстроке должен быть Unicode-safe.

Стандартный фильтр:

Awesomplete.FILTER_CONTAINS = function (text, input) {
    return text.toLowerCase().includes(input.trim().toLowerCase());
};

Для RTL-языков более устойчивый вариант:

function normalize(str) {
    return str
        .normalize("NFKC")
        .replace(/\u200f|\u200e/g, ""); // удаление directional marks
}

Awesomplete.FILTER_CONTAINS = function (text, input) {
    return normalize(text)
        .toLowerCase()
        .includes(normalize(input).toLowerCase());
};

Кастомизация отображения элементов списка

В RTL-интерфейсах важно контролировать порядок частей строки, особенно если используются комбинации “имя + описание” или “значение + код”.

Awesomplete позволяет переопределить форматирование через item:

new Awesomplete(input, {
    list: [
        "موسكو - Москва",
        "الرياض - Riyadh",
        "القاهرة - Cairo"
    ],
    item: function (text, input) {
        const li = document.createElement("li");
        li.textContent = text;
        li.style.direction = "rtl";
        li.style.textAlign = "right";
        return li;
    }
});

В RTL-среде часто требуется инвертировать порядок отображения:

function formatCity(item) {
    const [native, latin] = item.split(" - ");
    return `${latin} — ${native}`;
}

Работа курсора и автозамены

Awesomplete использует стандартное поведение input-элемента для вставки значения. В RTL-полях возможны эффекты:

  • вставка происходит в “неожиданной” позиции;
  • курсор смещается визуально влево вместо вправо;
  • смешение LTR- и RTL-символов ломает позиционирование.

Для стабилизации поведения применяется принудительная нормализация направления:

input.addEventListener("awesomplete-selectcomplete", function () {
    this.setAttribute("dir", "rtl");
    this.style.direction = "rtl";
    this.style.textAlign = "right";
});

Подсветка совпадений в RTL

Awesomplete подсвечивает совпадения через <mark>-элементы. В RTL-режиме важно учитывать, что визуальная подсветка может не совпадать с логическим порядком строки.

Расширенный вариант фильтрации и подсветки:

Awesomplete.ITEM = function (text, input) {
    const li = document.createElement("li");
    li.dir = "rtl";

    const index = text.toLowerCase().indexOf(input.toLowerCase());

    if (index >= 0) {
        li.innerHTML =
            text.substring(0, index) +
            "<mark>" +
            text.substring(index, index + input.length) +
            "</mark>" +
            text.substring(index + input.length);
    } else {
        li.textContent = text;
    }

    return li;
};

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


Интеграция с смешанными (RTL + LTR) данными

Частая ситуация — сочетание арабского текста и латинских идентификаторов:

  • “Dubai DXB”
  • “القاهرة CAI”
  • “Tehran THR”

В таких случаях возникают проблемы bidi-алгоритма браузера. Решение — использование изолирующих символов:

function bidiSafe(text) {
    return "\u2068" + text + "\u2069";
}

И применение в списке:

list: data.map(item => bidiSafe(item))

Позиционирование dropdown в гибридных интерфейсах

Если страница поддерживает переключение LTR/RTL динамически, Awesomplete требует пересчета позиции:

function updateDirection(input, isRTL) {
    input.setAttribute("dir", isRTL ? "rtl" : "ltr");

    const aw = input.awesomplete;
    if (aw) {
        aw.open();
        aw.close();
    }
}

Дополнительно полезно принудительно сбрасывать inline-стили:

const list = document.querySelector(".awesomplete [role='listbox']");
list.style.left = "";
list.style.right = isRTL ? "0" : "auto";

CSS-стратегия для стабильной RTL-работы

Сводная конфигурация, обеспечивающая предсказуемое поведение:

.awesomplete {
    direction: rtl;
    width: 100%;
}

.awesomplete input {
    direction: rtl;
    text-align: right;
}

.awesomplete [role="listbox"] {
    direction: rtl;
    right: 0;
    left: auto;
    text-align: right;
}

.awesomplete li {
    direction: rtl;
    text-align: right;
}

Типичные ошибки при реализации RTL

  • отсутствие dir="rtl" на input, при наличии RTL-данных;
  • попытка управлять направлением только через JavaScript без CSS;
  • игнорирование bidi-символов в смешанных строках;
  • неправильное позиционирование dropdown через left вместо right;
  • использование неподготовленных строк для фильтрации.

Поведение в мобильных браузерах

Мобильные WebView и браузеры по-разному интерпретируют RTL:

  • Safari может менять выравнивание текста независимо от CSS;
  • Android WebView иногда игнорирует text-align в dropdown;
  • курсор может “прыгать” при автозаполнении.

Для стабилизации поведения часто требуется дополнительная фиксация:

input {
    unicode-bidi: plaintext;
}

или

.awesomplete input {
    unicode-bidi: isolate;
}

Совместимость RTL и кастомных источников данных

При использовании динамических источников (API, JSON) важно нормализовать данные до передачи в Awesomplete:

fetch("/cities")
    .then(r => r.json())
    .then(data => {
        new Awesomplete(input, {
            list: data.map(item => ({
                label: bidiSafe(item.name),
                value: item.code
            }))
        });
    });

Это предотвращает нарушение порядка символов и визуальные артефакты в dropdown.