Vector map tiles provide a comprehensive and customizable mapping solution that unlocks a world of potential for developers and map enthusiasts.
In this tutorial, we're focusing on localizing map labels within MapLibre GL by leveraging the MapLibre Style Specification. We will show how to you can change map layers specification dynamically in a simple way.
To help you see how it's done, we're including a JSFiddle Demo. This online tool lets you see the code in action and try it out for yourself without needing to set up anything on your computer. By the end of this tutorial, you'll know how to make your maps speak multiple languages, enhancing accessibility and user experience for an international audience. Let's get started and unlock the power of language customization in your maps.
OSM as the Translation Source
In the realm of map localization, understanding the origin and extent of translation resources is crucial. OpenStreetMap (OSM) stands out as a primary source for map data, including geographic labels and translations. This extensive, community-driven database provides a foundation for most mapping applications, including those that focus on offering multilingual support.
OSM's comprehensive collection encompasses names of countries, cities, and significant towns, which are generally available in multiple languages, making it an invaluable resource for creating accessible, globally relevant maps.
Here's an example of the name translations of New York City:
Code | Name |
---|---|
name | City of New York |
name:ar | نيويورك |
name:be | Нью-Ёрк |
name:be-tarask | Нью-Ёрк |
name:br | New York |
... | ... |
name:uk | Нью-Йорк |
name:vi | New York |
name:zh | 纽约/紐約 |
name:zh-Hans | 纽约 |
name:zh-Hant | 紐約 |
However, while the translation of major geographic features in OSM is extensive, it's important to note the variability in translation coverage for more granular details, such as street names and amenities.
Understanding MapLibre GL Map
MapLibre GL is engineered to render Vector Maps, offering a sophisticated way to visualize geographical data.
style.json
Vector map tiles composed of detailed information about every segment of the map, organized and accessible through a structure defined in a style.json file, which is the cornerstone of how MapLibre GL interprets and displays vector map data, dictating everything from colors and fonts to the arrangement and visibility of map layers.
Check an example of OSM Bright Map style.json:
https://maps.geoapify.com/v1/styles/osm-bright/style.json?apiKey=YOUR_API_KEY
The style.json prescribes the map layers' behavior according to the Maplibre style specification. This includes defining the types of data used for each layer, conditions under which layers are visible, and their overall appearance on the map. For instance, a layer's style could specify that certain geographic features should only appear at specific zoom levels or that roads should be depicted with particular colors and widths.
For example, here is definition of "place-city" map text ("type": "symbol") layer:
{
"id": "place-city",
"type": "symbol",
"source": "default",
"source-layer": "place",
"filter": [
"all",
[ "!=", "capital", 2 ], [ "==", "class", "city" ]
],
"layout": {
"text-font": [
"Noto Sans Regular"
],
"text-size": {
"base": 1.2,
"stops": [[ 7, 14 ], [11, 24 ]]
},
"text-field": "{name:latin}\n{name:nonlatin}",
"text-max-width": 8,
"visibility": "visible"
},
"paint": {
"text-color": "#333",
"text-halo-width": 1.2,
"text-halo-color": "rgba(255,255,255,0.8)"
}
}
This JSON snippet defines a map layer in MapLibre GL that's designed to display city names. It specifies that the layer will show labels for cities, excluding capitals.
For the text appearance, it uses the "Noto Sans Regular" font, adjusting the size based on zoom levels, and combines Latin and non-Latin names, if available. The text is styled with a specific color and a halo effect for readability.
For map localization, just focus on the "text-field" within the "layout". You can skip the rest for now.
data.json
You might have noticed that the style references non-OSM properties, such as "name:latin" and "name:nonlatin", and wondered how to determine which properties are available for styling.
The data.json file describes the data source, providing insight into the field names that can be used in your map style:
https://maps.geoapify.com/v1/styles/osm-bright/data.json?apiKey=YOUR_API_KEY
For example, to see which translations are handled within the data.json, you can look for entries related to language-specific fields, giving you a clear view of how to access and utilize these translations in your map styling:
"fields": {
"name:mt": "String",
"name:pt": "String",
...
"name:nonlatin": "String",
"name:latin": "String",
...
}
Modifying Map Style for Localization
So, to localize your map, you'll need to modify the map style definitions for text layers. Instead of using non-language-specific fields like "name:latin" and "name:non-latin," you should set the layer to display a localized name specific to the language you're targeting. It's also crucial to define a fallback for the localized name in cases where it is unavailable, ensuring your map remains informative and accessible across different regions.
To tailor your map to serve different languages, there are two primary methods to adjust the map's style:
- Direct Modification of style.json: You can directly edit the style.json file to specify the desired language for map labels. This approach involves pre-defining the localization settings by adjusting the text-field properties within your layers to reflect the targeted language or languages. Once customized, this modified style.json is used when creating your map, ensuring that the map initializes with the intended language settings.
- Dynamically Changing the Map Layer in the Map Object: For scenarios requiring more flexibility, such as changing the language based on user interaction or real-time settings, the map style can be dynamically altered through the Map object in your application's code. This method allows you to programmatically update the text-field properties within the map's layers, adapting the displayed language without needing to reload the map.
Now, let's proceed with a JSFiddle example that showcases how to dynamically change the map style. This example will illustrate the practical application of altering the map's language settings on the fly, demonstrating the flexibility of MapLibre GL for real-time localization adjustments.
JSFiddle Demo Walkthrough
Here is a JSFiddle code sample that demonstrates the dynamic changing of OSM Bright map style to accommodate different languages:
Here's a breakdown of how the JavaScript code works:
1. Initializing the Map
- A new MapLibre GL map instance is created within the my-map container element.
- The map style is set to 'osm-bright' provided by Geoapify, incorporating an API key for authentication.
- A navigation control is added to the map, allowing users to pan and zoom interactively.
2. Identifying Text Layers For Localization
To select layers for localization, we use a straightforward approach:
- Retrieve all 'symbol' type layers whose text-field properties utilize a naming convention like name:XXX for labels.
- These layers are then earmarked for language updates, and their IDs are stored in the
mapTextLayers
array, ensuring they're readily accessible for subsequent localization adjustments. This method efficiently narrows down the target layers, setting the stage for dynamic language switching based on user preferences or application settings.
3. Localizing the Map:
The localizeMap()
function is designed to update the language of map labels dynamically. It's called with a language code (e.g., "en" for English) after the map finishes loading and whenever a user wishes to change the language.
- It's crucial to initiate the localization of the map only after it has fully loaded and the
mapTextLayers
have been defined. - For each layer identified as containing text labels, the function updates the text-field layout property:
map.setLayoutProperty(layerId, 'text-field', ['coalesce', ['get', `name:${lang}`],
['concat', ['get', `name:latin`], '\n', ['get', `name:nonlatin`]]
])
We leverage the following MapLibre's data expressions to set the layer property values:
coalesce
: This function is used to evaluate a list of values or expressions and return the value of the first expression that doesn't result innull
. In the context of localization, coalesce helps us select the first available translation, ensuring that some form of text label is always displayed on the map.concat
: This function combines multiple strings into a single string. It's particularly useful when you want to join different pieces of text, such as combining name:latin and name:nonlatin labels with a separator for layers that might lack a specific language translation.get
: This function retrieves the value of a specified property from the data source. When localizing map layers, we use get to fetch specific translations based on the language code, such asname:${lang}
, where${lang}
is dynamically replaced with the target language code.
Common Challenges and Solutions
Localizing maps, especially with dynamic data sources like those used in MapLibre GL, can present various challenges. Understanding these and knowing how to address them is crucial for creating accessible, user-friendly maps. Here are some common challenges and their solutions:
Ensuring Localization Only After Map Load
Challenge: Attempting to localize the map before it is fully loaded fails, as layers are not available for modification.
Solution: Utilize MapLibre GL's load
event to trigger the localization process. This ensures all layers are loaded and accessible, making your localization efforts comprehensive and effective.
Defining Fallbacks for the Main Language
Challenge: Some areas might lack specific language data, leading to missing labels.
Solution: Extend the coalesce
expression to include multiple languages. This way, if one language is not available, the map can fall back to another specified language. For example, you might prioritize languages based on the user's preferences or region:
['coalesce', ['get', `name:${userPreferredLanguage}`], ['get', 'name:en'], ['get', `name:latin`], ['get', `name:nonlatin`]]
This expression first attempts to use the user's preferred language, then English, and finally falls back to Latin or non-Latin names if necessary.
By anticipating these challenges and implementing the suggested solutions, you can significantly enhance the localization quality of your maps, making them more accessible and user-friendly for a global audience.
Conclusion
In conclusion, we've delved into the specifics of map localization using MapLibre GL, highlighting the steps and strategies to dynamically adapt map languages for global audiences. However, it's important to recognize that MapLibre GL offers far more than just localization capabilities. This powerful library opens up a world of possibilities for map customization, allowing developers to tailor every aspect of their maps to fit the unique needs and preferences of their users.
From adjusting visual styles to incorporating custom data layers and interactive elements, MapLibre GL equips you with the flexibility to create highly personalized map experiences. Whether you're aiming to enhance user engagement, visualize complex datasets, or simply create beautiful maps, MapLibre GL provides the features and control necessary to achieve your goals.