mirror of
https://github.com/FoggedLens/iD.git
synced 2026-06-05 14:38:05 +02:00
Implement support for multilingual descriptions from wiki data items
* Takes data directly from the Wikibase data items (OSM Wiki)
https://wiki.openstreetmap.org/wiki/OpenStreetMap:Data_Items
* Understands the difference in regions - e.g. will show different
images depending on the local settings
* Perf: Single request will get both the tag and key description
This commit is contained in:
@@ -4,7 +4,6 @@ import _forEach from 'lodash-es/forEach';
|
||||
import { json as d3_json } from 'd3-request';
|
||||
|
||||
import { utilQsString } from '../util';
|
||||
import { currentLocale } from '../util/locale';
|
||||
|
||||
|
||||
var apibase = 'https://wiki.openstreetmap.org/w/api.php';
|
||||
@@ -38,28 +37,140 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
docs: function(params, callback) {
|
||||
/** List of data items representing language regions.
|
||||
* To regenerate, use Sophox query: http://tinyurl.com/y6v9ne2c (every instance of Q6999)
|
||||
* A less accurate list can be seen here (everything that links to Q6999):
|
||||
* https://wiki.openstreetmap.org/w/index.php?title=Special%3AWhatLinksHere&target=Item%3AQ6999&namespace=120
|
||||
*/
|
||||
regionCodes: {
|
||||
ar: 'Q7780', az: 'Q7781', bg: 'Q7782', bn: 'Q7783', ca: 'Q7784', cs: 'Q7785', da: 'Q7786',
|
||||
de: 'Q6994', el: 'Q7787', es: 'Q7788', et: 'Q7789', fa: 'Q7790', fi: 'Q7791', fr: 'Q7792',
|
||||
gl: 'Q7793', hr: 'Q7794', ht: 'Q7795', hu: 'Q7796', id: 'Q7797', it: 'Q7798', ja: 'Q7799',
|
||||
ko: 'Q7800', lt: 'Q7801', lv: 'Q7802', ms: 'Q7803', nl: 'Q7804', no: 'Q7805', pl: 'Q7806',
|
||||
pt: 'Q7807', ro: 'Q7808', ru: 'Q7809', sk: 'Q7810', sq: 'Q7811', sv: 'Q7812', tr: 'Q7813',
|
||||
uk: 'Q7814', vi: 'Q7815', yue: 'Q7816', 'zh-hans': 'Q7817', 'zh-hant': 'Q7818',
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the best value for the property, or undefined if not found
|
||||
* @param entity object from wikibase
|
||||
* @param property string e.g. 'P4' for image
|
||||
* @param langCode string e.g. 'fr' for French
|
||||
*/
|
||||
claimToValue: function(entity, property, langCode) {
|
||||
if (!entity.claims[property]) return undefined;
|
||||
var region = this.regionCodes[langCode];
|
||||
var preferredPick, regionPick;
|
||||
_forEach(entity.claims[property], function(stmt) {
|
||||
// If exists, use value limited to the needed language (has a qualifier P26 = region)
|
||||
// Or if not found, use the first value with the "preferred" rank
|
||||
if (!preferredPick && stmt.rank === 'preferred') {
|
||||
preferredPick = stmt;
|
||||
}
|
||||
if (stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === region) {
|
||||
regionPick = stmt;
|
||||
}
|
||||
});
|
||||
var result = regionPick || preferredPick;
|
||||
|
||||
if (result) {
|
||||
var datavalue = result.mainsnak.datavalue;
|
||||
return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getDescription: function(entity) {
|
||||
if (entity.descriptions) {
|
||||
// Assume that there will be at most two languages because of
|
||||
// how we request it: English + possibly another one.
|
||||
// Pick non-English description if available (if we have more than one)
|
||||
var langs = Object.keys(entity.descriptions);
|
||||
if (langs.length) {
|
||||
var lng = langs.length > 1 && langs[0] === 'en' ? langs[1] : langs[0];
|
||||
return entity.descriptions[lng].value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
|
||||
toSitelink: function(key, value) {
|
||||
var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
|
||||
return result.replace(/_/g, ' ').trim();
|
||||
},
|
||||
|
||||
|
||||
getEntity: function(params, callback) {
|
||||
var doRequest = params.debounce ? debouncedRequest : request;
|
||||
|
||||
// if (params.value) path = 'tag/wiki_pages?';
|
||||
// else if (params.rtype) path = 'relation/wiki_pages?';
|
||||
var titles = [];
|
||||
var languages = ['en'];
|
||||
var result = {};
|
||||
var keySitelink = this.toSitelink(params.key);
|
||||
var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false;
|
||||
|
||||
if (params.langCode && params.langCode !== 'en') {
|
||||
languages.push(params.langCode);
|
||||
}
|
||||
|
||||
if (_wikibaseCache[keySitelink]) {
|
||||
result.key = _wikibaseCache[keySitelink];
|
||||
} else {
|
||||
titles.push(keySitelink);
|
||||
}
|
||||
|
||||
if (tagSitelink) {
|
||||
if (_wikibaseCache[tagSitelink]) {
|
||||
result.key = _wikibaseCache[tagSitelink];
|
||||
} else {
|
||||
titles.push(tagSitelink);
|
||||
}
|
||||
}
|
||||
|
||||
if (!titles.length) {
|
||||
// Nothing to do, we already had everything in the cache
|
||||
return callback(null, result);
|
||||
}
|
||||
|
||||
var obj = {
|
||||
action: 'wbgetentities',
|
||||
sites: 'wiki',
|
||||
titles: 'Tag:amenity=parking',
|
||||
languages: 'en',
|
||||
titles: titles.join('|'),
|
||||
languages: languages.join('|'),
|
||||
origin: '*',
|
||||
formatversion: 2,
|
||||
format: 'json'
|
||||
}
|
||||
format: 'json',
|
||||
// There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
|
||||
// We shouldn't use v1 until it gets fixed, but should switch to it afterwards
|
||||
// formatversion: 2,
|
||||
};
|
||||
|
||||
var url = apibase + '?' + utilQsString(obj);
|
||||
doRequest(url, function(err, d) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (!d.success || d.error) {
|
||||
callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
|
||||
} else {
|
||||
_wikibaseCache[url] = d.data;
|
||||
callback(null, d.data);
|
||||
_forEach(d.entities, function(res) {
|
||||
if (res.missing !== '') {
|
||||
var title = res.sitelinks.wiki.title;
|
||||
if (title === keySitelink) {
|
||||
_wikibaseCache[keySitelink] = res;
|
||||
result.key = res;
|
||||
} else if (title === tagSitelink) {
|
||||
_wikibaseCache[tagSitelink] = res;
|
||||
result.tag = res;
|
||||
} else {
|
||||
console.log('Unexpected title ' + title);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
+49
-43
@@ -1,6 +1,3 @@
|
||||
import _find from 'lodash-es/find';
|
||||
import _omit from 'lodash-es/omit';
|
||||
|
||||
import {
|
||||
event as d3_event,
|
||||
select as d3_select
|
||||
@@ -10,10 +7,10 @@ import { t } from '../util/locale';
|
||||
import { utilDetect } from '../util/detect';
|
||||
import { services } from '../services';
|
||||
import { svgIcon } from '../svg';
|
||||
import { utilQsString } from '../util';
|
||||
|
||||
|
||||
export function uiTagReference(tag) {
|
||||
// var taginfo = services.taginfo;
|
||||
var wikibase = services.osmWikibase;
|
||||
var tagReference = {};
|
||||
|
||||
@@ -22,33 +19,40 @@ export function uiTagReference(tag) {
|
||||
var _loaded;
|
||||
var _showing;
|
||||
|
||||
/**
|
||||
* @returns {{itemTitle: String, description: String, image: String|null}|null}
|
||||
**/
|
||||
function findLocal(data) {
|
||||
var entity = data.tag || data.key;
|
||||
if (!entity) return null;
|
||||
|
||||
// function findLocal(data) {
|
||||
// var locale = utilDetect().locale.toLowerCase();
|
||||
// var localized;
|
||||
var result = {
|
||||
title: entity.title,
|
||||
description: wikibase.getDescription(entity),
|
||||
};
|
||||
|
||||
// if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br'
|
||||
// localized = _find(data, function(d) {
|
||||
// return d.lang.toLowerCase() === locale;
|
||||
// });
|
||||
// if (localized) return localized;
|
||||
// }
|
||||
if (entity.claims) {
|
||||
var langCode = utilDetect().locale.toLowerCase();
|
||||
var url;
|
||||
var image = wikibase.claimToValue(entity, 'P4', langCode);
|
||||
if (image) {
|
||||
url = 'https://commons.wikimedia.org/w/index.php';
|
||||
} else {
|
||||
image = wikibase.claimToValue(entity, 'P28', langCode);
|
||||
if (image) {
|
||||
url = 'https://wiki.openstreetmap.org/w/index.php';
|
||||
}
|
||||
}
|
||||
if (image) {
|
||||
result.image = {
|
||||
url: url,
|
||||
title: 'Special:Redirect/file/' + image
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// // try the non-regional version of a language, like
|
||||
// // 'en' if the language is 'en-US'
|
||||
// if (locale.indexOf('-') !== -1) {
|
||||
// var first = locale.split('-')[0];
|
||||
// localized = _find(data, function(d) {
|
||||
// return d.lang.toLowerCase() === first;
|
||||
// });
|
||||
// if (localized) return localized;
|
||||
// }
|
||||
|
||||
// // finally fall back to english
|
||||
// return _find(data, function(d) {
|
||||
// return d.lang.toLowerCase() === 'en';
|
||||
// });
|
||||
// }
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function load(param) {
|
||||
@@ -57,33 +61,34 @@ export function uiTagReference(tag) {
|
||||
_button
|
||||
.classed('tag-reference-loading', true);
|
||||
|
||||
wikibase.docs(param, function show(err, data) {
|
||||
wikibase.getEntity(param, function show(err, data) {
|
||||
var docs;
|
||||
if (!err && data) {
|
||||
// docs = findLocal(data);
|
||||
debugger;
|
||||
docs = findLocal(data);
|
||||
}
|
||||
|
||||
_body.html('');
|
||||
|
||||
if (!docs || !docs.title) {
|
||||
if (param.hasOwnProperty('value')) {
|
||||
load(_omit(param, 'value')); // retry with key only
|
||||
} else {
|
||||
_body
|
||||
.append('p')
|
||||
.attr('class', 'tag-reference-description')
|
||||
.text(t('inspector.no_documentation_key'));
|
||||
done();
|
||||
}
|
||||
_body
|
||||
.append('p')
|
||||
.attr('class', 'tag-reference-description')
|
||||
.text(t('inspector.no_documentation_key'));
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (docs.image && docs.image.thumb_url_prefix) {
|
||||
if (docs.image) {
|
||||
var imageUrl = docs.image.url + '?' + utilQsString({
|
||||
title: docs.image.title,
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
|
||||
_body
|
||||
.append('img')
|
||||
.attr('class', 'tag-reference-wiki-image')
|
||||
.attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
|
||||
.attr('src', imageUrl)
|
||||
.on('load', function() { done(); })
|
||||
.on('error', function() { d3_select(this).remove(); done(); });
|
||||
} else {
|
||||
@@ -93,7 +98,7 @@ export function uiTagReference(tag) {
|
||||
_body
|
||||
.append('p')
|
||||
.attr('class', 'tag-reference-description')
|
||||
.text(docs.description || t('inspector.documentation_redirect'));
|
||||
.text(docs.description || t('inspector.no_documentation_key'));
|
||||
|
||||
_body
|
||||
.append('a')
|
||||
@@ -173,6 +178,7 @@ export function uiTagReference(tag) {
|
||||
} else if (_loaded) {
|
||||
done();
|
||||
} else {
|
||||
tag.langCode = utilDetect().locale.toLowerCase();
|
||||
load(tag);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user