diff --git a/chrome/content/zotero/elements/styleConfigurator.js b/chrome/content/zotero/elements/styleConfigurator.js index 69cefc5bda..8269453b67 100644 --- a/chrome/content/zotero/elements/styleConfigurator.js +++ b/chrome/content/zotero/elements/styleConfigurator.js @@ -144,9 +144,12 @@ val = ''; } + val = Zotero.Styles.resolveLocale(val); this._value = val; const styleData = this._style ? Zotero.Styles.get(this._style) : null; - this.localeListEl.value = styleData && styleData.effectiveLocale || this._value; + this.localeListEl.value = Zotero.Styles.resolveLocale( + styleData && styleData.effectiveLocale || this._value + ); } get style() { @@ -157,7 +160,9 @@ this._style = style; const styleData = style ? Zotero.Styles.get(style) : null; this.localeListEl.disabled = !style || !!styleData.effectiveLocale; - this.localeListEl.value = styleData && styleData.effectiveLocale || this._value || this.fallbackLocale; + this.localeListEl.value = Zotero.Styles.resolveLocale( + styleData && styleData.effectiveLocale || this._value || this.fallbackLocale + ); } connectedCallback() { @@ -171,7 +176,7 @@ this._value = this.getAttribute('value'); await Zotero.Styles.init(); - this.fallbackLocale = Zotero.Styles?.primaryDialects[Zotero.locale] || Zotero.locale; + this.fallbackLocale = Zotero.Styles.resolveLocale(Zotero.locale); const menuLocales = Zotero.Utilities.deepCopy(Zotero.Styles.locales); const menuLocalesKeys = Object.keys(menuLocales).sort(); diff --git a/chrome/content/zotero/xpcom/cite.js b/chrome/content/zotero/xpcom/cite.js index d74ab3df59..ef68dd30ee 100644 --- a/chrome/content/zotero/xpcom/cite.js +++ b/chrome/content/zotero/xpcom/cite.js @@ -695,7 +695,16 @@ Zotero.Cite.System.prototype = { lang = 'sr-Latn-RS'; break; } - return Zotero.Cite.Locale.get(lang); + var xml = Zotero.Cite.Locale.get(lang); + if (!xml) { + // Use the closest available locale (e.g., 'sr-Cyrl-RS' for 'sr-RS', + // which doesn't have its own CSL locale) + let fallback = Zotero.Styles.resolveLocale(lang); + if (fallback && fallback != lang) { + xml = Zotero.Cite.Locale.get(fallback); + } + } + return xml; } }; diff --git a/chrome/content/zotero/xpcom/style.js b/chrome/content/zotero/xpcom/style.js index 83e9a53407..1f04b69a8f 100644 --- a/chrome/content/zotero/xpcom/style.js +++ b/chrome/content/zotero/xpcom/style.js @@ -532,9 +532,31 @@ Zotero.Styles = new function () { }; }; + /** + * Resolve a locale to one with an available CSL locale, using the primary + * dialect of the language or the closest available locale (e.g., + * 'sr-Cyrl-RS' for 'sr-RS') if the exact locale isn't available + * + * @param {String} locale + * @return {String} The resolved locale, or the passed locale if it can't + * be resolved + */ + this.resolveLocale = function (locale) { + if (!_initialized || !locale) { + return locale; + } + if (locale in Zotero.Styles.locales) { + return locale; + } + return Zotero.Styles.primaryDialects[locale] + || Zotero.Utilities.Internal.resolveLocale( + locale, Object.keys(Zotero.Styles.locales), { silent: true }) + || locale; + }; + /** * Populate menulist with locales - * + * * @param {xul:menulist} menulist */ this.populateLocaleList = function (menulist) { @@ -546,8 +568,7 @@ Zotero.Styles = new function () { menulist.selectedItem = null; menulist.removeAllItems(); - let fallbackLocale = Zotero.Styles.primaryDialects[Zotero.locale] - || Zotero.locale; + let fallbackLocale = Zotero.Styles.resolveLocale(Zotero.locale); let menuLocales = Zotero.Utilities.deepCopy(Zotero.Styles.locales); let menuLocalesKeys = Object.keys(menuLocales).sort(); @@ -618,7 +639,7 @@ Zotero.Styles = new function () { if (menulist.labelElement) menulist.labelElement.disabled = false; let selectLocale = style.effectiveLocale || prefLocale || Zotero.locale; - selectLocale = Zotero.Styles.primaryDialects[selectLocale] || selectLocale; + selectLocale = Zotero.Styles.resolveLocale(selectLocale); // Make sure the locale we want to select is in the menulist if (availableLocales.indexOf(selectLocale) == -1) { diff --git a/test/tests/citeTest.js b/test/tests/citeTest.js index 6c169542a7..0224a697e4 100644 --- a/test/tests/citeTest.js +++ b/test/tests/citeTest.js @@ -41,6 +41,22 @@ describe("Zotero.Cite", function () { var output = Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, [item], "text"); assert.include(output, 'izd'); }); + + it("should use closest available locale for locale without a CSL locale", async function () { + var item = new Zotero.Item; + item.fromJSON({ + itemType: "book", + title: "Test Book", + edition: "2" + }); + await item.saveTx(); + + var style = Zotero.Styles.get('http://www.zotero.org/styles/chicago-notes-bibliography'); + var cslEngine = style.getCiteProc('sr-RS'); + + var output = Zotero.Cite.makeFormattedBibliographyOrCitationList(cslEngine, [item], "text"); + assert.include(output, 'изд'); + }); }); describe("#extraToCSL()", function () { diff --git a/test/tests/styleTest.js b/test/tests/styleTest.js index 3bcdc1d425..1e86b930fa 100644 --- a/test/tests/styleTest.js +++ b/test/tests/styleTest.js @@ -128,6 +128,20 @@ describe("Zotero.Styles", function () { }); }); + describe("Zotero.Styles.resolveLocale", function () { + it("should return available locale unchanged", function () { + assert.equal(Zotero.Styles.resolveLocale('de-AT'), 'de-AT'); + }); + + it("should return primary dialect for a language code", function () { + assert.equal(Zotero.Styles.resolveLocale('fa'), 'fa-IR'); + }); + + it("should return closest available locale for locale without a CSL locale", function () { + assert.equal(Zotero.Styles.resolveLocale('sr-RS'), 'sr-Cyrl-RS'); + }); + }); + describe("Cached CSL.Engine instances", function () { if (Zotero.Prefs.get('cite.useCiteprocRs')) { this.skip();