Поддержка RTL (right-to-left) в Kepler.gl затрагивает сразу несколько уровней: рендеринг карты через Mapbox GL JS, работу текстовых слоёв deck.gl, а также корректную обработку UI-слоя библиотеки и пользовательских данных, содержащих языки с направлением письма справа налево (арабский, иврит, персидский, урду).
Ключевая сложность заключается в том, что Kepler.gl не является самостоятельной картографической системой — он построен поверх Mapbox GL JS и deck.gl, поэтому RTL-поведение определяется сочетанием их возможностей и дополнительной конфигурацией.
Kepler.gl состоит из трёх основных слоёв:
RTL-логика распределяется следующим образом:
RTL влияет на все три уровня, но критически важным является именно Mapbox GL JS, так как он контролирует текст на карте.
Mapbox GL JS не включает поддержку арабского и других RTL-языков по умолчанию. Для корректного отображения требуется отдельный плагин:
npm install mapbox-gl-rtl-text
Далее он должен быть подключён до инициализации карты:
import mapboxgl from 'mapbox-gl';
import rtlTextPlugin from 'mapbox-gl-rtl-text';
mapboxgl.setRTLTextPlugin(
'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.3/mapbox-gl-rtl-text.js',
null,
true
);
RTL-плагин должен быть установлен до создания экземпляра Mapbox GL карты внутри Kepler.gl. В противном случае текстовые слои могут отрисоваться некорректно и не обновиться автоматически.
Kepler.gl позволяет передавать кастомную конфигурацию Mapbox через
mapboxApiAccessToken и дополнительные параметры
инициализации карты.
Базовая интеграция выглядит следующим образом:
import KeplerGl from 'kepler.gl';
import { Provider } from 'react-redux';
import store from './store';
function App() {
return (
<Provider store={store}>
<KeplerGl
id="map"
mapboxApiAccessToken={process.env.MAPBOX_TOKEN}
width={window.innerWidth}
height={window.innerHeight}
/>
</Provider>
);
}
RTL-логика при этом внедряется на уровне глобальной инициализации Mapbox, а не через props Kepler.gl.
Mapbox GL JS автоматически применяет RTL-обработку к следующим элементам:
Однако в Kepler.gl часто используются кастомные слои deck.gl, где
текст рендерится через TextLayer. В этом случае RTL не
применяется автоматически.
import { TextLayer } from '@deck.gl/layers';
const layer = new TextLayer({
id: 'text-layer',
data,
getPosition: d => d.coordinates,
getText: d => d.label,
getSize: 16
});
Без дополнительной обработки строки на арабском или иврите могут отображаться в неверном порядке символов.
deck.gl не имеет встроенной полноценной поддержки bidi-алгоритма (bidirectional text algorithm). Поэтому требуется предварительная нормализация текста.
Типичное решение — применение bidi-js или
rtl-detect + преобразование строки:
import bidiFactory from 'bidi-js';
const bidi = bidiFactory();
function formatRTLText(text) {
return bidi.from_string(text).toString();
}
Использование в слое:
const layer = new TextLayer({
id: 'text-layer',
data,
getText: d => formatRTLText(d.label),
getPosition: d => d.coordinates,
getSize: 14
});
Перед обработкой важно определить, является ли текст RTL. Для этого применяется детектор языка:
import rtlDetect from 'rtl-detect';
function normalizeText(text) {
if (rtlDetect.isRtlLang(text)) {
return formatRTLText(text);
}
return text;
}
В Kepler.gl это особенно важно при смешанных датасетах, где одновременно присутствуют латиница и RTL-скрипты.
UI Kepler.gl по умолчанию не поддерживает полноценный RTL-layout. Это означает:
Для частичной адаптации используется CSS-override:
.kepler-gl {
direction: rtl;
text-align: right;
}
Однако это приводит к побочным эффектам:
Поэтому чаще применяется гибридный подход: RTL только для карты, UI остаётся LTR.
Mapbox стили поддерживают специальные настройки для RTL через
layout.text-field.
Пример слоя символов:
{
id: 'country-label',
type: 'symbol',
source: 'mapbox',
'source-layer': 'country_label',
layout: {
'text-field': ['get', 'name_ar'],
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Regular']
}
}
Kepler.gl может наследовать такие стили через кастомный style object.
RTL-отображение напрямую зависит от наличия корректных шрифтов.
Рекомендуемые требования:
Mapbox style должен включать:
"text-font": ["Noto Sans Arabic Regular", "Arial Unicode MS Regular"]
Без этого текст может отображаться разрозненно (изолированные буквы вместо связанных форм).
На практике наиболее сложная ситуация — смешанные строки:
Пример:
محطة 5 - Zone A
Без bidi-обработки результат может быть визуально некорректным.
Решение:
function smartFormat(text) {
const hasRTL = rtlDetect.isRtlLang(text);
if (!hasRTL) return text;
return `\u202B${text}\u202C`;
}
Unicode маркеры:
\u202B RLE (right-to-left embedding)\u202C pop directional formattingФильтры данных (data filters) и всплывающие таблицы требуют отдельной обработки:
Intl.Collatorconst collator = new Intl.Collator('ar', { sensitivity: 'base' });
data.sort((a, b) => collator.compare(a.name, b.name));
Без этого арабские строки сортируются лексикографически по Unicode-коду, что даёт некорректный порядок.
Bidi-алгоритмы и преобразования строк могут стать узким местом при больших наборах данных (100k+ объектов).
Оптимизации:
const cache = new Map();
function cachedFormat(text) {
if (cache.has(text)) return cache.get(text);
const result = normalizeText(text);
cache.set(text, result);
return result;
}
deck.gl использует WebGL, поэтому RTL-логика не может опираться на CSS direction для графических слоёв.
Это означает:
Любая RTL-логика должна быть завершена до стадии рендеринга.
На практике часто встречаются следующие проблемы:
Каждая из этих ошибок приводит к визуальным артефактам: перевёрнутому тексту, разорванным словам или неправильному порядку слов.
Наиболее устойчивый подход в Kepler.gl:
Такая схема минимизирует вмешательство в внутреннюю архитектуру Kepler.gl и снижает риск регрессий при обновлениях библиотек.