Map controls speak the reader's language on both engines ('Zoom in', 'Toggle attribution', gesture hints)

MapLibre's and Mapbox's own control labels stayed English under ar/es (rega's row 30). Both engines take
a `locale` map of their UI strings at construction; useMapInstance now passes one built from the new
mapui catalogue (17 strings x 11 locales, parity PASS) for the reader's language. The keys are the
engines' own (MapLibre 5.24's list, plus Mapbox's gesture-hint names); an engine ignores the rest.
Built doxa-simple-map carries the translations; check:i18n-treeshake PASS.
This commit is contained in:
Doxa Maps 2026-09-26 07:48:28 +00:00
parent 3c69770422
commit 7fbad5499c
12 changed files with 237 additions and 0 deletions

View file

@ -38,6 +38,31 @@ const ENGINES = {
maplibre: { global: 'maplibregl', js: CDN.maplibreGlJs.url, css: CDN.maplibreGlCss.url },
}
// THE ENGINE'S OWN CONTROL LABELS ("Zoom in", "Toggle attribution", the gesture hints…) in the reader's
// language. Both engines take a `locale` map of their UI strings at construction; without one they stay
// English under ar/es (rega's row 30). Strings live in library/i18n/locales/<lc>/mapui.json. The keys are
// the engines' own; an engine ignores the ones it does not use.
const ENGINE_UI_KEYS = {
'Map.Title': 'map', 'Marker.Title': 'marker', 'Popup.Close': 'closePopup',
'NavigationControl.ZoomIn': 'zoomIn', 'NavigationControl.ZoomOut': 'zoomOut', 'NavigationControl.ResetBearing': 'resetBearing',
'AttributionControl.ToggleAttribution': 'toggleAttribution', 'AttributionControl.MapFeedback': 'mapFeedback',
'FullscreenControl.Enter': 'enterFullscreen', 'FullscreenControl.Exit': 'exitFullscreen',
'GeolocateControl.FindMyLocation': 'findMyLocation', 'GeolocateControl.LocationNotAvailable': 'locationNotAvailable',
'GlobeControl.Enable': 'enableGlobe', 'GlobeControl.Disable': 'disableGlobe',
'CooperativeGesturesHandler.WindowsHelpText': 'ctrlScroll', 'CooperativeGesturesHandler.MacHelpText': 'cmdScroll',
'CooperativeGesturesHandler.MobileHelpText': 'twoFingers',
// Mapbox's names for the same gesture hints
'ScrollZoomBlocker.CtrlMessage': 'ctrlScroll', 'ScrollZoomBlocker.CmdMessage': 'cmdScroll', 'TouchPanBlocker.Message': 'twoFingers',
}
export function engineUiLocale(lang) {
const out = {}
for (const [engineKey, ours] of Object.entries(ENGINE_UI_KEYS)) {
const v = tStatic(`mapui.${ours}`, lang)
if (v) out[engineKey] = v
}
return out
}
// ONE MAPBOX WORDMARK PER PAGE: every map instance renders its own logo, so a
// page composing several maps stacks duplicates. First live instance keeps the
// logo (Mapbox attribution satisfied once per document); later ones hide theirs
@ -72,6 +97,7 @@ import { resolveMapProvider, explicitMapProvider } from './useMapProvider.js'
import { CDN, pinElement } from '../constants/cdn.js'
import { installRtlTextPlugin } from '../maps/rtl-text.js'
import { localizeBasemapLabels } from '../maps/localize-labels.js'
import { tStatic } from '../i18n/index.js'
/**
* The live engine global for a provider, or undefined if not yet loaded.
@ -515,6 +541,7 @@ export function useMapInstance({
// day/night decision, not a guess made here. (Computed above the branch: the Mapbox
// branch's own `initialConfig` is scoped to that branch and is NOT visible here.)
style: maplibreStyle(effectiveStyle, _bootPreset),
locale: engineUiLocale(readerLang()),
center,
zoom,
minZoom: initialMinZoom,
@ -541,6 +568,7 @@ export function useMapInstance({
accessToken: token, // BP #1 from W1A — per-instance token; avoids last-writer-wins on multi-embed pages
container: containerRef.value, // real DOM element — never a string ID in shadow DOM
style: effectiveStyle,
locale: engineUiLocale(readerLang()),
// Pins a Standard basemap's lightPreset/theme BEFORE its first frame; without it the
// schema default 'day' paints one LIGHT frame before any setConfigProperty can run.
...(initialConfig ? { config: initialConfig } : {}),

View file

@ -0,0 +1,19 @@
{
"map": "الخريطة",
"marker": "علامة على الخريطة",
"closePopup": "إغلاق النافذة المنبثقة",
"zoomIn": "تكبير",
"zoomOut": "تصغير",
"resetBearing": "اسحب لتدوير الخريطة، وانقر لإعادة الشمال",
"toggleAttribution": "إظهار/إخفاء المصادر",
"mapFeedback": "ملاحظات على الخريطة",
"enterFullscreen": "ملء الشاشة",
"exitFullscreen": "الخروج من ملء الشاشة",
"findMyLocation": "تحديد موقعي",
"locationNotAvailable": "الموقع غير متاح",
"enableGlobe": "تفعيل الكرة الأرضية",
"disableGlobe": "إيقاف الكرة الأرضية",
"ctrlScroll": "استخدم Ctrl مع التمرير لتكبير الخريطة",
"cmdScroll": "استخدم ⌘ مع التمرير لتكبير الخريطة",
"twoFingers": "استخدم إصبعين لتحريك الخريطة"
}

View file

@ -0,0 +1,19 @@
{
"map": "Karte",
"marker": "Kartenmarkierung",
"closePopup": "Popup schließen",
"zoomIn": "Vergrößern",
"zoomOut": "Verkleinern",
"resetBearing": "Ziehen, um die Karte zu drehen; klicken, um nach Norden auszurichten",
"toggleAttribution": "Quellenangaben ein-/ausblenden",
"mapFeedback": "Feedback zur Karte",
"enterFullscreen": "Vollbild",
"exitFullscreen": "Vollbild beenden",
"findMyLocation": "Meinen Standort finden",
"locationNotAvailable": "Standort nicht verfügbar",
"enableGlobe": "Globus einschalten",
"disableGlobe": "Globus ausschalten",
"ctrlScroll": "Strg + Scrollen zum Zoomen der Karte",
"cmdScroll": "⌘ + Scrollen zum Zoomen der Karte",
"twoFingers": "Mit zwei Fingern die Karte bewegen"
}

View file

@ -0,0 +1,19 @@
{
"map": "Map",
"marker": "Map marker",
"closePopup": "Close popup",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetBearing": "Drag to rotate map, click to reset north",
"toggleAttribution": "Toggle attribution",
"mapFeedback": "Map feedback",
"enterFullscreen": "Enter fullscreen",
"exitFullscreen": "Exit fullscreen",
"findMyLocation": "Find my location",
"locationNotAvailable": "Location not available",
"enableGlobe": "Enable globe",
"disableGlobe": "Disable globe",
"ctrlScroll": "Use Ctrl + scroll to zoom the map",
"cmdScroll": "Use ⌘ + scroll to zoom the map",
"twoFingers": "Use two fingers to move the map"
}

View file

@ -0,0 +1,19 @@
{
"map": "Mapa",
"marker": "Marcador del mapa",
"closePopup": "Cerrar ventana",
"zoomIn": "Acercar",
"zoomOut": "Alejar",
"resetBearing": "Arrastra para girar el mapa; haz clic para orientar al norte",
"toggleAttribution": "Mostrar u ocultar atribución",
"mapFeedback": "Comentarios sobre el mapa",
"enterFullscreen": "Pantalla completa",
"exitFullscreen": "Salir de pantalla completa",
"findMyLocation": "Buscar mi ubicación",
"locationNotAvailable": "Ubicación no disponible",
"enableGlobe": "Activar globo",
"disableGlobe": "Desactivar globo",
"ctrlScroll": "Usa Ctrl + desplazamiento para hacer zoom en el mapa",
"cmdScroll": "Usa ⌘ + desplazamiento para hacer zoom en el mapa",
"twoFingers": "Usa dos dedos para mover el mapa"
}

View file

@ -0,0 +1,19 @@
{
"map": "Carte",
"marker": "Marqueur de carte",
"closePopup": "Fermer la fenêtre",
"zoomIn": "Zoom avant",
"zoomOut": "Zoom arrière",
"resetBearing": "Faites glisser pour faire pivoter la carte, cliquez pour revenir au nord",
"toggleAttribution": "Afficher/masquer l'attribution",
"mapFeedback": "Commentaires sur la carte",
"enterFullscreen": "Plein écran",
"exitFullscreen": "Quitter le plein écran",
"findMyLocation": "Trouver ma position",
"locationNotAvailable": "Position non disponible",
"enableGlobe": "Activer le globe",
"disableGlobe": "Désactiver le globe",
"ctrlScroll": "Utilisez Ctrl + molette pour zoomer sur la carte",
"cmdScroll": "Utilisez ⌘ + molette pour zoomer sur la carte",
"twoFingers": "Utilisez deux doigts pour déplacer la carte"
}

View file

@ -0,0 +1,19 @@
{
"map": "मानचित्र",
"marker": "मानचित्र चिह्न",
"closePopup": "पॉपअप बंद करें",
"zoomIn": "ज़ूम इन",
"zoomOut": "ज़ूम आउट",
"resetBearing": "मानचित्र घुमाने के लिए खींचें, उत्तर पर लौटने के लिए क्लिक करें",
"toggleAttribution": "श्रेय दिखाएँ/छिपाएँ",
"mapFeedback": "मानचित्र पर प्रतिक्रिया",
"enterFullscreen": "पूर्ण स्क्रीन",
"exitFullscreen": "पूर्ण स्क्रीन से बाहर निकलें",
"findMyLocation": "मेरा स्थान खोजें",
"locationNotAvailable": "स्थान उपलब्ध नहीं",
"enableGlobe": "ग्लोब चालू करें",
"disableGlobe": "ग्लोब बंद करें",
"ctrlScroll": "मानचित्र ज़ूम करने के लिए Ctrl + स्क्रॉल करें",
"cmdScroll": "मानचित्र ज़ूम करने के लिए ⌘ + स्क्रॉल करें",
"twoFingers": "मानचित्र हिलाने के लिए दो उँगलियों का उपयोग करें"
}

View file

@ -0,0 +1,19 @@
{
"map": "Mappa",
"marker": "Indicatore sulla mappa",
"closePopup": "Chiudi popup",
"zoomIn": "Ingrandisci",
"zoomOut": "Riduci",
"resetBearing": "Trascina per ruotare la mappa, fai clic per tornare al nord",
"toggleAttribution": "Mostra/nascondi attribuzione",
"mapFeedback": "Feedback sulla mappa",
"enterFullscreen": "Schermo intero",
"exitFullscreen": "Esci da schermo intero",
"findMyLocation": "Trova la mia posizione",
"locationNotAvailable": "Posizione non disponibile",
"enableGlobe": "Attiva globo",
"disableGlobe": "Disattiva globo",
"ctrlScroll": "Usa Ctrl + scorrimento per ingrandire la mappa",
"cmdScroll": "Usa ⌘ + scorrimento per ingrandire la mappa",
"twoFingers": "Usa due dita per spostare la mappa"
}

View file

@ -0,0 +1,19 @@
{
"map": "Mapa",
"marker": "Marcador do mapa",
"closePopup": "Fechar janela",
"zoomIn": "Aumentar zoom",
"zoomOut": "Diminuir zoom",
"resetBearing": "Arraste para girar o mapa, clique para voltar ao norte",
"toggleAttribution": "Mostrar/ocultar atribuição",
"mapFeedback": "Comentários sobre o mapa",
"enterFullscreen": "Tela cheia",
"exitFullscreen": "Sair da tela cheia",
"findMyLocation": "Encontrar minha localização",
"locationNotAvailable": "Localização indisponível",
"enableGlobe": "Ativar globo",
"disableGlobe": "Desativar globo",
"ctrlScroll": "Use Ctrl + rolagem para ampliar o mapa",
"cmdScroll": "Use ⌘ + rolagem para ampliar o mapa",
"twoFingers": "Use dois dedos para mover o mapa"
}

View file

@ -0,0 +1,19 @@
{
"map": "Hartă",
"marker": "Marcaj pe hartă",
"closePopup": "Închide fereastra",
"zoomIn": "Mărește",
"zoomOut": "Micșorează",
"resetBearing": "Trage pentru a roti harta, apasă pentru a reveni la nord",
"toggleAttribution": "Afișează/ascunde atribuirea",
"mapFeedback": "Feedback despre hartă",
"enterFullscreen": "Ecran complet",
"exitFullscreen": "Ieși din ecran complet",
"findMyLocation": "Găsește-mi locația",
"locationNotAvailable": "Locație indisponibilă",
"enableGlobe": "Activează globul",
"disableGlobe": "Dezactivează globul",
"ctrlScroll": "Folosește Ctrl + derulare pentru a mări harta",
"cmdScroll": "Folosește ⌘ + derulare pentru a mări harta",
"twoFingers": "Folosește două degete pentru a muta harta"
}

View file

@ -0,0 +1,19 @@
{
"map": "Карта",
"marker": "Метка на карте",
"closePopup": "Закрыть окно",
"zoomIn": "Приблизить",
"zoomOut": "Отдалить",
"resetBearing": "Перетащите, чтобы повернуть карту; нажмите, чтобы вернуть север",
"toggleAttribution": "Показать/скрыть источники",
"mapFeedback": "Отзыв о карте",
"enterFullscreen": "Во весь экран",
"exitFullscreen": "Выйти из полноэкранного режима",
"findMyLocation": "Найти моё местоположение",
"locationNotAvailable": "Местоположение недоступно",
"enableGlobe": "Включить глобус",
"disableGlobe": "Выключить глобус",
"ctrlScroll": "Используйте Ctrl + прокрутку для масштабирования карты",
"cmdScroll": "Используйте ⌘ + прокрутку для масштабирования карты",
"twoFingers": "Используйте два пальца, чтобы перемещать карту"
}

View file

@ -0,0 +1,19 @@
{
"map": "地图",
"marker": "地图标记",
"closePopup": "关闭弹窗",
"zoomIn": "放大",
"zoomOut": "缩小",
"resetBearing": "拖动以旋转地图,点击以恢复正北",
"toggleAttribution": "显示/隐藏数据来源",
"mapFeedback": "地图反馈",
"enterFullscreen": "全屏",
"exitFullscreen": "退出全屏",
"findMyLocation": "查找我的位置",
"locationNotAvailable": "无法获取位置",
"enableGlobe": "启用地球仪",
"disableGlobe": "停用地球仪",
"ctrlScroll": "使用 Ctrl + 滚轮缩放地图",
"cmdScroll": "使用 ⌘ + 滚轮缩放地图",
"twoFingers": "使用两根手指移动地图"
}