Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to localize mapbox UI #8095

Merged
merged 1 commit into from
Nov 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/ui/control/fullscreen_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,15 @@ class FullscreenControl {
}

_updateTitle() {
const title = this._isFullscreen() ? "Exit fullscreen" : "Enter fullscreen";
const title = this._getTitle();
this._fullscreenButton.setAttribute("aria-label", title);
this._fullscreenButton.title = title;
}

_getTitle() {
return this._map._getUIString(this._isFullscreen() ? 'FullscreenControl.Exit' : 'FullscreenControl.Enter');
}

_isFullscreen() {
return this._fullscreen;
}
Expand Down
17 changes: 11 additions & 6 deletions src/ui/control/geolocate_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,9 @@ class GeolocateControl extends Evented {
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background-error');
this._geolocateButton.disabled = true;
this._geolocateButton.title = 'Location not available';
this._geolocateButton.setAttribute('aria-label', 'Location not available');
const title = this._map._getUIString('GeolocateControl.LocationNotAvailable');
this._geolocateButton.title = title;
this._geolocateButton.setAttribute('aria-label', title);

if (this._geolocationWatchID !== undefined) {
this._clearWatch();
Expand Down Expand Up @@ -293,13 +294,17 @@ class GeolocateControl extends Evented {
this._geolocateButton = DOM.create('button', `mapboxgl-ctrl-geolocate`, this._container);
DOM.create('span', `mapboxgl-ctrl-icon`, this._geolocateButton).setAttribute('aria-hidden', true);
this._geolocateButton.type = 'button';
this._geolocateButton.title = 'Find my location';
this._geolocateButton.setAttribute('aria-label', 'Find my location');

if (supported === false) {
warnOnce('Geolocation support is not available so the GeolocateControl will be disabled.');
const title = this._map._getUIString('GeolocateControl.LocationNotAvailable');
this._geolocateButton.disabled = true;
this._geolocateButton.title = 'Location not available';
this._geolocateButton.setAttribute('aria-label', 'Location not available');
this._geolocateButton.title = title;
this._geolocateButton.setAttribute('aria-label', title);
} else {
const title = this._map._getUIString('GeolocateControl.FindMyLocation');
this._geolocateButton.title = title;
this._geolocateButton.setAttribute('aria-label', title);
}

if (this.options.trackUserLocation) {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/control/logo_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class LogoControl {
anchor.target = "_blank";
anchor.rel = "noopener nofollow";
anchor.href = "https://www.mapbox.com/";
anchor.setAttribute("aria-label", "Mapbox logo");
anchor.setAttribute("aria-label", this._map._getUIString('LogoControl.Title'));
anchor.setAttribute("rel", "noopener nofollow");
this._container.appendChild(anchor);
this._container.style.display = 'none';
Expand Down
22 changes: 15 additions & 7 deletions src/ui/control/navigation_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class NavigationControl {
_container: HTMLElement;
_zoomInButton: HTMLButtonElement;
_zoomOutButton: HTMLButtonElement;
_compass: HTMLElement;
_compass: HTMLButtonElement;
_compassIcon: HTMLElement;
_handler: DragRotateHandler;

Expand All @@ -50,18 +50,19 @@ class NavigationControl {

if (this.options.showZoom) {
bindAll([
'_setButtonTitle',
'_updateZoomButtons'
], this);
this._zoomInButton = this._createButton('mapboxgl-ctrl-zoom-in', 'Zoom in', (e) => this._map.zoomIn({}, {originalEvent: e}));
this._zoomInButton = this._createButton('mapboxgl-ctrl-zoom-in', (e) => this._map.zoomIn({}, {originalEvent: e}));
DOM.create('span', `mapboxgl-ctrl-icon`, this._zoomInButton).setAttribute('aria-hidden', true);
this._zoomOutButton = this._createButton('mapboxgl-ctrl-zoom-out', 'Zoom out', (e) => this._map.zoomOut({}, {originalEvent: e}));
this._zoomOutButton = this._createButton('mapboxgl-ctrl-zoom-out', (e) => this._map.zoomOut({}, {originalEvent: e}));
DOM.create('span', `mapboxgl-ctrl-icon`, this._zoomOutButton).setAttribute('aria-hidden', true);
}
if (this.options.showCompass) {
bindAll([
'_rotateCompassArrow'
], this);
this._compass = this._createButton('mapboxgl-ctrl-compass', 'Reset bearing to north', (e) => {
this._compass = this._createButton('mapboxgl-ctrl-compass', (e) => {
if (this.options.visualizePitch) {
this._map.resetNorthPitch({}, {originalEvent: e});
} else {
Expand Down Expand Up @@ -90,10 +91,13 @@ class NavigationControl {
onAdd(map: Map) {
this._map = map;
if (this.options.showZoom) {
this._setButtonTitle(this._zoomInButton, 'ZoomIn');
this._setButtonTitle(this._zoomOutButton, 'ZoomOut');
this._map.on('zoom', this._updateZoomButtons);
this._updateZoomButtons();
}
if (this.options.showCompass) {
this._setButtonTitle(this._compass, 'ResetBearing');
if (this.options.visualizePitch) {
this._map.on('pitch', this._rotateCompassArrow);
}
Expand Down Expand Up @@ -126,14 +130,18 @@ class NavigationControl {
delete this._map;
}

_createButton(className: string, ariaLabel: string, fn: () => mixed) {
_createButton(className: string, fn: () => mixed) {
const a = DOM.create('button', className, this._container);
a.type = 'button';
a.title = ariaLabel;
a.setAttribute('aria-label', ariaLabel);
a.addEventListener('click', fn);
return a;
}

_setButtonTitle(button: HTMLButtonElement, title: string) {
const str = this._map._getUIString(`NavigationControl.${title}`);
button.title = str;
button.setAttribute('aria-label', str);
}
}

export default NavigationControl;
18 changes: 7 additions & 11 deletions src/ui/control/scale_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,23 @@ function updateScale(map, container, options) {
const maxFeet = 3.2808 * maxMeters;
if (maxFeet > 5280) {
const maxMiles = maxFeet / 5280;
setScale(container, maxWidth, maxMiles, 'mi');
setScale(container, maxWidth, maxMiles, map._getUIString('ScaleControl.Miles'));
} else {
setScale(container, maxWidth, maxFeet, 'ft');
setScale(container, maxWidth, maxFeet, map._getUIString('ScaleControl.Feet'));
}
} else if (options && options.unit === 'nautical') {
const maxNauticals = maxMeters / 1852;
setScale(container, maxWidth, maxNauticals, 'nm');
setScale(container, maxWidth, maxNauticals, map._getUIString('ScaleControl.NauticalMiles'));
} else if (maxMeters >= 1000) {
setScale(container, maxWidth, maxMeters / 1000, map._getUIString('ScaleControl.Kilometers'));
} else {
setScale(container, maxWidth, maxMeters, 'm');
setScale(container, maxWidth, maxMeters, map._getUIString('ScaleControl.Meters'));
}
}

function setScale(container, maxWidth, maxDistance, unit) {
let distance = getRoundNum(maxDistance);
const distance = getRoundNum(maxDistance);
const ratio = distance / maxDistance;

if (unit === 'm' && distance >= 1000) {
distance = distance / 1000;
unit = 'km';
}

container.style.width = `${maxWidth * ratio}px`;
container.innerHTML = distance + unit;
}
Expand Down
20 changes: 20 additions & 0 deletions src/ui/default_locale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @flow

const defaultLocale = {
'FullscreenControl.Enter': 'Enter fullscreen',
'FullscreenControl.Exit': 'Exit fullscreen',
'GeolocateControl.FindMyLocation': 'Find my location',
'GeolocateControl.LocationNotAvailable': 'Location not available',
'LogoControl.Title': 'Mapbox logo',
'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'

};

export default defaultLocale;
17 changes: 15 additions & 2 deletions src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type DragPanHandler, {DragPanOptions} from './handler/drag_pan';
import type KeyboardHandler from './handler/keyboard';
import type DoubleClickZoomHandler from './handler/dblclick_zoom';
import type TouchZoomRotateHandler from './handler/touch_zoom_rotate';
import defaultLocale from './default_locale';
import type {TaskID} from '../util/task_queue';
import type {Cancelable} from '../types/cancelable';
import type {
Expand Down Expand Up @@ -96,7 +97,8 @@ type MapOptions = {
renderWorldCopies?: boolean,
maxTileCacheSize?: number,
transformRequest?: RequestTransformFunction,
accessToken: string
accessToken: string,
locale?: Object
};

const defaultMinZoom = 0;
Expand Down Expand Up @@ -235,7 +237,7 @@ const defaultOptions = {
* @param {number} [options.fadeDuration=300] Controls the duration of the fade-in/fade-out animation for label collisions, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading.
* @param {boolean} [options.crossSourceCollisions=true] If `true`, symbols from multiple sources can collide with each other during collision detection. If `false`, collision detection is run separately for the symbols in each source.
* @param {string} [options.accessToken=null] If specified, map will use this token instead of the one defined in mapboxgl.accessToken.

* @param {string} [options.locale=null] A patch to apply to the default localization table for UI strings, e.g. control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; see `src/ui/default_locale.js` for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table).
* @example
* var map = new mapboxgl.Map({
* container: 'map',
Expand Down Expand Up @@ -293,6 +295,7 @@ class Map extends Camera {
_mapId: number;
_localIdeographFontFamily: string;
_requestManager: RequestManager;
_locale: Object;

/**
* The map's {@link ScrollZoomHandler}, which implements zooming in and out with a scroll wheel or trackpad.
Expand Down Expand Up @@ -374,6 +377,7 @@ class Map extends Camera {
this._renderTaskQueue = new TaskQueue();
this._controls = [];
this._mapId = uniqueId();
this._locale = extend({}, defaultLocale, options.locale);
vakila marked this conversation as resolved.
Show resolved Hide resolved

this._requestManager = new RequestManager(options.transformRequest, options.accessToken);

Expand Down Expand Up @@ -1168,6 +1172,15 @@ class Map extends Camera {
}
}

_getUIString(key: string) {
const str = this._locale[key];
if (str == null) {
throw new Error(`Missing UI string '${key}'`);
}

return str;
}

_updateStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions) {
if (this.style) {
this.style.setEventedParent(null);
Expand Down