diff --git a/modules/core/localizer.js b/modules/core/localizer.js
index 39985b266..b68b24338 100644
--- a/modules/core/localizer.js
+++ b/modules/core/localizer.js
@@ -94,14 +94,20 @@ export function coreLocalizer() {
.then(() => {
let requestedLocales = (_preferredLocaleCodes || [])
// List of locales preferred by the browser in priority order.
- // This always includes an `en` fallback, so we know at least one is valid.
- .concat(utilDetect().browserLocales);
+ .concat(utilDetect().browserLocales)
+ // fallback to English since it's the only guaranteed complete language
+ .concat(['en']);
_localeCodes = localesToUseFrom(requestedLocales);
- // run iD in the highest-priority locale; the rest are fallbacks
+ // Run iD in the highest-priority locale; the rest are fallbacks
_localeCode = _localeCodes[0];
- const loadStringsPromises = _localeCodes.map(function(code) {
+ // Will always return the index for `en` if nothing else
+ const fullCoverageIndex = _localeCodes.findIndex(function(locale) {
+ return _dataLocales[locale].pct === 1;
+ });
+ // We only need to load locales up until we find one with full coverage
+ const loadStringsPromises = _localeCodes.slice(0, fullCoverageIndex + 1).map(function(code) {
return localizer.loadLocale(code);
});
return Promise.all(loadStringsPromises);
@@ -116,32 +122,19 @@ export function coreLocalizer() {
function localesToUseFrom(requestedLocales) {
let supportedLocales = _dataLocales;
- let toLoad = [];
-
+ let toUse = [];
for (let i in requestedLocales) {
let locale = requestedLocales[i];
- if (supportedLocales[locale]) {
- toLoad.push(locale);
- }
+ if (supportedLocales[locale]) toUse.push(locale);
if (locale.includes('-')) {
// Full locale ('es-ES'), add fallback to the base ('es')
let langPart = locale.split('-')[0];
- if (supportedLocales[langPart]) {
- toLoad.push(langPart);
- }
+ if (supportedLocales[langPart]) toUse.push(langPart);
}
}
-
- toLoad = utilArrayUniq(toLoad);
-
- // this is guaranteed to always return an index since `en` is always listed
- // and `en` always has full coverage
- let fullCoverageIndex = toLoad.findIndex(function(locale) {
- return supportedLocales[locale].pct === 1;
- });
- // we only need to load locales up until we find one with full coverage
- return toLoad.slice(0, fullCoverageIndex + 1);
+ // remove duplicates
+ return utilArrayUniq(toUse);
}
function updateForCurrentLocale() {
@@ -329,7 +322,11 @@ export function coreLocalizer() {
// Returns the localized text wrapped in an HTML element encoding the locale info
localizer.t.html = function(stringId, replacements, locale) {
const info = localizer.tInfo(stringId, replacements, locale);
- return `${info.text}`;
+ return localizer.htmlForLocalizedText(info.text, info.locale);
+ };
+
+ localizer.htmlForLocalizedText = function(text, localeCode) {
+ return `${text}`;
};
localizer.languageName = (code, options) => {
diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js
index f56f825b5..d2bc29ed7 100644
--- a/modules/services/osm_wikibase.js
+++ b/modules/services/osm_wikibase.js
@@ -32,22 +32,6 @@ function request(url, callback) {
}
-/**
- * Get the best string value from the descriptions/labels result
- * Note that if mediawiki doesn't recognize language code, it will return all values.
- * In that case, fallback to use English.
- * @param values object - either descriptions or labels
- * @param langCode String
- * @returns localized string
- */
-function localizedToString(values, langCode) {
- if (values) {
- values = values[langCode] || values.en;
- }
- return values ? values.value : '';
-}
-
-
export default {
init: function() {
@@ -137,12 +121,16 @@ export default {
var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
var localeSitelink;
- if (params.langCode && _localeIDs[params.langCode] === undefined) {
- // If this is the first time we are asking about this locale,
- // fetch corresponding entity (if it exists), and cache it.
- // If there is no such entry, cache `false` value to avoid re-requesting it.
- localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
- titles.push(localeSitelink);
+ if (params.langCodes) {
+ params.langCodes.forEach(function(langCode) {
+ if (_localeIDs[langCode] === undefined) {
+ // If this is the first time we are asking about this locale,
+ // fetch corresponding entity (if it exists), and cache it.
+ // If there is no such entry, cache `false` value to avoid re-requesting it.
+ localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
+ titles.push(localeSitelink);
+ }
+ });
}
if (rtypeSitelink) {
@@ -183,7 +171,7 @@ export default {
action: 'wbgetentities',
sites: 'wiki',
titles: titles.join('|'),
- languages: params.langCode,
+ languages: params.langCodes.join('|'),
languagefallback: 1,
origin: '*',
format: 'json',
@@ -202,9 +190,6 @@ export default {
var localeID = false;
Object.values(d.entities).forEach(function(res) {
if (res.missing !== '') {
- // Simplify access to the localized values
- res.description = localizedToString(res.descriptions, params.langCode);
- res.label = localizedToString(res.labels, params.langCode);
var title = res.sitelinks.wiki.title;
if (title === rtypeSitelink) {
@@ -226,7 +211,7 @@ export default {
if (localeSitelink) {
// If locale ID is not found, store false to prevent repeated queries
- that.addLocale(params.langCode, localeID);
+ that.addLocale(params.langCodes[0], localeID);
}
callback(null, result);
@@ -253,8 +238,10 @@ export default {
//
getDocs: function(params, callback) {
var that = this;
- var langCode = localizer.localeCode().toLowerCase();
- params.langCode = langCode;
+ var langCodes = localizer.localeCodes().map(function(code) {
+ return code.toLowerCase();
+ });
+ params.langCodes = langCodes;
this.getEntity(params, function(err, data) {
if (err) {
@@ -268,21 +255,33 @@ export default {
return;
}
+ var i;
+ var description;
+ for (i in langCodes) {
+ let code = langCodes[i];
+ if (entity.descriptions[code] && entity.descriptions[code].language === code) {
+ description = entity.descriptions[code];
+ break;
+ }
+ }
+ if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0];
+
// prepare result
var result = {
title: entity.title,
- description: entity.description,
+ description: description ? description.value : '',
+ descriptionLocaleCode: description ? description.language : '',
editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
};
// add image
if (entity.claims) {
var imageroot;
- var image = that.claimToValue(entity, 'P4', langCode);
+ var image = that.claimToValue(entity, 'P4', langCodes[0]);
if (image) {
imageroot = 'https://commons.wikimedia.org/w/index.php';
} else {
- image = that.claimToValue(entity, 'P28', langCode);
+ image = that.claimToValue(entity, 'P28', langCodes[0]);
if (image) {
imageroot = 'https://wiki.openstreetmap.org/w/index.php';
}
@@ -302,21 +301,20 @@ export default {
var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
- // If exact language code does not exist, try to find the first part before the '-'
- // BUG: in some cases, a more elaborate fallback logic might be needed
- var langPrefix = langCode.split('-', 2)[0];
-
- // use the first acceptable wiki page
- result.wiki =
- getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
- getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
- getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
- getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
- getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
- getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
- getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
- getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
- getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
+ var wikis = [rtypeWiki, tagWiki, keyWiki];
+ for (i in wikis) {
+ var wiki = wikis[i];
+ for (var j in langCodes) {
+ var code = langCodes[j];
+ var referenceId = (langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en') ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
+ var info = getWikiInfo(wiki, code, referenceId);
+ if (info) {
+ result.wiki = info;
+ break;
+ }
+ }
+ if (result.wiki) break;
+ }
callback(null, result);
diff --git a/modules/services/wikidata.js b/modules/services/wikidata.js
index d81ec0d5d..a3cd287fb 100644
--- a/modules/services/wikidata.js
+++ b/modules/services/wikidata.js
@@ -1,6 +1,6 @@
import { json as d3_json } from 'd3-fetch';
-import { utilArrayUniq, utilQsString } from '../util';
+import { utilQsString } from '../util';
import { localizer } from '../core/localizer';
var apibase = 'https://www.wikidata.org/w/api.php?';
@@ -85,15 +85,9 @@ export default {
languagesToQuery: function() {
- var localeCode = localizer.localeCode().toLowerCase();
- // HACK: en-us isn't a wikidata language. We should really be filtering by
- // the languages known to be supported by wikidata.
- if (localeCode === 'en-us') localeCode = 'en';
- return utilArrayUniq([
- localeCode,
- localizer.languageCode().toLowerCase(),
- 'en'
- ]);
+ return localizer.localeCodes().map(function(code) {
+ return code.toLowerCase();
+ });
},
@@ -157,14 +151,20 @@ export default {
var i;
var description;
- if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
- description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
+ for (i in langs) {
+ let code = langs[i];
+ if (entity.descriptions[code] && entity.descriptions[code].language === code) {
+ description = entity.descriptions[code];
+ break;
+ }
}
+ if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0];
// prepare result
var result = {
title: entity.id,
- description: description,
+ description: description ? description.value : '',
+ descriptionLocaleCode: description ? description.language : '',
editURL: 'https://www.wikidata.org/wiki/' + entity.id
};
diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js
index da8f2236c..5e000de08 100644
--- a/modules/ui/tag_reference.js
+++ b/modules/ui/tag_reference.js
@@ -3,7 +3,7 @@ import {
select as d3_select
} from 'd3-selection';
-import { t } from '../core/localizer';
+import { t, localizer } from '../core/localizer';
import { services } from '../services';
import { svgIcon } from '../svg/icon';
@@ -64,7 +64,7 @@ export function uiTagReference(what) {
_body
.append('p')
.attr('class', 'tag-reference-description')
- .html(docs.description || t.html('inspector.no_documentation_key'))
+ .html(docs.description ? localizer.htmlForLocalizedText(docs.description, docs.descriptionLocaleCode) : t.html('inspector.no_documentation_key'))
.append('a')
.attr('class', 'tag-reference-edit')
.attr('target', '_blank')
diff --git a/modules/util/detect.js b/modules/util/detect.js
index 1eafa9c7b..974e682a3 100644
--- a/modules/util/detect.js
+++ b/modules/util/detect.js
@@ -94,9 +94,7 @@ export function utilDetect(refresh) {
.concat(navigator.languages || [])
.concat([
// old property for backwards compatibility
- navigator.userLanguage,
- // fallback to English
- 'en'
+ navigator.userLanguage
])
// remove any undefined values
.filter(Boolean)