Multilingual поддержка

Современные картографические приложения часто обслуживают пользователей из разных стран и языковых регионов. Названия населённых пунктов, улиц, административных единиц и объектов инфраструктуры могут существовать одновременно на нескольких языках. В таких условиях карта должна корректно отображать локализованные подписи, обеспечивать удобный поиск и поддерживать динамическое переключение языка интерфейса.

В экосистеме 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

Не все объекты содержат переводы на каждый язык. Для предотвращения пустых подписей применяется выражение coalesce.

Пример:

'text-field': [
    'coalesce',
    ['get', 'name:ru'],
    ['get', 'name:en'],
    ['get', 'name']
]

Логика работы:

  1. Проверяется name:ru.
  2. Если поле отсутствует — используется name:en.
  3. Если отсутствует и оно — применяется поле 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 позволяет реализовать локализацию внутри одного стиля.

Пример:

[
    '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

Многие векторные наборы данных, построенные на основе данных 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);

}

Такой подход уменьшает количество операций рендеринга и улучшает отзывчивость интерфейса.


Архитектура многоязычной карты

Полноценная система локализации обычно включает несколько уровней:

  1. Многоязычные данные в векторных тайлах.
  2. Локализованные подписи через text-field.
  3. Механизм резервных языков через coalesce.
  4. Переключение языка во время работы приложения.
  5. Сохранение пользовательских настроек.
  6. Поддержку RTL-языков.
  7. Локализацию пользовательского интерфейса.
  8. Автоматическое определение языка браузера.
  9. Поддержку двуязычных и многострочных подписей.
  10. Централизованное управление локализацией всех слоёв карты.

Совместное использование этих механизмов позволяет создавать картографические приложения мирового уровня, способные корректно отображать географические данные для пользователей различных языковых и культурных сред.