Move getLocaleCollation and localeCompare to Zotero.Intl
This commit is contained in:
parent
3a8d04f467
commit
4a97529b66
2 changed files with 158 additions and 139 deletions
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1412,122 +1412,10 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
|
|||
|
||||
|
||||
this.getLocaleCollation = function () {
|
||||
if (this.collation) {
|
||||
return this.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;
|
||||
}
|
||||
return Zotero.Intl.collation;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
Loading…
Add table
Reference in a new issue