Move getLocaleCollation and localeCompare to Zotero.Intl

This commit is contained in:
Sylvester Keil 2018-07-30 12:35:11 +02:00
parent 3a8d04f467
commit 4a97529b66
No known key found for this signature in database
GPG key ID: 878933BCEAB25A10
2 changed files with 158 additions and 139 deletions

View file

@ -24,6 +24,7 @@
*/
Zotero.Intl = new function () {
let bundle;
let collation;
let intlProps;
let pluralFormGet;
let pluralFormNumForms;
@ -65,29 +66,6 @@ Zotero.Intl = new function () {
};
function getIntlProp(name, fallback = null) {
try {
return intlProps.GetStringFromName(name);
}
catch (e) {
Zotero.logError(`Couldn't load ${name} from intl.properties`);
return fallback;
}
}
function setOrClearIntlPref(name, type) {
var val = getIntlProp(name);
if (val !== null) {
if (type == 'boolean') {
val = val == 'true';
}
Zotero.Prefs.set(name, val, true);
}
else {
Zotero.Prefs.clear(name, true);
}
}
/**
* @param {String} name
* @param {String[]} [params=[]] - Strings to substitute for placeholders
@ -132,4 +110,157 @@ Zotero.Intl = new function () {
}
return l10n;
};
/*
* Compares two strings based on the current collator.
* @param {String} string1
* @param {String} string2
* @return {Number} a number indicating how string1 and string2 compare to
* each other according to the sort order of this Collator object: a
* negative value if string1 comes before string2; a positive value if
* string1 comes after string2; 0 if they are considered equal.
*/
this.compare = function (...args) {
return this.collation.compareString(1, ...args);
};
Object.defineProperty(this, 'collation', {
get() {
if (collation == null) {
collation = getLocaleCollation();
}
return collation;
}
});
function getIntlProp(name, fallback = null) {
try {
return intlProps.GetStringFromName(name);
}
catch (e) {
Zotero.logError(`Couldn't load ${name} from intl.properties`);
return fallback;
}
}
function setOrClearIntlPref(name, type) {
var val = getIntlProp(name);
if (val !== null) {
if (type == 'boolean') {
val = val == 'true';
}
Zotero.Prefs.set(name, val, true);
}
else {
Zotero.Prefs.clear(name, true);
}
}
function getLocaleCollation() {
try {
// DEBUG: Is this necessary, or will Intl.Collator just default to the same locales we're
// passing manually?
let locales;
// Fx55+
if (Services.locale.getAppLocalesAsBCP47) {
locales = Services.locale.getAppLocalesAsBCP47();
}
else {
let locale;
// Fx54
if (Services.locale.getAppLocale) {
locale = Services.locale.getAppLocale();
}
// Fx <=53
else {
locale = Services.locale.getApplicationLocale();
locale = locale.getCategory('NSILOCALE_COLLATE');
}
// Extract a valid language tag
try {
locale = locale.match(/^[a-z]{2}(\-[A-Z]{2})?/)[0];
}
catch (e) {
throw new Error(`Error parsing locale ${locale}`);
}
locales = [locale];
}
var collator = new Intl.Collator(locales, {
numeric: true,
sensitivity: 'base'
});
}
catch (e) {
Zotero.logError(e);
// Fall back to en-US sorting
try {
Zotero.logError("Falling back to en-US sorting");
collator = new Intl.Collator(['en-US'], {
numeric: true,
sensitivity: 'base'
});
}
catch (e) {
Zotero.logError(e);
// If there's still an error, just skip sorting
collator = {
compare: function (a, b) {
return 0;
}
};
}
}
// Grab all ASCII punctuation and space at the begining of string
var initPunctuationRE = /^[\x20-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/;
// Punctuation that should be ignored when sorting
var ignoreInitRE = /["'[{(]+$/;
// Until old code is updated, pretend we're returning an nsICollation
return this.collation = {
compareString: function (_, a, b) {
if (!a && !b) return 0;
if (!a || !b) return b ? -1 : 1;
// Compare initial punctuation
var aInitP = initPunctuationRE.exec(a) || '';
var bInitP = initPunctuationRE.exec(b) || '';
var aWordStart = 0, bWordStart = 0;
if (aInitP) {
aWordStart = aInitP[0].length;
aInitP = aInitP[0].replace(ignoreInitRE, '');
}
if (bInitP) {
bWordStart = bInitP.length;
bInitP = bInitP[0].replace(ignoreInitRE, '');
}
// If initial punctuation is equivalent, use collator comparison
// that ignores all punctuation
//
// Update: Intl.Collator's ignorePunctuation also ignores whitespace, so we're
// no longer using it, meaning we could take out most of the code to handle
// initial punctuation separately, unless we think we'll at some point switch to
// a collation function that ignores punctuation but not whitespace.
if (aInitP == bInitP || !aInitP && !bInitP) return collator.compare(a, b);
// Otherwise consider "attached" words as well, e.g. the order should be
// "__ n", "__z", "_a"
// We don't actually care what the attached word is, just whether it's
// there, since at this point we're guaranteed to have non-equivalent
// initial punctuation
if (aWordStart < a.length) aInitP += 'a';
if (bWordStart < b.length) bInitP += 'a';
return aInitP.localeCompare(bInitP);
}
};
}
};

View file

@ -1412,122 +1412,10 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
this.getLocaleCollation = function () {
if (this.collation) {
return this.collation;
}
return Zotero.Intl.collation;
};
try {
// DEBUG: Is this necessary, or will Intl.Collator just default to the same locales we're
// passing manually?
let locales;
// Fx55+
if (Services.locale.getAppLocalesAsBCP47) {
locales = Services.locale.getAppLocalesAsBCP47();
}
else {
let locale;
// Fx54
if (Services.locale.getAppLocale) {
locale = Services.locale.getAppLocale();
}
// Fx <=53
else {
locale = Services.locale.getApplicationLocale();
locale = locale.getCategory('NSILOCALE_COLLATE');
}
// Extract a valid language tag
try {
locale = locale.match(/^[a-z]{2}(\-[A-Z]{2})?/)[0];
}
catch (e) {
throw new Error(`Error parsing locale ${locale}`);
}
locales = [locale];
}
var collator = new Intl.Collator(locales, {
numeric: true,
sensitivity: 'base'
});
}
catch (e) {
Zotero.logError(e);
// Fall back to en-US sorting
try {
Zotero.logError("Falling back to en-US sorting");
collator = new Intl.Collator(['en-US'], {
numeric: true,
sensitivity: 'base'
});
}
catch (e) {
Zotero.logError(e);
// If there's still an error, just skip sorting
collator = {
compare: function (a, b) {
return 0;
}
};
}
}
// Grab all ASCII punctuation and space at the begining of string
var initPunctuationRE = /^[\x20-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/;
// Punctuation that should be ignored when sorting
var ignoreInitRE = /["'[{(]+$/;
// Until old code is updated, pretend we're returning an nsICollation
return this.collation = {
compareString: function (_, a, b) {
if (!a && !b) return 0;
if (!a || !b) return b ? -1 : 1;
// Compare initial punctuation
var aInitP = initPunctuationRE.exec(a) || '';
var bInitP = initPunctuationRE.exec(b) || '';
var aWordStart = 0, bWordStart = 0;
if (aInitP) {
aWordStart = aInitP[0].length;
aInitP = aInitP[0].replace(ignoreInitRE, '');
}
if (bInitP) {
bWordStart = bInitP.length;
bInitP = bInitP[0].replace(ignoreInitRE, '');
}
// If initial punctuation is equivalent, use collator comparison
// that ignores all punctuation
//
// Update: Intl.Collator's ignorePunctuation also ignores whitespace, so we're
// no longer using it, meaning we could take out most of the code to handle
// initial punctuation separately, unless we think we'll at some point switch to
// a collation function that ignores punctuation but not whitespace.
if (aInitP == bInitP || !aInitP && !bInitP) return collator.compare(a, b);
// Otherwise consider "attached" words as well, e.g. the order should be
// "__ n", "__z", "_a"
// We don't actually care what the attached word is, just whether it's
// there, since at this point we're guaranteed to have non-equivalent
// initial punctuation
if (aWordStart < a.length) aInitP += 'a';
if (bWordStart < b.length) bInitP += 'a';
return aInitP.localeCompare(bInitP);
}
};
}
this.defineProperty(this, "localeCompare", {
get: function() {
var collation = this.getLocaleCollation();
return collation.compareString.bind(collation, 1);
}
}, {lazy: true});
this.localeCompare = Zotero.Intl.compare;
/*
* Sets font size based on prefs -- intended for use on root element