@phensley/cldr-core
Version:
Core library for @phensley/cldr
149 lines • 5.83 kB
JavaScript
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
;