diff --git a/chrome/content/zotero/xpcom/intl.js b/chrome/content/zotero/xpcom/intl.js new file mode 100644 index 0000000000..16e750558a --- /dev/null +++ b/chrome/content/zotero/xpcom/intl.js @@ -0,0 +1,133 @@ +/* + ***** BEGIN LICENSE BLOCK ***** + + Copyright © 2018 Center for History and New Media + George Mason University, Fairfax, Virginia, USA + http://zotero.org + + This file is part of Zotero. + + Zotero is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Zotero is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Zotero. If not, see . + + ***** END LICENSE BLOCK ***** +*/ +Zotero.intl = new function () { + let bundle; + let intlProps; + let pluralFormGet; + let pluralFormNumForms; + + // Get settings from language pack (extracted by zotero-build/locale/merge_mozilla_files) + this.init = function () { + bundle = Services.strings.createBundle('chrome://zotero/locale/zotero.properties'); + intlProps = Services.strings.createBundle('chrome://zotero/locale/mozilla/intl.properties'); + + [pluralFormGet, pluralFormNumForms] = PluralForm.makeGetter(parseInt(getIntlProp('pluralRule', 1))); + setOrClearIntlPref('intl.accept_languages', 'string'); + + Zotero.locale = getIntlProp('general.useragent.locale', 'en-US'); + + // Also load the brand as appName + Zotero.appName = Services.strings + .createBundle('chrome://branding/locale/brand.properties') + .GetStringFromName('brandShortName'); + + // Set the locale direction to Zotero.dir + Zotero.dir = 'ltr'; + + // TODO: is there a better way to get the entity from JS? + if (!(Zotero.isNode || Zotero.isElectron)) { + let xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + xmlhttp.open('GET', 'chrome://global/locale/global.dtd', false); + xmlhttp.overrideMimeType('text/plain'); + xmlhttp.send(null); + let matches = xmlhttp.responseText.match(/(ltr|rtl)/); + if (matches && matches[0] == 'rtl') { + Zotero.dir = 'rtl'; + } + } + + Zotero.rtl = (Zotero.dir === 'rtl'); + }; + + + 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 + * @param {Number} [num] - Number (also appearing in `params`) to use when determining which plural + * form of the string to use; localized strings should include all forms in the order specified + * in https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals, + * separated by semicolons + */ + this.getString = function (name, params, num) { + try { + var l10n; + if (params != undefined) { + if (typeof params != 'object'){ + params = [params]; + } + l10n = bundle.formatStringFromName(name, params, params.length); + } + else { + l10n = bundle.GetStringFromName(name); + } + if (num !== undefined) { + let availableForms = l10n.split(/;/); + // If not enough available forms, use last one -- PluralForm.get() uses first by + // default, but it's more likely that a localizer will translate the two English + // strings with some plural form as the second one, so we might as well use that + if (availableForms.length < pluralFormNumForms()) { + l10n = availableForms[availableForms.length - 1]; + } + else { + l10n = pluralFormGet(num, l10n); + } + } + } + catch (e){ + if (e.name == 'NS_ERROR_ILLEGAL_VALUE') { + Zotero.debug(params, 1); + } + else if (e.name != 'NS_ERROR_FAILURE') { + Zotero.logError(e); + } + throw new Error('Localized string not available for ' + name); + } + return l10n; + }; +}; diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index ca31cf1cd9..d608da844d 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -225,59 +225,8 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js"); // Browser Zotero.browser = "g"; - // - // Get settings from language pack (extracted by zotero-build/locale/merge_mozilla_files) - // - 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, 1); - } - else { - Zotero.Prefs.clear(name, 1); - } - } - var intlProps = Services.strings.createBundle("chrome://zotero/locale/mozilla/intl.properties"); - this.locale = getIntlProp('general.useragent.locale', 'en-US'); - let [get, numForms] = PluralForm.makeGetter(parseInt(getIntlProp('pluralRule', 1))); - this.pluralFormGet = get; - this.pluralFormNumForms = numForms; - setOrClearIntlPref('intl.accept_languages', 'string'); - - // Also load the brand as appName - var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); - this.appName = brandBundle.GetStringFromName("brandShortName"); - - _localizedStringBundle = Services.strings.createBundle("chrome://zotero/locale/zotero.properties"); - - // Set the locale direction to Zotero.dir - // DEBUG: is there a better way to get the entity from JS? - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - xmlhttp.open('GET', 'chrome://global/locale/global.dtd', false); - xmlhttp.overrideMimeType('text/plain'); - xmlhttp.send(null); - var matches = xmlhttp.responseText.match(/(ltr|rtl)/); - if (matches && matches[0] == 'rtl') { - Zotero.dir = 'rtl'; - } - else { - Zotero.dir = 'ltr'; - } - Zotero.rtl = Zotero.dir == 'rtl'; - + Zotero.intl.init(); + Zotero.Prefs.init(); Zotero.Debug.init(options && options.forceDebugLog); @@ -1412,47 +1361,9 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js"); * separated by semicolons */ this.getString = function (name, params, num) { - return this.getStringFromBundle(_localizedStringBundle, ...arguments); + return Zotero.intl.getString(...arguments); } - - this.getStringFromBundle = function (bundle, name, params, num) { - try { - if (params != undefined) { - if (typeof params != 'object'){ - params = [params]; - } - var l10n = bundle.formatStringFromName(name, params, params.length); - } - else { - var l10n = bundle.GetStringFromName(name); - } - if (num !== undefined) { - let availableForms = l10n.split(/;/); - // If not enough available forms, use last one -- PluralForm.get() uses first by - // default, but it's more likely that a localizer will translate the two English - // strings with some plural form as the second one, so we might as well use that - if (availableForms.length < this.pluralFormNumForms()) { - l10n = availableForms[availableForms.length - 1]; - } - else { - l10n = this.pluralFormGet(num, l10n); - } - } - } - catch (e){ - if (e.name == 'NS_ERROR_ILLEGAL_VALUE') { - Zotero.debug(params, 1); - } - else if (e.name != 'NS_ERROR_FAILURE') { - Zotero.logError(e); - } - throw new Error('Localized string not available for ' + name); - } - return l10n; - } - - /** * Defines property on the object * More compact way to do Object.defineProperty diff --git a/components/zotero-service.js b/components/zotero-service.js index bf9a3da8b7..b19cb89c01 100644 --- a/components/zotero-service.js +++ b/components/zotero-service.js @@ -33,6 +33,7 @@ const Ci = Components.interfaces; /** XPCOM files to be loaded for all modes **/ const xpcomFilesAll = [ 'zotero', + 'intl', 'prefs', 'dataDirectory', 'date',