Skip to content
Change a Map's Language

Change a Map's Language in Maptoolkit Maps JS

Map label language in Maptoolkit Maps JS is controlled by the text-field layout property on symbol layers, which reads from a name field in the vector tile data. To switch language, you iterate over all symbol layers and update text-field to reference the appropriate localized name field, such as name:de or name:fr. Use this to build multilingual map applications where the label language should match the user’s browser locale or a dropdown selection.

const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [10.0, 51.0],
        zoom: 4,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    function setLanguage(field) {
        if (!map.isStyleLoaded()) return;
        const layers = map.getStyle().layers;
        for (const layer of layers) {
            if (layer.type !== 'symbol') continue;
            if (!layer.layout || !layer.layout['text-field']) continue;
            const expr = field === 'name'
                ? ['get', 'name']
                : ['coalesce', ['get', field], ['get', 'name']];
            map.setLayoutProperty(layer.id, 'text-field', expr);
        }
    }
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Change a Map's Language - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Switch map label language by updating the text-field expression on symbol layers." />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.css" />
    <style>
        html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
        #map { width: 100%; height: 100%; }
        #controls {
            position: absolute;
            top: 10px;
            left: 10px;
            display: flex;
            gap: 6px;
            flex-wrap: wrap;
            max-width: 320px;
        }
        #controls button {
            padding: 8px 14px;
            background: #3887be;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 13px;
        }
        #controls button:hover { background: #2c6d9e; }
    </style>
</head>
<body>
<div id="map"></div>
<div id="controls">
    <button onclick="setLanguage('name')">Local</button>
    <button onclick="setLanguage('name_de')">DE</button>
    <button onclick="setLanguage('name_en')">EN</button>
    <button onclick="setLanguage('name_fr')">FR</button>
    <button onclick="setLanguage('name_it')">IT</button>
    <button onclick="setLanguage('name_es')">ES</button>
    <button onclick="setLanguage('name_zh')">中文</button>
</div>
<script>
    const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
        center: [10.0, 51.0],
        zoom: 4,
        attributionControl: { compact: false }
    });

    map.addControl(new maptoolkit.NavigationControl(), 'top-right');

    function setLanguage(field) {
        if (!map.isStyleLoaded()) return;
        const layers = map.getStyle().layers;
        for (const layer of layers) {
            if (layer.type !== 'symbol') continue;
            if (!layer.layout || !layer.layout['text-field']) continue;
            const expr = field === 'name'
                ? ['get', 'name']
                : ['coalesce', ['get', field], ['get', 'name']];
            map.setLayoutProperty(layer.id, 'text-field', expr);
        }
    }
</script>
</body>
</html>

Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.

Use the Maptoolkit Connector. Create an interactive map with zoom level 4, centered around [10.0, 51.0]. Add buttons to switch map label language between Local, DE, EN, FR, IT, ES, and Chinese.

How it works

Labels come from the vector tiles, which carry a name field plus name_de, name_en and other translations. Switching language means changing the text-field expression on every symbol layer to read a different field.

The loop walks map.getStyle().layers, skips anything that is not a symbol with a text-field, and calls setLayoutProperty on the rest. There is no single map-level language switch, because the field name lives in each layer.

Two things about the translation data are worth knowing. Not every place has every language, so a bare ['get', 'name_de'] loses labels that have no German name; a coalesce back to name keeps them. And translations that are byte-identical to name are dropped from the tiles deliberately, since consumers fall back to name anyway, so an expression without a fallback loses more labels than you would expect.

Arabic is right-to-left. MapLibre GL JS handles it natively from version 6.9.0; below that it needs the RTL text plugin registered or the letters render unjoined and reversed.

isStyleLoaded guards the call, since layers cannot be read before the style parses.

Next steps

Detecting the browser’s language and setting it once at load is the usual next step, with a manual switch for the cases where the guess is wrong.

Labels are only part of localisation, and their casing and spacing usually needs attention at the same time. Units, date formats and the direction of your own interface all have to follow, and a map showing kilometres inside an imperial interface is a more obvious mistake than an untranslated place name.