Современные картографические приложения часто обслуживают пользователей из разных стран и языковых регионов. Названия населённых пунктов, улиц, административных единиц и объектов инфраструктуры могут существовать одновременно на нескольких языках. В таких условиях карта должна корректно отображать локализованные подписи, обеспечивать удобный поиск и поддерживать динамическое переключение языка интерфейса.
В экосистеме MapLibre GL JS многоязычность реализуется преимущественно через механизм стилей и свойства объектов в векторных тайлах. Карта получает данные, содержащие несколько языковых вариантов названий, после чего стиль определяет, какое именно поле использовать для отображения подписей.
Векторные тайлы обычно содержат набор языковых атрибутов для одного объекта.
Пример структуры данных:
{
"name": "Moscow",
"name:en": "Moscow",
"name:ru": "Москва",
"name:de": "Moskau",
"name:fr": "Moscou"
}
Либо используются сокращённые обозначения:
{
"name_en": "Moscow",
"name_ru": "Москва",
"name_de": "Moskau"
}
Формат зависит от поставщика данных.
Наиболее распространённые варианты:
| Поле | Описание |
|---|---|
| name | Основное название |
| name:en | Английское название |
| name:ru | Русское название |
| name:de | Немецкое название |
| name:fr | Французское название |
| name:es | Испанское название |
MapLibre GL JS не навязывает конкретную структуру хранения языковых данных. Главное условие — наличие соответствующих атрибутов в источнике данных.
Текстовые подписи формируются через свойство
text-field.
Пример:
map.addLayer({
id: 'cities',
type: 'symbol',
source: 'cities',
'source-layer': 'place',
layout: {
'text-field': ['get', 'name:ru'],
'text-size': 14
}
});
В этом случае карта будет отображать русские названия объектов.
Для английского языка:
layout: {
'text-field': ['get', 'name:en']
}
Для немецкого:
layout: {
'text-field': ['get', 'name:de']
}
Выражение get извлекает значение указанного атрибута из
объекта тайла.
Наиболее распространённый сценарий — изменение языка без полной перезагрузки карты.
Пример функции:
function setLanguage(language) {
map.setLayoutProperty(
'cities',
'text-field',
['get', `name:${language}`]
);
}
Использование:
setLanguage('en');
или
setLanguage('ru');
После вызова MapLibre автоматически пересчитает подписи и обновит отображение слоя.
Обычно карта содержит множество слоёв:
Для изменения языка всех подписей создаётся единая функция:
function updateLanguage(lang) {
const layers = [
'country-label',
'city-label',
'road-label',
'water-label',
'poi-label'
];
layers.forEach(layerId => {
map.setLayoutProperty(
layerId,
'text-field',
['get', `name:${lang}`]
);
});
}
Вызов:
updateLanguage('fr');
Все подписи на карте будут переведены на французский язык при наличии соответствующих данных.
Не все объекты содержат переводы на каждый язык. Для предотвращения
пустых подписей применяется выражение coalesce.
Пример:
'text-field': [
'coalesce',
['get', 'name:ru'],
['get', 'name:en'],
['get', 'name']
]
Логика работы:
name:ru.name:en.name.Это один из важнейших механизмов локализации карт.
Язык браузера можно определить через объект
navigator.
const browserLanguage =
navigator.language.slice(0, 2);
Примеры результатов:
ru
en
de
fr
es
После определения языка можно переключить подписи:
updateLanguage(browserLanguage);
Благодаря этому карта сразу отображается в привычной локализации пользователя.
Некоторые объекты имеют:
Пример:
{
"name": "北京",
"name:en": "Beijing",
"name:ru": "Пекин"
}
В зависимости от требований проекта можно отображать:
Только локальное название:
['get', 'name']
Только перевод:
['get', 'name:en']
Комбинированный вариант:
[
'concat',
['get', 'name'],
' (',
['get', 'name:en'],
')'
]
Результат:
北京 (Beijing)
В международных приложениях нередко используются подписи сразу на двух языках.
Пример:
'text-field': [
'concat',
['get', 'name:ru'],
'\n',
['get', 'name:en']
]
Результат:
Москва
Moscow
Многострочные подписи особенно полезны:
MapLibre GL JS поддерживает языки с различными направлениями письма.
Основные категории:
| Язык | Направление |
|---|---|
| Русский | Слева направо |
| Английский | Слева направо |
| Немецкий | Слева направо |
| Арабский | Справа налево |
| Иврит | Справа налево |
Для корректной работы арабского и иврита используется плагин обработки RTL-текста.
Подключение:
maplibregl.setRTLTextPlugin(
'rtl-text-plugin.js'
);
После подключения подписи будут автоматически отображаться в правильном порядке.
MapLibre отвечает только за отображение карты. Интерфейс приложения локализуется отдельно.
Пример словаря:
const translations = {
en: {
zoomIn: 'Zoom In',
zoomOut: 'Zoom Out'
},
ru: {
zoomIn: 'Приблизить',
zoomOut: 'Отдалить'
}
};
Получение перевода:
function t(key) {
return translations[currentLanguage][key];
}
Использование:
button.textContent = t('zoomIn');
Таким образом синхронизируются язык карты и язык пользовательского интерфейса.
В некоторых стилях языковые поля уже встроены в конфигурацию.
Пример фрагмента стиля:
{
"id": "place-label",
"type": "symbol",
"layout": {
"text-field": "{name}"
}
}
После загрузки стиля возможно программное изменение свойства:
map.setLayoutProperty(
'place-label',
'text-field',
['get', 'name:ru']
);
Такой подход позволяет переиспользовать один и тот же стиль для разных локализаций.
При большом количестве слоёв удобно обходить весь стиль автоматически.
Пример:
const style = map.getStyle();
style.layers.forEach(layer => {
if (layer.type === 'symbol') {
map.setLayoutProperty(
layer.id,
'text-field',
['get', 'name:ru']
);
}
});
На практике обычно добавляются дополнительные проверки, чтобы изменять только слои подписей.
Выражение match позволяет реализовать локализацию внутри
одного стиля.
Пример:
[
'match',
['get', 'lang'],
'ru',
['get', 'name:ru'],
'de',
['get', 'name:de'],
['get', 'name:en']
]
Логика:
lang = ru отображается русский текст;lang = de — немецкий;Чтобы сохранять предпочтения пользователя между сессиями, используется Local Storage.
Сохранение:
localStorage.setItem(
'map-language',
'ru'
);
Чтение:
const language =
localStorage.getItem('map-language');
Применение:
if (language) {
updateLanguage(language);
}
После повторного открытия приложения карта автоматически вернётся к выбранной локализации.
Многие векторные наборы данных, построенные на основе данных OpenStreetMap, содержат большое количество языковых тегов:
name
name:ru
name:en
name:de
name:fr
name:ja
name:zh
name:ar
Это обеспечивает высокий уровень локализации без дополнительной подготовки данных.
Например:
{
"name": "Warszawa",
"name:ru": "Варшава",
"name:en": "Warsaw",
"name:de": "Warschau"
}
MapLibre GL JS способен напрямую использовать такие атрибуты через выражения стиля.
При работе с большим количеством слоёв частое изменение свойства
text-field может вызывать дополнительные перерасчёты
стиля.
Практические рекомендации:
Пример проверки:
let currentLanguage = 'en';
function setLanguage(lang) {
if (currentLanguage === lang) {
return;
}
currentLanguage = lang;
updateLanguage(lang);
}
Такой подход уменьшает количество операций рендеринга и улучшает отзывчивость интерфейса.
Полноценная система локализации обычно включает несколько уровней:
text-field.coalesce.Совместное использование этих механизмов позволяет создавать картографические приложения мирового уровня, способные корректно отображать географические данные для пользователей различных языковых и культурных сред.