Локализация интерфейса в CesiumJS представляет собой набор практик и архитектурных решений, направленных на адаптацию текстовых элементов, форматов отображения данных и пользовательских сообщений под различные языки и региональные настройки. В отличие от классических UI-фреймворков, здесь отсутствует единая встроенная система интернационализации, поэтому локализация реализуется на уровне приложения и расширений над сценой.
Основная сложность заключается в том, что интерфейс Cesium состоит из нескольких слоёв:
Каждый слой требует отдельного подхода к переводу. Центрального словаря интерфейсных строк нет, поэтому применяется модель внешних словарей и функций форматирования.
На практике используется структура словаря ключ–значение:
const locales = {
ru: {
zoomIn: "Приблизить",
zoomOut: "Отдалить",
home: "Начальный вид",
layers: "Слои",
search: "Поиск",
},
en: {
zoomIn: "Zoom in",
zoomOut: "Zoom out",
home: "Home",
layers: "Layers",
search: "Search",
}
};
Далее слой UI получает строки через функцию:
function t(key, locale = "en") {
return locales[locale][key] || key;
}
Такой подход обеспечивает независимость логики Cesium от конкретного языка.
В CesiumJS стандартный Viewer генерирует элементы управления динамически. Это означает, что изменение языка требует модификации DOM после инициализации.
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.homeButton.container.title = t("home", "ru");
viewer.fullscreenButton.container.title = "Полноэкранный режим";
viewer.sceneModePicker.container.title = "Режим сцены";
Однако прямое изменение DOM не всегда стабильно, так как обновления Cesium могут пересоздавать элементы управления.
Более устойчивый подход заключается в создании Viewer без стандартного UI и последующем построении собственного интерфейса:
const viewer = new Cesium.Viewer("cesiumContainer", {
animation: false,
timeline: false,
baseLayerPicker: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
fullscreenButton: false
});
Далее UI создаётся вручную:
const button = document.createElement("button");
button.innerText = t("zoomIn", "ru");
button.addEventListener("click", () => {
viewer.camera.zoomIn(1000);
});
document.body.appendChild(button);
Такой подход полностью отделяет интерфейс от внутренней логики Cesium.
Важной частью интерфейса являются координаты, высоты, расстояния и
временные метки. Для их форматирования используется стандарт
Intl:
const numberFormatter = new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 2
});
const formattedHeight = numberFormatter.format(1523.456); // "1 523,46"
Для координат:
function formatLonLat(value, locale) {
return new Intl.NumberFormat(locale, {
minimumFractionDigits: 4,
maximumFractionDigits: 4
}).format(value);
}
Cesium активно работает со временем (JulianDate, clock, timeline). Для отображения пользователю требуется преобразование в локальное время:
const date = Cesium.JulianDate.toDate(viewer.clock.currentTime);
const formatted = new Intl.DateTimeFormat("ru-RU", {
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit"
}).format(date);
Это особенно важно для таймлайна и анализа спутниковых данных.
Система CreditDisplay в CesiumJS отображает текстовые атрибуции провайдеров данных. Эти строки часто приходят из внешних источников и требуют постобработки.
viewer.creditDisplay.addDefaultCredit(new Cesium.Credit("Данные © OpenStreetMap", true));
Если требуется мультиязычность:
const creditText = {
ru: "Данные © OpenStreetMap",
en: "Data © OpenStreetMap"
};
viewer.creditDisplay.addDefaultCredit(
new Cesium.Credit(creditText["ru"], true)
);
Tooltip-строки являются частью пользовательского слоя и обычно заменяются вручную:
function setTooltips(locale) {
document.querySelectorAll("[data-tooltip]").forEach(el => {
const key = el.getAttribute("data-tooltip");
el.title = t(key, locale);
});
}
HTML-структура:
<button data-tooltip="zoomIn"></button>
Хотя Cesium сам по себе не управляет направлением текста, интерфейс может требовать поддержки языков с письмом справа налево:
document.documentElement.setAttribute("dir", "rtl");
document.documentElement.lang = "ar";
Для корректного отображения UI важно учитывать:
Ошибки загрузки ресурсов (tiles, imagery, terrain) обычно перехватываются через события:
viewer.scene.globe.tileLoadProgressEvent.addEventListener((count) => {
if (count === 0) {
console.log(t("tilesLoaded", "ru"));
}
});
Для расширенной обработки:
try {
const tileset = new Cesium.Cesium3DTileset({ url: "model.json" });
viewer.scene.primitives.add(tileset);
} catch (e) {
showError(t("loadError", "ru"));
}
Стандартный Geocoder в Viewer требует замены для мультиязычного поведения:
const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false
});
Далее подключается кастомный поиск:
async function search(query, locale) {
const response = await fetch(`/api/search?q=${query}&lang=${locale}`);
return response.json();
}
Результаты отображаются уже с локализованными подписями.
Названия тайловых слоёв и картографических данных часто приходят из внешних провайдеров. Для их адаптации используется слой отображения:
const layerNames = {
streets: { ru: "Улицы", en: "Streets" },
satellite: { ru: "Спутник", en: "Satellite" }
};
function getLayerName(id, locale) {
return layerNames[id][locale];
}
Переключение языка в рантайме требует централизованного обновления всех UI-компонентов:
let currentLocale = "en";
function setLocale(locale) {
currentLocale = locale;
updateUITexts();
updateTooltips(locale);
updateCredits(locale);
}
Ключевая проблема заключается в том, что Cesium не перерисовывает интерфейс автоматически, поэтому требуется ручная синхронизация всех слоёв.
При использовании внешних библиотек (например, i18next) Cesium выступает как визуальный контейнер:
i18next.init({
lng: "ru",
resources: {
ru: {
translation: {
zoomIn: "Приблизить"
}
}
}
});
button.innerText = i18next.t("zoomIn");
Такой подход позволяет централизовать локализацию вне Cesium-логики.
В сложных геоинформационных системах локализация затрагивает:
Label-объекты требуют отдельной обработки текста:
const label = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(90, 45),
label: {
text: t("cityLabel", "ru")
}
});
При смене языка необходимо пересоздавать или обновлять все label-компоненты.
Частая ошибка — обновление UI через массовый DOM-рефлоу. В системах на CesiumJS рекомендуется:
Оптимальная модель — разделение сценической логики и UI-слоя, где Cesium отвечает только за визуализацию, а локализация управляется внешним контроллером.