diff --git a/debug/i18n.html b/debug/i18n.html index 2187b7703a6..35d3af9448c 100644 --- a/debug/i18n.html +++ b/debug/i18n.html @@ -21,7 +21,7 @@ left: 0; padding: 10px; } - + .map-overlay .map-overlay-inner { background-color: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); @@ -29,27 +29,27 @@ padding: 10px; margin-bottom: 10px; } - + .map-overlay-inner fieldset { border: none; padding: 0; margin: 0 0 10px; } - + .map-overlay-inner fieldset:last-child { margin: 0; } - + .map-overlay-inner select { width: 100%; } - + .map-overlay-inner label { display: block; font-weight: bold; margin: 0 0 5px; } - + .map-overlay-inner button { display: inline-block; width: 36px; @@ -57,11 +57,11 @@ border: none; cursor: pointer; } - + .map-overlay-inner button:focus { outline: none; } - + .map-overlay-inner button:hover { box-shadow: inset 0 0 0 3px rgba(0, 0, 0, 0.1); } @@ -132,8 +132,14 @@ diff --git a/src/css/mapbox-gl.css b/src/css/mapbox-gl.css index 5f417312db3..69026ae89f5 100644 --- a/src/css/mapbox-gl.css +++ b/src/css/mapbox-gl.css @@ -518,6 +518,7 @@ a.mapboxgl-ctrl-logo.mapboxgl-compact { padding: 0 5px; color: #333; box-sizing: border-box; + white-space: nowrap; } .mapboxgl-popup { diff --git a/src/ui/control/scale_control.js b/src/ui/control/scale_control.js index c2087cc7a8e..8b9c431ad32 100644 --- a/src/ui/control/scale_control.js +++ b/src/ui/control/scale_control.js @@ -37,13 +37,14 @@ const defaultOptions: Options = { class ScaleControl { _map: Map; _container: HTMLElement; + _language: ?string; options: Options; constructor(options: Options) { this.options = extend({}, defaultOptions, options); bindAll([ - '_onMove', + '_update', 'setUnit' ], this); } @@ -52,26 +53,33 @@ class ScaleControl { return 'bottom-left'; } - _onMove() { - updateScale(this._map, this._container, this.options); + _update() { + updateScale(this._map, this._container, this._language, this.options); } onAdd(map: Map): HTMLElement { this._map = map; + this._language = map.getLanguage(); this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-scale', map.getContainer()); + this._container.dir = 'auto'; - this._map.on('move', this._onMove); - this._onMove(); + this._map.on('move', this._update); + this._update(); return this._container; } onRemove() { this._container.remove(); - this._map.off('move', this._onMove); + this._map.off('move', this._update); this._map = (undefined: any); } + _setLanguage(language: string) { + this._language = language; + this._update(); + } + /** * Set the scale's unit of the distance. * @@ -79,13 +87,13 @@ class ScaleControl { */ setUnit(unit: Unit) { this.options.unit = unit; - updateScale(this._map, this._container, this.options); + this._update(); } } export default ScaleControl; -function updateScale(map, container, options) { +function updateScale(map, container, language, options) { // A horizontal scale is imagined to be present at center of the map // container with maximum length (Default) as 100px. // Using spherical law of cosines approximation, the real distance is @@ -104,26 +112,36 @@ function updateScale(map, container, options) { const maxFeet = 3.2808 * maxMeters; if (maxFeet > 5280) { const maxMiles = maxFeet / 5280; - setScale(container, maxWidth, maxMiles, map._getUIString('ScaleControl.Miles'), map); + setScale(container, maxWidth, maxMiles, language, 'mile', map); } else { - setScale(container, maxWidth, maxFeet, map._getUIString('ScaleControl.Feet'), map); + setScale(container, maxWidth, maxFeet, language, 'foot', map); } } else if (options && options.unit === 'nautical') { const maxNauticals = maxMeters / 1852; - setScale(container, maxWidth, maxNauticals, map._getUIString('ScaleControl.NauticalMiles'), map); + setScale(container, maxWidth, maxNauticals, language, 'nautical-mile', map); } else if (maxMeters >= 1000) { - setScale(container, maxWidth, maxMeters / 1000, map._getUIString('ScaleControl.Kilometers'), map); + setScale(container, maxWidth, maxMeters / 1000, language, 'kilometer', map); } else { - setScale(container, maxWidth, maxMeters, map._getUIString('ScaleControl.Meters'), map); + setScale(container, maxWidth, maxMeters, language, 'meter', map); } } -function setScale(container, maxWidth, maxDistance, unit, map) { +function setScale(container, maxWidth, maxDistance, language, unit, map) { const distance = getRoundNum(maxDistance); const ratio = distance / maxDistance; + map._requestDomTask(() => { container.style.width = `${maxWidth * ratio}px`; - container.innerHTML = `${distance} ${unit}`; + + // Intl.NumberFormat doesn't support nautical-mile as a unit, + // so we are hardcoding `nm` as a unit symbol for all locales + if (unit === 'nautical-mile') { + container.innerHTML = `${distance} nm`; + return; + } + + // $FlowFixMe — flow v0.142.0 doesn't support optional `locales` argument and `unit` style option + container.innerHTML = new Intl.NumberFormat(language, {style: 'unit', unitDisplay: 'narrow', unit}).format(distance); }); } diff --git a/src/ui/default_locale.js b/src/ui/default_locale.js index db9f19c262e..969578ec543 100644 --- a/src/ui/default_locale.js +++ b/src/ui/default_locale.js @@ -12,11 +12,6 @@ const defaultLocale = { 'NavigationControl.ResetBearing': 'Reset bearing to north', 'NavigationControl.ZoomIn': 'Zoom in', 'NavigationControl.ZoomOut': 'Zoom out', - 'ScaleControl.Feet': 'ft', - 'ScaleControl.Meters': 'm', - 'ScaleControl.Kilometers': 'km', - 'ScaleControl.Miles': 'mi', - 'ScaleControl.NauticalMiles': 'nm', 'ScrollZoomBlocker.CtrlMessage': 'Use ctrl + scroll to zoom the map', 'ScrollZoomBlocker.CmdMessage': 'Use ⌘ + scroll to zoom the map', 'TouchPanBlocker.Message': 'Use two fingers to move the map' diff --git a/src/ui/map.js b/src/ui/map.js index 1431eb37ca2..f6f6b8cd377 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -79,6 +79,7 @@ type IControl = { onRemove(map: Map): void; +getDefaultPosition?: () => ControlPosition; + +_setLanguage?: (language: string) => void; } /* eslint-enable no-use-before-define */ @@ -1070,11 +1071,18 @@ class Map extends Camera { if (this.style) { for (const id in this.style._sourceCaches) { const source = this.style._sourceCaches[id]._source; - if (source.language && source.language !== language && source._setLanguage) { - source._setLanguage(language); + if (source.language && source.language !== this._language && source._setLanguage) { + source._setLanguage(this._language); } } } + + for (const control of this._controls) { + if (control._setLanguage) { + control._setLanguage(this._language); + } + } + return this; } diff --git a/test/unit/ui/control/scale.test.js b/test/unit/ui/control/scale.test.js index fb8a7fd3405..f518676234e 100644 --- a/test/unit/ui/control/scale.test.js +++ b/test/unit/ui/control/scale.test.js @@ -38,6 +38,23 @@ test('ScaleControl should change unit of distance after calling setUnit', (t) => t.end(); }); +test('ScaleControl should change language after calling map.setLanguage', (t) => { + const map = createMap(t); + const scale = new ScaleControl(); + const selector = '.mapboxgl-ctrl-bottom-left .mapboxgl-ctrl-scale'; + map.addControl(scale); + map._domRenderTaskQueue.run(); + + let contents = map.getContainer().querySelector(selector).innerHTML; + t.match(contents, /km/); + + map.setLanguage('ru'); + map._domRenderTaskQueue.run(); + contents = map.getContainer().querySelector(selector).innerHTML; + t.match(contents, /км/); + t.end(); +}); + test('ScaleControl should respect the maxWidth regardless of the unit and actual scale', (t) => { const map = createMap(t); const maxWidth = 100;