@americana/diplomat 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,34 @@
2
2
 
3
3
  Diplomat prepares your interactive map for an international audience. With a few lines of code, your [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js/)–powered map will speak the user’s preferred language while informing them about local languages the world over.
4
4
 
5
- | Before | After |
6
- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
- | <img src="docs/navajo-nation.png" width="400" alt="Navajo Nation;Naabeehó Bináhásdzo"> | [<img src="docs/navajo-nation-es.png" width="400" alt="Nación Navajo (Navajo Nation • Naabeehó Bináhásdzo)">](https://americanamap.org/#map=9/36.2134/-109.2837&language=es) |
8
- | <img src="docs/north-sea.png" width="400" alt="North Sea / Nordsee / Noordzee / Nordsøen / Nordsjøen / Mer du Nord"> | [<img src="docs/north-sea-la.png" width="400" alt="Mare Germanicum">](https://americanamap.org/#map=4/56/3&language=la) |
9
- | <img src="docs/section-ross.png" width="400" alt="Ross Avenue;Tennesssee Avenue at Rhode Island Avenue;Section Avenue"> | [<img src="docs/section-ross-en.png" width="400" alt="Ross Avenue • Tennesssee Avenue at Rhode Island Avenue • Section Avenue">](https://americanamap.org/#map=17/39.168568/-84.460075) |
5
+ | Before | After |
6
+ | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | <img src="docs/navajo-nation.png" width="400" alt="Navajo Nation;Naabeehó Bináhásdzo"> | [<img src="docs/navajo-nation-es.png" width="400" alt="Nación Navajo (Navajo Nation • Naabeehó Bináhásdzo)">](https://americanamap.org/#map=9/36.2134/-109.2837&language=es) |
8
+ | <img src="docs/north-sea.png" width="400" alt="North Sea / Nordsee / Noordzee / Nordsøen / Nordsjøen / Mer du Nord"> | [<img src="docs/north-sea-la.png" width="400" alt="Mare Germanicum">](https://americanamap.org/#map=4/56/3&language=la) |
9
+ | <img src="docs/section-ross.png" width="400" alt="Ross Avenue;Tennessee Avenue at Rhode Island Avenue;Section Avenue"> | [<img src="docs/section-ross-en.png" width="400" alt="Ross Avenue • Tennessee Avenue at Rhode Island Avenue • Section Avenue">](https://americanamap.org/#map=17/39.168568/-84.460075) |
10
+
11
+ ## Features
12
+
13
+ Diplomat gives all parties a win-win:
14
+
15
+ - Tailors labels to the user’s preferred language.
16
+ - Respects multilingualism with optional dual language labels: both the user’s preferred language and the local native language.
17
+ - Recognizes any language, dialect, or script out of the box with sophisticated language code matching.
18
+
19
+ Diplomat lets your designer save face:
20
+
21
+ - Uses space efficiently with both multiline and inline label layouts.
22
+ - Deduplicates names within a label to avoid clutter.
23
+ - Preserves diacritics in English exonyms where appropriate.
24
+ - Respects right-to-left and vertical text layouts.
25
+
26
+ Diplomat works quietly behind the scenes:
27
+
28
+ - Changes the style on the fly at runtime – no need to publish a new style or tileset for every language.
29
+ - You choose which style layers to localize, or localize them all automatically.
30
+ - Supports multiple popular vector tile schemas without hacky workarounds, plus custom vector tilesets and GeoJSON sources.
31
+
32
+ All localization is subject to the availability of localized names in the map data source. See [the caveats](#caveats) for more details.
10
33
 
11
34
  ## Requirements
12
35
 
@@ -21,7 +44,7 @@ With additional configuration, Diplomat supports even more vector tilesets, incl
21
44
 
22
45
  - [Mapbox Streets](https://docs.mapbox.com/data/tilesets/reference/mapbox-streets-v8/#names)
23
46
  - [OpenHistoricalMap](https://wiki.openstreetmap.org/wiki/OpenHistoricalMap/Reuse#Vector_tiles)
24
- - [Shortbread](https://shortbread-tiles.org/schema/): [OpenStreetMap.org](https://vector.openstreetmap.org/)
47
+ - [Shortbread](https://shortbread-tiles.org/schema/) implementations, e.g., [OpenStreetMap.org](https://vector.openstreetmap.org/), [VersaTiles](https://github.com/versatiles-org/versatiles-generator/)
25
48
 
26
49
  ## Installation
27
50
 
@@ -35,11 +58,17 @@ Alternatively, you can include the plugin as a standalone script from a CDN such
35
58
 
36
59
  ## Usage
37
60
 
61
+ Import Diplomat as a module:
62
+
63
+ ```js
64
+ import * as Diplomat from "@americana/diplomat";
65
+ ```
66
+
38
67
  After creating an instance of `maplibregl.Map`, register an event listener for the `styledata` event that localizes the map:
39
68
 
40
69
  ```js
41
70
  map.once("styledata", (event) => {
42
- map.localizeStyle();
71
+ Diplomat.localizeStyle(map);
43
72
  });
44
73
  ```
45
74
 
@@ -47,8 +76,8 @@ If your stylesheet uses a tileset that formats the name keys differently, such a
47
76
 
48
77
  ```js
49
78
  map.once("styledata", (event) => {
50
- let locales = maplibregl.Diplomat.getLocales();
51
- map.localizeStyle(locales, {
79
+ let locales = Diplomat.getLocales();
80
+ Diplomat.localizeStyle(map, locales, {
52
81
  localizedNamePropertyFormat: "name_$1",
53
82
  });
54
83
  });
@@ -58,15 +87,11 @@ If you set the `hash` option to a string when creating the `Map`, you can have t
58
87
 
59
88
  ```js
60
89
  addEventListener("hashchange", (event) => {
61
- let oldLanguage = maplibregl.Diplomat.getLanguageFromURL(
62
- new URL(event.oldURL),
63
- );
64
- let newLanguage = maplibregl.Diplomat.getLanguageFromURL(
65
- new URL(event.newURL),
66
- );
90
+ let oldLanguage = Diplomat.getLanguageFromURL(new URL(event.oldURL));
91
+ let newLanguage = Diplomat.getLanguageFromURL(new URL(event.newURL));
67
92
 
68
93
  if (oldLanguage !== newLanguage) {
69
- map.localizeStyle();
94
+ Diplomat.localizeStyle(map);
70
95
  }
71
96
  });
72
97
  ```
@@ -75,7 +100,7 @@ Similarly, you can immediately respond to any change the user makes to their bro
75
100
 
76
101
  ```js
77
102
  addEventListener("languagechange", (event) => {
78
- map.localizeStyle();
103
+ Diplomat.localizeStyle(map);
79
104
  });
80
105
  ```
81
106
 
@@ -95,7 +120,7 @@ Each of the supported properties may be set to a list of values separated by [se
95
120
 
96
121
  ## API
97
122
 
98
- This plugin adds several constants to a `maplibregl.Diplomat` namespace and adds a single method to each instance of `maplibregl.Map`.
123
+ This plugin adds several symbols to a `maplibregl.Diplomat` namespace and adds a single method to each instance of `maplibregl.Map`. The following documentation uses the notation `maplibregl.Diplomat.*` in case you include Diplomat as a script. However, if you import Diplomat as a module, these symbols are directly imported into your code, without any namespacing.
99
124
 
100
125
  ### `maplibregl.Diplomat.localizedName`
101
126
 
@@ -213,20 +238,53 @@ Example:
213
238
  maplibregl.Diplomat.getLocales().includes("en");
214
239
  ```
215
240
 
216
- ### `maplibregl.Map.prototype.localizeStyle()`
241
+ ### `maplibregl.Diplomat.getRelatedLanguageTags()`
242
+
243
+ Returns an array of the language tags related to the given language tag, sorted from most specific to least specific.
244
+
245
+ Parameters:
246
+
247
+ - **`tag`** (`string`): The language tag that the returned language tags are related to.
248
+
249
+ Returns a sorted array of related language tags, or an empty array if `tag` is malformed.
250
+
251
+ Example:
252
+
253
+ ```js
254
+ maplibregl.Diplomat.getRelatedLanguageTags("sr-RS").includes("sr-Cyrl");
255
+ maplibregl.Diplomat.getRelatedLanguageTags("zh").includes("zh-Hans-CN");
256
+ ```
257
+
258
+ ### `maplibregl.Diplomat.localizeStyle()`
217
259
 
218
260
  Updates each style layer's `text-field` value to match the given locales, upgrading any unlocalizable layer along the way.
219
261
 
220
- This method ugprades unlocalizable layers to localized multiline or inline labels depending on the `symbol-placement` layout property. To add a dual language label to a layer, set its `text-field` layout property manually using the [`maplibregl.Diplomat.localizedNameWithLocalGloss`](#maplibrediplomatlocalizednamewithlocalgloss) constant.
262
+ If neither `options.layers` nor `options.sourceLayers` is specified in `options`, this function makes localizable any style layer that gets the feature property specified in `options.unlocalizedNameProperty`, or `name` by default.
221
263
 
222
264
  Parameters:
223
265
 
224
- - **`layers`** (`[object]`): The style layers to localize.
266
+ - **`map`** (`maplibregl.Map`): The map to localize.
225
267
  - **`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.
226
268
  - **`options`** (`object`):
269
+ - **`layers`** (`[string]`): The style layers with these IDs will be made localizable.
270
+ - **`sourceLayers`** (`[string]`): The style layers that use these source layers will be made localizable. These are source layer IDs, not style layer IDs.
227
271
  - **`unlocalizedNameProperty`** (`string`): The name of the property holding the unlocalized name. `name` by default.
228
- - **`localizedNamePropertyFormat`** (`string`): "The format of properties holding localized names, where `$1` is replaced by an IETF language tag. `name:$1` by default.
229
- - **`options.uppercaseCountryNames`** (`boolean`): Whether to write country names in all uppercase, respecting the locale’s case conventions.
272
+ - **`localizedNamePropertyFormat`** (`string`): The format of properties holding localized names, where `$1` is replaced by an IETF language tag. `name:$1` by default.
273
+ - **`glossLocalNames`** (`boolean`): Whether to format each label as a dual language label including a local name gloss.
274
+ - **`uppercaseCountryNames`** (`boolean`): Whether to write country names in all uppercase, respecting the locale’s case conventions.
275
+
276
+ Example:
277
+
278
+ ```js
279
+ maplibregl.Diplomat.localizeStyle(map, ["eo"], {
280
+ sourceLayers: ["place"],
281
+ glossLocalNames: true,
282
+ });
283
+ ```
284
+
285
+ ### `maplibregl.Map.prototype.localizeStyle()`
286
+
287
+ A wrapper for [`maplibregl.Diplomat.localizeStyle()`](#maplibregldiplomatlocalizestyle) that does not require passing in a `maplibregl.Map`. Only available when including Diplomat as a script.
230
288
 
231
289
  Example:
232
290
 
@@ -242,7 +300,7 @@ Diplomat only switches between languages that are present in the stylesheet’s
242
300
 
243
301
  By default, MapLibre GL&nbsp;JS does not support bidirectional text. Arabic, Hebrew, and other right-to-left languages will be unreadable unless you [install the mapbox-gl-rtl-text plugin](https://maplibre.org/maplibre-gl-js/docs/examples/add-support-for-right-to-left-scripts/).
244
302
 
245
- Diplomat only performs basic language fallbacks according to the [ICU locale fallback algorithm](https://unicode-org.github.io/icu/userguide/locale/#fallback). It makes no attempt to fallback to a related but distinct language code, for example from `sr-Cyrl` to `ru` or from `nb` to `no`. Instead, the user can [set their preferred languages](https://www.w3.org/International/questions/qa-lang-priorities) in their browser or operating system settings.
303
+ Diplomat performs basic language fallbacks according to the [ICU locale fallback algorithm](https://unicode-org.github.io/icu/userguide/locale/#fallback). Additionally, it implements the [Likely Subtags](https://www.unicode.org/reports/tr35/#Likely_Subtags) algorithm of Unicode Technical Standard #35, so for example requesting either `zh` or `cmn` returns a name tagged as `zh-Hans-CN`, among other variations. However, in general, it does not fall back to a related but distinct language code, such as from `sr-Cyrl` to `ru` or from `nb` to `no`. Instead, the user can [set their preferred languages](https://www.w3.org/International/questions/qa-lang-priorities) in their browser or operating system settings.
246
304
 
247
305
  For historical reasons, [OpenStreetMap’s coverage in many reasons](https://wiki.openstreetmap.org/wiki/Multilingual_names) encodes multiple local names separated by human-readable punctuation. Diplomat makes no attempt to guess which punctuation is part of a name and which punctuation delimits two names.
248
306
 
@@ -83,11 +83,10 @@ addEventListener("load", () => {
83
83
  );
84
84
 
85
85
  map.once("styledata", (event) => {
86
- map.setLayoutProperty(
87
- "place-labels",
88
- "text-field",
89
- maplibregl.Diplomat.localizedNameWithLocalGloss,
90
- );
86
+ map.localizeStyle(maplibregl.Diplomat.getLocales(), {
87
+ sourceLayers: ["place"],
88
+ glossLocalNames: true,
89
+ });
91
90
  map.localizeStyle(maplibregl.Diplomat.getLocales(), {
92
91
  uppercaseCountryNames: true,
93
92
  });