tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 49a892dd01e58b8039e9815b861629a6afdb1794
parent 158e32b20064646fbe0fba556968c8e061520cb9
Author: Eemeli Aro <eemeli@mozilla.com>
Date:   Thu, 30 Oct 2025 10:57:47 +0000

Bug 1760013 - Inline plural rule selection into devtools/shared/plural-form.js. r=devtools-reviewers,nchevobbe

The messages depending on this should really be migrated to a better format, but this change at least reduces the burden on translators a little bit.

Differential Revision: https://phabricator.services.mozilla.com/D267415

Diffstat:
Mdevtools/client/shared/test-helpers/jest-fixtures/Services.js | 3+++
Mdevtools/shared/plural-form.js | 246++++++++++++++++++++++++++++++++-----------------------------------------------
Mdevtools/shared/tests/xpcshell/test_pluralForm-english.js | 4+---
Adevtools/shared/tests/xpcshell/test_pluralForm-irish.js | 42++++++++++++++++++++++++++++++++++++++++++
Ddevtools/shared/tests/xpcshell/test_pluralForm-makeGetter.js | 38--------------------------------------
Mdevtools/shared/tests/xpcshell/xpcshell.toml | 2+-
Mtoolkit/locales/en-US/chrome/global/intl.properties | 6------
7 files changed, 145 insertions(+), 196 deletions(-)

