Skip to content

Change the Map Language

Every Maptoolkit community style ships as a set of stylesheets — one with local place names, plus one per translation, for example summer.json (local names) and summer-en.json (English names). This map lets you switch the language of every place label — cities, water bodies, POIs, peaks — anywhere in the world, by loading the matching stylesheet with map.setStyle(). All 7 styles (summer, light, hiking, winter, dark, street, cycling) follow this same <style>-<language>.json naming.

import { MaptoolkitLogoControl } from 'https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-gl-logo/+esm';

const LANGUAGES = [
  { label: 'Local', code: null },
  { label: 'العربية', code: 'ar' },
  { label: 'čeština', code: 'cs' },
  { label: 'Deutsch', code: 'de' },
  { label: 'English', code: 'en' },
  { label: 'Español', code: 'es' },
  { label: 'Français', code: 'fr' },
  { label: 'हिन्दी', code: 'hi' },
  { label: 'Magyar', code: 'hu' },
  { label: 'Italiano', code: 'it' },
  { label: '日本語', code: 'ja' },
  { label: '한국어', code: 'ko' },
  { label: 'Polski', code: 'pl' },
  { label: '中文', code: 'zh' }
];

// Every style is published per language, e.g. summer.json (local names) and
// summer-en.json (English names). The same <style>-<language>.json naming
// applies to all 7 Maptoolkit styles, so swap styleBase to switch this map
// to light, hiking, winter, dark, street, or cycling.
const styleBase = 'https://styles.maptoolkit.org/summer';

function styleUrl(code) {
  return code ? `${styleBase}-${code}.json` : `${styleBase}.json`;
}

const map = new maplibregl.Map({
  container: 'map', attributionControl: { customAttribution: '' },
  style: styleUrl(null),
  center: [17.71561, 47.64616], zoom: 3.6, bearing: 0, pitch: 0
});

function applyLanguage(code) {
  map.setStyle(styleUrl(code));
}

// Dropdown UI: a trigger button showing the current language, and a panel
// listing every language with the stylesheet it loads.
(function initLangDropdown() {
  const root    = document.getElementById('mapLang');
  const trigger = root.querySelector('.map-lang-trigger');
  const panel   = root.querySelector('.map-lang-panel');
  const current = root.querySelector('.map-lang-current');

  LANGUAGES.forEach(function (lang, i) {
    const opt = document.createElement('button');
    opt.type = 'button';
    opt.className = 'map-lang-option' + (i === 0 ? ' active' : '');
    opt.role = 'option';
    opt.innerHTML = '<span></span><span class="map-lang-field"></span>';
    opt.children[0].textContent = lang.label;
    opt.children[1].textContent = styleUrl(lang.code).split('/').pop();
    opt.addEventListener('click', function () {
      current.textContent = lang.label;
      panel.querySelectorAll('.map-lang-option').forEach(function (o) { o.classList.toggle('active', o === opt); });
      root.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
      applyLanguage(lang.code);
    });
    panel.appendChild(opt);
  });

  trigger.addEventListener('click', function (e) {
    e.stopPropagation();
    const open = root.classList.toggle('open');
    trigger.setAttribute('aria-expanded', String(open));
  });
  document.addEventListener('click', function (e) {
    if (!root.contains(e.target)) {
      root.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
    }
  });
})();

