Skip to content

Commit 55f6157

Browse files
committed
Added name key options
1 parent 6319825 commit 55f6157

File tree

3 files changed

+82
-25
lines changed

3 files changed

+82
-25
lines changed

README.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@ Diplomat is compatible with applications using MapLibre GL JS v5.13.0 and a
1414

1515
The stylesheet must use the newer [expression](https://maplibre.org/maplibre-style-spec/expressions/) syntax; [legacy style functions](https://maplibre.org/maplibre-style-spec/deprecations/#function) are not supported. The stylesheet’s sources must conform to [Diplomat’s schema](#schema). Several popular vector tilesets already conform to this schema, including:
1616

17-
- [OpenMapTiles](https://openmaptiles.org/schema/) implementations, e.g., [MapTiler](https://cloud.maptiler.com/tiles/v3-openmaptiles/), [OpenFreeMap](https://openfreemap.org/), and [OpenStreetMap U.S.](https://tiles.openstreetmap.us/vector/openmaptiles/)
17+
- [OpenMapTiles](https://openmaptiles.org/schema/) implementations, e.g., [MapTiler](https://cloud.maptiler.com/tiles/v3-openmaptiles/), [OpenFreeMap](https://openfreemap.org/), [OpenStreetMap U.S.](https://tiles.openstreetmap.us/vector/openmaptiles/), [Stadia Maps](https://docs.stadiamaps.com/vector/))
1818
- [Tilezen](https://tilezen.readthedocs.io/en/latest/layers/) implementations, e.g., [Protomaps](https://protomaps.com/)
1919

20+
With additional configuration, Diplomat supports even more vector tilesets, including:
21+
22+
- [Mapbox Streets](https://docs.mapbox.com/data/tilesets/reference/mapbox-streets-v8/#names))
23+
- [OpenHistoricalMap](https://wiki.openstreetmap.org/wiki/OpenHistoricalMap/Reuse#Vector_tiles)
24+
- [Shortbread](https://shortbread-tiles.org/schema/): [OpenStreetMap.org](https://vector.openstreetmap.org/)
25+
2026
## Installation
2127

2228
This plugin is available as [an NPM package](https://www.npmjs.com/package/@americana/diplomat). To install it, run the following command:
@@ -58,6 +64,14 @@ map.once("styledata", (event) => {
5864
});
5965
```
6066

67+
If your stylesheet uses a tileset that formats the name keys differently, such as OpenHistoricalMap or Shortbread, set the format when localizing the layers, for example:
68+
69+
```js
70+
map.localizeLayers(style.layers, locales, {
71+
localizedNamePropertyFormat: "name_$1",
72+
});
73+
```
74+
6175
If you set the `hash` option to a string when creating the `Map`, you can have this code respond to a `language` parameter in the URL hash. Add a window event listener for whenever the hash changes, in order to update the layers:
6276

6377
```js
@@ -85,10 +99,10 @@ addEventListener("hashchange", (event) => {
8599

86100
Diplomat can manipulate any GeoJSON or vector tile source, as long as it includes the following properties on each feature:
87101

88-
- **`name`** (`string`): The name in the local or official language.
89-
- **<code>name:<var>xyz</var></code>** (`string`): The name in another language, where <var>xyz</var> is a valid [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). For example, <code>name:zh</code> for Chinese, <code>name:zh-Hant</code> for Traditional Chinese, <code>name:zh-Hant-TW</code> for Traditional Chinese (Taiwan), and <code>name:zh-Latn-pinyin</code> for Chinese in pinyin.
102+
- **`name`** (`string`): The name in the local or official language. You can customize this property by setting the `unlocalizedNameProperty` option when calling [`maplibregl.Map.prototype.localizeLayers()`](#maplibreglmapprototypelocalizelayers).
103+
- **<code>name:<var>xyz</var></code>** (`string`): The name in another language, where <var>xyz</var> is a valid [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). For example, <code>name:zh</code> for Chinese, <code>name:zh-Hant</code> for Traditional Chinese, <code>name:zh-Hant-TW</code> for Traditional Chinese (Taiwan), and <code>name:zh-Latn-pinyin</code> for Chinese in pinyin. You can customize this format by setting the `localizedNamePropertyFormat` option when calling [`maplibregl.Map.prototype.localizeLayers()`](#maplibreglmapprototypelocalizelayers).
90104

91-
For compatibility with the [OpenMapTiles](https://openmaptiles.org/schema/) schema, `name_en` and `name_de` are also recognized as alternatives to `name:en` and `name:de` for English and German, respectively, but only in the `transportation_name` layer. For performance reasons, this format is not supported for any other language or layer.
105+
For compatibility with the [OpenMapTiles](https://openmaptiles.org/schema/) schema, `name_en` and `name_de` are also recognized as alternatives to `name:en` and `name:de` for English and German, respectively, but only in the `transportation_name` layer. For performance reasons, Diplomat does not look for this format by default for any other language or layer.
92106

93107
Each of the supported properties may be set to a list of values separated by [semicolons](https://wiki.openstreetmap.org/wiki/Semi-colon_value_separator). For example, if a place speaks both English and French, `name` should be `English Name;French Name`. Similarly, if a landmark has three equally common names in Spanish, regardless of dialect, `name:es` should be `Nombre Uno;Nombre Dos;Nombre Tres`. In the rare case that a single name contains a semicolon, it should be escaped as a double semicolon (`;;`).
94108

@@ -182,6 +196,9 @@ Parameters:
182196

183197
- **`layers`** (`[object]`): The style layers to localize.
184198
- **`locales`** (`[string]`): The locales to insert into each layer, as a comma-separated list of [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag). Uses the `language` URL hash parameter or browser preferences by default.
199+
- **`options`** (`object`):
200+
- **`unlocalizedNameProperty`** (`string`): The name of the property holding the unlocalized name. `name` by default.
201+
- **`localizedNamePropertyFormat`** (`string`): "The format of properties holding localized names, where `$1` is replaced by an IETF language tag. `name:$1` by default.
185202

186203
> [!NOTE]
187204
> This method modifies the `layers` structure in place. If it comes from the return value of [`maplibregl.Map.prototype.getStyle()`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#getstyle), you must manually synchronize the layers with the style afterwards by calling [`maplibregl.Map.prototype.setStyle()`](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#setstyle).
@@ -190,7 +207,9 @@ Example:
190207

191208
```js
192209
const style = map.getStyle();
193-
map.localizeLayers(style.layers, locales);
210+
map.localizeLayers(style.layers, locales, {
211+
localizedNamePropertyFormat: "name_$1",
212+
});
194213
map.setStyle(style);
195214
```
196215

index.js

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,30 @@ export function getLocales() {
4545
* language that the user prefers.
4646
*
4747
* @param {[string]} locales - Locales of the name fields to include.
48-
* @param {boolean} includesLegacyFields - Whether to include the older fields
48+
* @param {boolean} options.includesLegacyFields - Whether to include the older fields
4949
* that include underscores, for layers that have not transitioned to the
5050
* colon syntax.
51+
* @param {string} options.unlocalizedNameProperty - The name of the property holding the unlocalized name.
52+
* @param {string} options.localizedNamePropertyFormat - The format of properties holding localized names, where `$1` is replaced by an IETF language tag.
5153
*/
52-
export function getLocalizedNameExpression(locales, includesLegacyFields) {
54+
export function getLocalizedNameExpression(locales, options = {}) {
5355
let nameFields = [
5456
...locales.flatMap((l) => {
55-
let fields = [`name:${l}`];
57+
let localizedNamePropertyFormat =
58+
options.localizedNamePropertyFormat || `name:$1`;
59+
let localizedNameProperty = localizedNamePropertyFormat.replaceAll(
60+
"$1",
61+
l,
62+
);
63+
let fields = [localizedNameProperty];
5664
// transportation_label uses an underscore instead of a colon.
5765
// https://github.com/openmaptiles/openmaptiles/issues/769
58-
if (includesLegacyFields && (l === "de" || l === "en"))
59-
fields.push(`name_${l}`);
66+
if (options.includesLegacyFields && (l === "de" || l === "en")) {
67+
fields.push(localizedNameProperty.replaceAll(":", "_"));
68+
}
6069
return fields;
6170
}),
62-
"name",
71+
options.unlocalizedNameProperty || "name",
6372
];
6473
return ["coalesce", ...nameFields.map((f) => ["get", f])];
6574
}
@@ -89,10 +98,15 @@ const diacriticInsensitiveCollatorVariable = `${variablePrefix}__diacriticInsens
8998
*
9099
* @param {[object]} layers - The style layers to localize.
91100
* @param {[string]} locales - The locales to insert into each layer.
101+
* @param {string} options.unlocalizedNameProperty - The name of the property holding the unlocalized name.
102+
* @param {string} options.localizedNamePropertyFormat - The format of properties holding localized names, where `$1` is replaced by an IETF language tag.
92103
*/
93-
export function localizeLayers(layers, locales = getLocales()) {
94-
let localizedNameExpression = getLocalizedNameExpression(locales, false);
95-
let legacyLocalizedNameExpression = getLocalizedNameExpression(locales, true);
104+
export function localizeLayers(layers, locales = getLocales(), options = {}) {
105+
let localizedNameExpression = getLocalizedNameExpression(locales, options);
106+
let legacyLocalizedNameExpression = getLocalizedNameExpression(locales, {
107+
...options,
108+
includesLegacyFields: true,
109+
});
96110

97111
for (let layer of layers) {
98112
if ("layout" in layer && "text-field" in layer.layout) {
@@ -785,7 +799,11 @@ if (typeof window !== "undefined" && "maplibregl" in window) {
785799
localizedNameWithLocalGloss,
786800
updateVariable,
787801
};
788-
maplibregl.Map.prototype.localizeLayers = function (layers, locales) {
789-
localizeLayers(layers, locales);
802+
maplibregl.Map.prototype.localizeLayers = function (
803+
layers,
804+
locales,
805+
options = {},
806+
) {
807+
localizeLayers(layers, locales, options);
790808
};
791809
}

index.spec.mjs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,35 @@ describe("getLocalizedNameExpression", function () {
128128
]);
129129
});
130130
it("includes legacy fields", function () {
131-
assert.deepEqual(getLocalizedNameExpression(["en-US", "en", "de"], true), [
132-
"coalesce",
133-
["get", "name:en-US"],
134-
["get", "name:en"],
135-
["get", "name_en"],
136-
["get", "name:de"],
137-
["get", "name_de"],
138-
["get", "name"],
139-
]);
131+
assert.deepEqual(
132+
getLocalizedNameExpression(["en-US", "en", "de"], {
133+
includesLegacyFields: true,
134+
}),
135+
[
136+
"coalesce",
137+
["get", "name:en-US"],
138+
["get", "name:en"],
139+
["get", "name_en"],
140+
["get", "name:de"],
141+
["get", "name_de"],
142+
["get", "name"],
143+
],
144+
);
145+
});
146+
it("uses the provided property formats", function () {
147+
assert.deepEqual(
148+
getLocalizedNameExpression(["en-US", "en", "de"], {
149+
unlocalizedNameProperty: "moniker",
150+
localizedNamePropertyFormat: "moniker($1)",
151+
}),
152+
[
153+
"coalesce",
154+
["get", "moniker(en-US)"],
155+
["get", "moniker(en)"],
156+
["get", "moniker(de)"],
157+
["get", "moniker"],
158+
],
159+
);
140160
});
141161
});
142162

0 commit comments

Comments
 (0)