diff --git a/devtools/client/shared/test-helpers/jest-fixtures/Services.js b/devtools/client/shared/test-helpers/jest-fixtures/Services.js @@ -539,6 +539,9 @@ const Services = { }, appinfo: "", + locale: { + appLocalesAsLangTags: ["en-US", "en"], + }, obs: { addObserver: () => {} }, strings: { createBundle() { diff --git a/devtools/shared/plural-form.js b/devtools/shared/plural-form.js @@ -2,140 +2,24 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* - * The code below is mostly is a slight modification of the now removed - * intl/locale/PluralForm.jsm that removes dependencies on chrome privileged - * APIs. To make maintenance easier, this file is kept as close as possible to - * the original in terms of implementation. The modified methods here are - * - makeGetter (remove code adding the caller name to the log) - * - get ruleNum() (rely on LocalizationHelper instead of String.services) - * - log() (rely on console.log) - * - * Disable eslint warnings to preserve original code style. - */ - +// Disable eslint warnings to preserve original code style. /* eslint-disable */ /** * This module provides the PluralForm object which contains a method to figure * out which plural form of a word to use for a given number based on the - * current localization. There is also a makeGetter method that creates a get - * function for the desired plural rule. This is useful for extensions that - * specify their own plural rule instead of relying on the browser default. - * (I.e., the extension hasn't been localized to the browser's locale.) - * - * See: http://developer.mozilla.org/en/docs/Localization_and_Plurals - * - * List of methods: - * - * string pluralForm - * get(int aNum, string aWords) - * - * int numForms - * numForms() - * - * [string pluralForm get(int aNum, string aWords), int numForms numForms()] - * makeGetter(int aRuleNum) - * Note: Basically, makeGetter returns 2 functions that do "get" and "numForm" + * current localization. */ -const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); -const L10N = new LocalizationHelper("toolkit/locales/intl.properties"); - -// These are the available plural functions that give the appropriate index -// based on the plural rule number specified. The first element is the number -// of plural forms and the second is the function to figure out the index. -const gFunctions = [ - // 0: Chinese - [1, (n) => 0], - // 1: English - [2, (n) => n!=1?1:0], - // 2: French - [2, (n) => n>1?1:0], - // 3: Latvian - [3, (n) => n%10==1&&n%100!=11?1:n%10==0?0:2], - // 4: Scottish Gaelic - [4, (n) => n==1||n==11?0:n==2||n==12?1:n>0&&n<20?2:3], - // 5: Romanian - [3, (n) => n==1?0:n==0||n%100>0&&n%100<20?1:2], - // 6: Lithuanian - [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?2:1], - // 7: Russian - [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2], - // 8: Slovak - [3, (n) => n==1?0:n>=2&&n<=4?1:2], - // 9: Polish - [3, (n) => n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2], - // 10: Slovenian - [4, (n) => n%100==1?0:n%100==2?1:n%100==3||n%100==4?2:3], - // 11: Irish Gaeilge - [5, (n) => n==1?0:n==2?1:n>=3&&n<=6?2:n>=7&&n<=10?3:4], - // 12: Arabic - [6, (n) => n==0?5:n==1?0:n==2?1:n%100>=3&&n%100<=10?2:n%100>=11&&n%100<=99?3:4], - // 13: Maltese - [4, (n) => n==1?0:n==0||n%100>0&&n%100<=10?1:n%100>10&&n%100<20?2:3], - // 14: Unused - [3, (n) => n%10==1?0:n%10==2?1:2], - // 15: Icelandic, Macedonian - [2, (n) => n%10==1&&n%100!=11?0:1], - // 16: Breton - [5, (n) => n%10==1&&n%100!=11&&n%100!=71&&n%100!=91?0:n%10==2&&n%100!=12&&n%100!=72&&n%100!=92?1:(n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99?2:n%1000000==0&&n!=0?3:4], - // 17: Shuar - [2, (n) => n!=0?1:0], - // 18: Welsh - [6, (n) => n==0?0:n==1?1:n==2?2:n==3?3:n==6?4:5], - // 19: Bosnian, Croatian, Serbian - [3, (n) => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2], -]; - const PluralForm = { - /** - * Get the correct plural form of a word based on the number - * - * @param aNum - * The number to decide which plural form to use - * @param aWords - * A semi-colon (;) separated string of words to pick the plural form - * @return The appropriate plural form of the word - */ - get get() + init() { - // This method will lazily load to avoid perf when it is first needed and - // creates getPluralForm function. The function it creates is based on the - // value of pluralRule specified in the intl stringbundle. - // See: http://developer.mozilla.org/en/docs/Localization_and_Plurals - - // Delete the getters to be overwritten delete this.numForms; delete this.get; - // Make the plural form get function and set it as the default get - [this.get, this.numForms] = this.makeGetter(this.ruleNum); - return this.get; - }, - - /** - * Create a pair of plural form functions for the given plural rule number. - * - * @param aRuleNum - * The plural rule number to create functions - * @return A pair: [function that gets the right plural form, - * function that returns the number of plural forms] - */ - makeGetter: function(aRuleNum) - { - // Default to "all plural" if the value is out of bounds or invalid - if (aRuleNum < 0 || aRuleNum >= gFunctions.length || isNaN(aRuleNum)) { - log(["Invalid rule number: ", aRuleNum, " -- defaulting to 0"]); - aRuleNum = 0; - } - - // Get the desired pluralRule function - let [numForms, pluralFunc] = gFunctions[aRuleNum]; - - // Return functions that give 1) the number of forms and 2) gets the right - // plural form - return [function(aNum, aWords) { + let [numForms, pluralFunc] = this.getPluralRule(); + this.numForms = () => numForms; + this.get = (aNum, aWords) => { // Figure out which index to use for the semi-colon separated words let index = pluralFunc(aNum ? Number(aNum) : 0); let words = aWords ? aWords.split(/;/) : [""]; @@ -145,16 +29,29 @@ const PluralForm = { // Check for array out of bounds or empty strings if ((ret == undefined) || (ret == "")) { - // Display a message in the error console - log(["Index #", index, " of '", aWords, "' for value ", aNum, - " is invalid -- plural rule #", aRuleNum, ";"]); + console.warn(`plural-form.js: Index #${index} of '${aWords}' for value ${aNum} is invalid;\n`); // Default to the first entry (which might be empty, but not undefined) ret = words[0]; } return ret; - }, () => numForms]; + }; + }, + + /** + * Get the correct plural form of a word based on the number + * + * @param aNum + * The number to decide which plural form to use + * @param aWords + * A semi-colon (;) separated string of words to pick the plural form + * @return The appropriate plural form of the word + */ + get get() + { + this.init(); + return this.get; }, /** @@ -164,39 +61,92 @@ const PluralForm = { */ get numForms() { - // We lazily load numForms, so trigger the init logic with get() - this.get(); + this.init(); return this.numForms; }, /** - * Get the plural rule number from the intl stringbundle + * Selects the number of plural categories and the function for selecting between them. + * + * The default is to use the same plural rules as English, which has "one" and "other" categories. + * This is only used for a small number of devtools messages that have a custom format; + * Fluent plurals in general rely on Unicode Common Locale Data Repository data. * - * @return The plural rule number + * @return The available plural function that gives the appropriate index + * based on the plural rule number specified. The first element is the number + * of plural forms and the second is the function to figure out the index. */ - get ruleNum() + getPluralRule() { - try { - return parseInt(L10N.getStr("pluralRule"), 10); - } catch (e) { - // Fallback to English if the pluralRule property is not available. - return 1; + const appLocales = Services.locale.appLocalesAsLangTags; + const locale = new Intl.Locale(appLocales[0]); + switch (locale.language) { + case "bo": + case "id": + case "ja": + case "km": + case "ko": + case "lo": + case "meh": + case "ms": + case "my": + case "th": + case "vi": + case "wo": + case "zh": + return [1, (n) => 0]; + case "bn": + case "fa": + case "fr": + case "gu": + case "hi": + case "oc": + case "pa": + return [2, (n) => n>1?1:0]; + case "ltg": + case "lv": + return [3, (n) => n%10==1&&n%100!=11?1:n%10==0?0:2]; + case "gd": + return [4, (n) => n==1||n==11?0:n==2||n==12?1:n>0&&n<20?2:3]; + case "ro": + return [3, (n) => n==1?0:n==0||n%100>0&&n%100<20?1:2]; + case "lt": + return [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?2:1]; + case "be": + case "ru": + case "uk": + return [3, (n) => n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2]; + case "cs": + case "sk": + return [3, (n) => n==1?0:n>=2&&n<=4?1:2]; + case "pl": + case "szl": + return [3, (n) => n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2]; + case "dsb": + case "hsb": + case "sl": + return [4, (n) => n%100==1?0:n%100==2?1:n%100==3||n%100==4?2:3]; + case "ga": + return [5, (n) => n==1?0:n==2?1:n>=3&&n<=6?2:n>=7&&n<=10?3:4]; + case "ar": + return [6, (n) => n==0?5:n==1?0:n==2?1:n%100>=3&&n%100<=10?2:n%100>=11&&n%100<=99?3:4]; + case "is": + case "mk": + return [4, (n) => n==1?0:n==0||n%100>0&&n%100<=10?1:n%100>10&&n%100<20?2:3]; + case "br": + return [5, (n) => n%10==1&&n%100!=11&&n%100!=71&&n%100!=91?0:n%10==2&&n%100!=12&&n%100!=72&&n%100!=92?1:(n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99?2:n%1000000==0&&n!=0?3:4]; + case "cy": + return [6, (n) => n==0?0:n==1?1:n==2?2:n==3?3:n==6?4:5]; + case "bs": + case "hr": + case "sr": + return [3, (n) => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2]; + default: + return [2, (n) => n!=1?1:0]; } } }; -/** - * Private helper function to log errors to the error console and command line - * - * @param aMsg - * Error message to log or an array of strings to concat - */ -function log(aMsg) -{ - let msg = "plural-form.js: " + (aMsg.join ? aMsg.join("") : aMsg); - console.log(msg + "\n"); -} - exports.PluralForm = PluralForm; exports.get = PluralForm.get; diff --git a/devtools/shared/tests/xpcshell/test_pluralForm-english.js b/devtools/shared/tests/xpcshell/test_pluralForm-english.js @@ -5,9 +5,7 @@ "use strict"; /** - * This unit test makes sure the plural form for Irish Gaeilge is working by - * using the makeGetter method instead of using the default language (by - * development), English. + * This unit test uses the default language (for development), English. */ const { PluralForm } = require("resource://devtools/shared/plural-form.js"); diff --git a/devtools/shared/tests/xpcshell/test_pluralForm-irish.js b/devtools/shared/tests/xpcshell/test_pluralForm-irish.js @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * This unit test makes sure the plural form for Irish Gaelic is working. + */ + +const { PluralForm } = require("resource://devtools/shared/plural-form.js"); + +function run_test() { + const origAvLocales = Services.locale.availableLocales; + registerCleanupFunction(() => { + Services.locale.availableLocales = origAvLocales; + }); + + Services.locale.availableLocales = ["ga-IE", "en-US"]; + Services.locale.requestedLocales = ["ga-IE"]; + PluralForm.init(); + + // Irish has 5 plural forms + Assert.equal(5, PluralForm.numForms()); + + // I don't really know Irish, so I'll stick in some dummy text + const words = "is 1;is 2;is 3-6;is 7-10;everything else"; + + const test = function (text, low, high) { + for (let num = low; num <= high; num++) { + Assert.equal(text, PluralForm.get(num, words)); + } + }; + + // Make sure for good inputs, things work as expected + test("everything else", 0, 0); + test("is 1", 1, 1); + test("is 2", 2, 2); + test("is 3-6", 3, 6); + test("is 7-10", 7, 10); + test("everything else", 11, 200); +} diff --git a/devtools/shared/tests/xpcshell/test_pluralForm-makeGetter.js b/devtools/shared/tests/xpcshell/test_pluralForm-makeGetter.js @@ -1,38 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -/** - * This unit test makes sure the plural form for Irish Gaeilge is working by - * using the makeGetter method instead of using the default language (by - * development), English. - */ - -const { PluralForm } = require("resource://devtools/shared/plural-form.js"); - -function run_test() { - // Irish is plural rule #11 - const [get, numForms] = PluralForm.makeGetter(11); - - // Irish has 5 plural forms - Assert.equal(5, numForms()); - - // I don't really know Irish, so I'll stick in some dummy text - const words = "is 1;is 2;is 3-6;is 7-10;everything else"; - - const test = function (text, low, high) { - for (let num = low; num <= high; num++) { - Assert.equal(text, get(num, words)); - } - }; - - // Make sure for good inputs, things work as expected - test("everything else", 0, 0); - test("is 1", 1, 1); - test("is 2", 2, 2); - test("is 3-6", 3, 6); - test("is 7-10", 7, 10); - test("everything else", 11, 200); -} diff --git a/devtools/shared/tests/xpcshell/xpcshell.toml b/devtools/shared/tests/xpcshell/xpcshell.toml @@ -59,7 +59,7 @@ skip-if = [ ["test_pluralForm-english.js"] -["test_pluralForm-makeGetter.js"] +["test_pluralForm-irish.js"] ["test_prettifyCSS.js"] skip-if = [ diff --git a/toolkit/locales/en-US/chrome/global/intl.properties b/toolkit/locales/en-US/chrome/global/intl.properties @@ -29,9 +29,3 @@ intl.accept_languages=en-US, en # Set it to the value of one of the menuitems in the "selectLangs" menulist in # http://searchfox.org/mozilla-central/source/browser/components/preferences/dialogs/fonts.xhtml font.language.group=x-western - -# LOCALIZATION NOTE (pluralRule): Pick the appropriate plural rule for your -# language. This will determine how many plural forms of a word you will need -# to provide and in what order. -# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals -pluralRule=1