map.addControl(new MaptoolkitLogoControl());
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/maplibre-gl.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/maplibre-gl.css" />
  <style>
    html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
    #map { width: 100%; height: 100%; }

    .map-lang { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 1; font: 13px/1.2 system-ui, sans-serif; }
    .map-lang-trigger {
      display: inline-flex; align-items: center; gap: 6px;
      padding: 6px 10px; background: #fff; border: none; border-radius: 6px;
      font-weight: 700; font-size: 13px; color: #222; cursor: pointer;
      box-shadow: 0 1px 4px rgba(0,0,0,.3);
    }
    .map-lang-caret { width: 12px; height: 12px; color: #888; transition: transform .15s; }
    .map-lang.open .map-lang-caret { transform: rotate(180deg); }
    .map-lang-panel {
      position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%); min-width: 170px;
      background: #fff; border-radius: 8px; padding: 4px;
      box-shadow: 0 4px 14px rgba(0,0,0,.3);
      display: none; max-height: 280px; overflow-y: auto;
    }
    .map-lang.open .map-lang-panel { display: block; }
    .map-lang-option {
      display: flex; justify-content: space-between; align-items: center; width: 100%;
      background: transparent; border: none; padding: 6px 10px; border-radius: 6px;
      font-size: 12.5px; color: #222; cursor: pointer; text-align: left; gap: 16px;
    }
    .map-lang-option:hover { background: #f1f1f1; }
    .map-lang-option.active { background: #1a73e8; color: #fff; }
    .map-lang-option .map-lang-field { font-family: ui-monospace, monospace; font-size: 11px; color: #888; }
    .map-lang-option.active .map-lang-field { color: rgba(255,255,255,.7); }
  </style>
</head>
<body>
  <div class="map-lang" id="mapLang">
    <button class="map-lang-trigger" type="button" aria-haspopup="listbox" aria-expanded="false">
      <span class="map-lang-current">Local</span>
      <svg class="map-lang-caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
    </button>
    <div class="map-lang-panel" role="listbox"></div>
  </div>
  <div id="map"></div>
  <script type="module">
import { MaptoolkitLogoControl } from 'https://cdn.jsdelivr.net/npm/@maptoolkit/maplibre-gl-logo/+esm';

const LANGUAGES = [
  { label: 'Local', code: null },
  { label: 'العربية', code: 'ar' },
  { label: 'čeština', code: 'cs' },
  { label: 'Deutsch', code: 'de' },
  { label: 'English', code: 'en' },
  { label: 'Español', code: 'es' },
  { label: 'Français', code: 'fr' },
  { label: 'हिन्दी', code: 'hi' },
  { label: 'Magyar', code: 'hu' },
  { label: 'Italiano', code: 'it' },
  { label: '日本語', code: 'ja' },
  { label: '한국어', code: 'ko' },
  { label: 'Polski', code: 'pl' },
  { label: '中文', code: 'zh' }
];

// Every style is published per language, e.g. summer.json (local names) and
// summer-en.json (English names). The same <style>-<language>.json naming
// applies to all 7 Maptoolkit styles, so swap styleBase to switch this map
// to light, hiking, winter, dark, street, or cycling.
const styleBase = 'https://styles.maptoolkit.org/summer';

function styleUrl(code) {
  return code ? `${styleBase}-${code}.json` : `${styleBase}.json`;
}

const map = new maplibregl.Map({
  container: 'map', attributionControl: { customAttribution: '' },
  style: styleUrl(null),
  center: [17.71561, 47.64616], zoom: 3.6, bearing: 0, pitch: 0
});

function applyLanguage(code) {
  map.setStyle(styleUrl(code));
}

// Dropdown UI: a trigger button showing the current language, and a panel
// listing every language with the stylesheet it loads.
(function initLangDropdown() {
  const root    = document.getElementById('mapLang');
  const trigger = root.querySelector('.map-lang-trigger');
  const panel   = root.querySelector('.map-lang-panel');
  const current = root.querySelector('.map-lang-current');

  LANGUAGES.forEach(function (lang, i) {
    const opt = document.createElement('button');
    opt.type = 'button';
    opt.className = 'map-lang-option' + (i === 0 ? ' active' : '');
    opt.role = 'option';
    opt.innerHTML = '<span></span><span class="map-lang-field"></span>';
    opt.children[0].textContent = lang.label;
    opt.children[1].textContent = styleUrl(lang.code).split('/').pop();
    opt.addEventListener('click', function () {
      current.textContent = lang.label;
      panel.querySelectorAll('.map-lang-option').forEach(function (o) { o.classList.toggle('active', o === opt); });
      root.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
      applyLanguage(lang.code);
    });
    panel.appendChild(opt);
  });

  trigger.addEventListener('click', function (e) {
    e.stopPropagation();
    const open = root.classList.toggle('open');
    trigger.setAttribute('aria-expanded', String(open));
  });
  document.addEventListener('click', function (e) {
    if (!root.contains(e.target)) {
      root.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
    }
  });
})();

map.addControl(new MaptoolkitLogoControl());
  </script>
</body>
</html>