Skip to content

Commit b15f4ea

Browse files
authored
Add function to map localized language names to ISO codes (zotero#16)
1 parent 86afa51 commit b15f4ea

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

test/tests/utilities_itemTest.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,38 @@ describe("Zotero.Utilities.Item", function () {
236236
assert.equal(accessed['date-parts'][0][1], 1);
237237
assert.equal(accessed['date-parts'][0][2], 9);
238238
});
239+
240+
it("should convert localized language names to ISO 639-1", function () {
241+
let item = newItem('journalArticle');
242+
243+
item.language = 'French';
244+
let language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
245+
assert.equal(language, 'fr');
246+
247+
item.language = 'francais'; // Diacritics are ignored
248+
language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
249+
assert.equal(language, 'fr');
250+
251+
item.language = 'foobar';
252+
language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
253+
assert.equal(language, 'foobar');
254+
255+
item.language = 'zh-Hans';
256+
language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
257+
assert.equal(language, 'zh-Hans');
258+
259+
item.language = 'العربية';
260+
language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
261+
assert.equal(language, 'ar');
262+
263+
// If Intl is unavailable, should return the input value
264+
let Intl = globalThis.Intl;
265+
globalThis.Intl = undefined;
266+
item.language = 'French';
267+
language = Zotero.Utilities.Item.itemToCSLJSON(item).language;
268+
assert.equal(language, 'French');
269+
globalThis.Intl = Intl;
270+
});
239271
});
240272

241273

utilities_item.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,55 @@ var Utilities_Item = {
722722
});
723723
},
724724

725+
/**
726+
* Map a user-provided language name to an ISO 639-1 language code.
727+
* Language names are matched against languages' English names and native
728+
* names. Case and diacritics are ignored.
729+
*
730+
* @param {String} language
731+
* @return {String}
732+
*/
733+
languageToISO6391: function (language) {
734+
if (!language) {
735+
return '';
736+
}
737+
738+
if (!globalThis.Intl || !globalThis.Intl.DisplayNames) {
739+
Zotero.debug('Intl.DisplayNames not available: returning language as-is');
740+
return language;
741+
}
742+
743+
let normalize = s => s.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
744+
745+
let languageMap = Utilities_Item._languageMap;
746+
if (!languageMap) {
747+
languageMap = Utilities_Item._languageMap = new Map();
748+
749+
let allLocales = ["ab", "aa", "af", "ak", "sq", "am", "ar", "an", "hy", "as", "av", "ae", "ay", "az", "bm", "ba", "eu", "be", "bn", "bi", "bs", "br", "bg", "my", "ca", "ch", "ce", "ny", "zh", "cu", "cv", "kw", "co", "cr", "hr", "cs", "da", "dv", "nl", "dz", "en", "eo", "et", "ee", "fo", "fj", "fi", "fr", "fy", "ff", "gd", "gl", "lg", "ka", "de", "el", "kl", "gn", "gu", "ht", "ha", "he", "hz", "hi", "ho", "hu", "is", "io", "ig", "id", "ia", "ie", "iu", "ik", "ga", "it", "ja", "jv", "kn", "kr", "ks", "kk", "km", "ki", "rw", "ky", "kv", "kg", "ko", "kj", "ku", "lo", "la", "lv", "li", "ln", "lt", "lu", "lb", "mk", "mg", "ms", "ml", "mt", "gv", "mi", "mr", "mh", "mn", "na", "nv", "nd", "nr", "ng", "ne", "no", "nb", "nn", "ii", "oc", "oj", "or", "om", "os", "pi", "ps", "fa", "pl", "pt", "pa", "qu", "ro", "rm", "rn", "ru", "se", "sm", "sg", "sa", "sc", "sr", "sn", "sd", "si", "sk", "sl", "so", "st", "es", "su", "sw", "ss", "sv", "tl", "ty", "tg", "ta", "tt", "te", "th", "bo", "ti", "to", "ts", "tn", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "ve", "vi", "vo", "wa", "cy", "wo", "xh", "yi", "yo", "za", "zu"];
750+
let englishLanguageNames = new Intl.DisplayNames('en', { type: 'language' });
751+
let userLanguageNames = new Intl.DisplayNames(Zotero.locale, { type: 'language' });
752+
for (let locale of Intl.DisplayNames.supportedLocalesOf(allLocales)) {
753+
let inEnglish = englishLanguageNames.of(locale);
754+
if (inEnglish) {
755+
languageMap.set(normalize(inEnglish), locale);
756+
}
757+
758+
let inUser = userLanguageNames.of(locale);
759+
if (inUser) {
760+
languageMap.set(normalize(inUser), locale);
761+
}
762+
763+
let selfLanguageNames = new Intl.DisplayNames(locale, { type: 'language' });
764+
let inSelf = selfLanguageNames.of(locale);
765+
if (inSelf) {
766+
languageMap.set(normalize(inSelf), locale);
767+
}
768+
}
769+
}
770+
771+
return languageMap.get(normalize(language)) || language;
772+
},
773+
725774
/**
726775
* Converts an item from toArray() format to an array of items in
727776
* the content=json format used by the server

0 commit comments

Comments
 (0)