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.
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.