UNPKG

@phensley/cldr-core

Version:
149 lines 5.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var languagetag_1 = require("./languagetag"); var distance_1 = require("./distance"); var parser_1 = require("./parser"); var resolver_1 = require("./resolver"); var autogen_distance_1 = require("./autogen.distance"); // Space and comma-separated bundle ids. var TAG_SEP = /[,\s]+/g; var numberCmp = function (a, b) { return a === b ? 0 : a < b ? -1 : 1; }; var UNDEFINED = new languagetag_1.LanguageTag(); var paradigmLocaleMap = autogen_distance_1.paradigmLocales.reduce(function (o, k, i) { var compact = resolver_1.LanguageResolver.resolve(k).compact(); o[compact] = i; return o; }, {}); var Entry = /** @class */ (function () { function Entry(id, tag) { this.id = id; this.tag = tag; this.compact = tag.compact(); } return Entry; }()); /** * Flatten and split the string or array into a list of matcher entries. */ var parse = function (locales) { var raw; if (typeof locales === 'string') { raw = locales.split(TAG_SEP); } else { raw = locales.reduce(function (a, e) { var tmp = e.split(TAG_SEP); return a.concat(tmp); }, []); } var result = []; var len = raw.length; for (var i = 0; i < len; i++) { var id = raw[i].trim(); var tag = parser_1.parseLanguageTag(id); // Preserve 'und' undefined locale. If we resolve it, adding // likely subtags will expand it to 'en-Latn-US'. if (tag.hasLanguage() || tag.hasScript() || tag.hasRegion()) { result.push(new Entry(id, resolver_1.LanguageResolver.resolve(tag))); } else { result.push(new Entry(id, UNDEFINED)); } } return result; }; /** * Given a list of supported locales, and a list of a user's desired locales * (sorted in the order of preference, descending), returns the supported * locale closest to the user preference. The first locale in the list will * be used as the default. The default will be selected if no match is within * the distance threshold. * * Implementation of CLDR enhanced language matching: * http://www.unicode.org/reports/tr35/tr35.html#EnhancedLanguageMatching * * @alpha */ var LocaleMatcher = /** @class */ (function () { function LocaleMatcher(supportedLocales) { var _this = this; this.exactMap = {}; this.supported = parse(supportedLocales); this.count = this.supported.length; // The first locale in the list is used as the default. this.default = this.supported[0]; this.supported.sort(function (a, b) { // Keep default tag at the front. if (a.tag === _this.default.tag) { return -1; } if (b.tag === _this.default.tag) { return 1; } // Sort all paradigm locales before non-paradigms. var pa = paradigmLocaleMap[a.compact]; var pb = paradigmLocaleMap[b.compact]; if (pa !== undefined) { return pb === undefined ? -1 : numberCmp(pa, pb); } else if (pb !== undefined) { return 1; } // All other locales stay in their relative positions. return 0; }); // Wire up a map for quick lookups of exact matches. These have a // distance of 0 and will short-circuit the matching loop. this.supported.forEach(function (locale) { var key = locale.compact; var bundles = _this.exactMap[key]; if (bundles === undefined) { bundles = [locale]; _this.exactMap[key] = bundles; } else { bundles.push(locale); } }); } /** * Find the desired locale that is the closed match to a supported locale, within * the given threshold. Any matches whose distance is greater than or equal to the * threshold will be treated as having maximum distance. */ LocaleMatcher.prototype.match = function (desiredLocales, threshold) { if (threshold === void 0) { threshold = distance_1.DEFAULT_THRESHOLD; } var desireds = parse(desiredLocales); var len = desireds.length; var bestDistance = distance_1.MAX_DISTANCE; var bestMatch = undefined; var bestDesired = len === 0 ? this.default : desireds[0]; for (var i = 0; i < len; i++) { var desired = desireds[i]; var exact = this.exactMap[desired.compact]; if (exact !== undefined) { return { locale: { id: exact[0].id, tag: exact[0].tag }, distance: 0 }; } for (var j = 0; j < this.count; j++) { var supported = this.supported[j]; var distance = distance_1.getDistance(desired.tag, supported.tag, threshold); if (bestDistance === undefined || distance < bestDistance) { bestDistance = distance; bestMatch = supported; bestDesired = desired; } } } var extensions = bestDesired.tag.extensions(); var privateUse = bestDesired.tag.privateUse(); var _a = bestMatch === undefined ? this.default : bestMatch, id = _a.id, tag = _a.tag; var result = new languagetag_1.LanguageTag(tag.language(), tag.script(), tag.region(), tag.variant(), extensions, privateUse); return { locale: { id: id, tag: result }, distance: bestMatch === undefined ? distance_1.MAX_DISTANCE : bestDistance }; }; return LocaleMatcher; }()); exports.LocaleMatcher = LocaleMatcher; //# sourceMappingURL=matcher.js.map