intl/locale/src/PluralForm.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/locale/src/PluralForm.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,177 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +this.EXPORTED_SYMBOLS = [ "PluralForm" ];
     1.9 +
    1.10 +/**
    1.11 + * This module provides the PluralForm object which contains a method to figure
    1.12 + * out which plural form of a word to use for a given number based on the
    1.13 + * current localization. There is also a makeGetter method that creates a get
    1.14 + * function for the desired plural rule. This is useful for extensions that
    1.15 + * specify their own plural rule instead of relying on the browser default.
    1.16 + * (I.e., the extension hasn't been localized to the browser's locale.)
    1.17 + *
    1.18 + * See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
    1.19 + *
    1.20 + * List of methods:
    1.21 + *
    1.22 + * string pluralForm
    1.23 + * get(int aNum, string aWords)
    1.24 + *
    1.25 + * int numForms
    1.26 + * numForms()
    1.27 + *
    1.28 + * [string pluralForm get(int aNum, string aWords), int numForms numForms()]
    1.29 + * makeGetter(int aRuleNum)
    1.30 + * Note: Basically, makeGetter returns 2 functions that do "get" and "numForm"
    1.31 + */
    1.32 +
    1.33 +const Cc = Components.classes;
    1.34 +const Ci = Components.interfaces;
    1.35 +
    1.36 +const kIntlProperties = "chrome://global/locale/intl.properties";
    1.37 +
    1.38 +// These are the available plural functions that give the appropriate index
    1.39 +// based on the plural rule number specified. The first element is the number
    1.40 +// of plural forms and the second is the function to figure out the index.
    1.41 +let gFunctions = [
    1.42 +  // 0: Chinese
    1.43 +  [1, function(n) 0],
    1.44 +  // 1: English
    1.45 +  [2, function(n) n!=1?1:0],
    1.46 +  // 2: French
    1.47 +  [2, function(n) n>1?1:0],
    1.48 +  // 3: Latvian
    1.49 +  [3, function(n) n%10==1&&n%100!=11?1:n!=0?2:0],
    1.50 +  // 4: Scottish Gaelic
    1.51 +  [4, function(n) n==1||n==11?0:n==2||n==12?1:n>0&&n<20?2:3],
    1.52 +  // 5: Romanian
    1.53 +  [3, function(n) n==1?0:n==0||n%100>0&&n%100<20?1:2],
    1.54 +  // 6: Lithuanian
    1.55 +  [3, function(n) n%10==1&&n%100!=11?0:n%10>=2&&(n%100<10||n%100>=20)?2:1],
    1.56 +  // 7: Russian
    1.57 +  [3, function(n) n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2],
    1.58 +  // 8: Slovak
    1.59 +  [3, function(n) n==1?0:n>=2&&n<=4?1:2],
    1.60 +  // 9: Polish
    1.61 +  [3, function(n) n==1?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2],
    1.62 +  // 10: Slovenian
    1.63 +  [4, function(n) n%100==1?0:n%100==2?1:n%100==3||n%100==4?2:3],
    1.64 +  // 11: Irish Gaeilge
    1.65 +  [5, function(n) n==1?0:n==2?1:n>=3&&n<=6?2:n>=7&&n<=10?3:4],
    1.66 +  // 12: Arabic
    1.67 +  [6, function(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],
    1.68 +  // 13: Maltese
    1.69 +  [4, function(n) n==1?0:n==0||n%100>0&&n%100<=10?1:n%100>10&&n%100<20?2:3],
    1.70 +  // 14: Macedonian
    1.71 +  [3, function(n) n%10==1?0:n%10==2?1:2],
    1.72 +  // 15: Icelandic
    1.73 +  [2, function(n) n%10==1&&n%100!=11?0:1],
    1.74 +  // 16: Breton
    1.75 +  [5, function(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],
    1.76 +];
    1.77 +
    1.78 +this.PluralForm = {
    1.79 +  /**
    1.80 +   * Get the correct plural form of a word based on the number
    1.81 +   *
    1.82 +   * @param aNum
    1.83 +   *        The number to decide which plural form to use
    1.84 +   * @param aWords
    1.85 +   *        A semi-colon (;) separated string of words to pick the plural form
    1.86 +   * @return The appropriate plural form of the word
    1.87 +   */
    1.88 +  get get()
    1.89 +  {
    1.90 +    // This method will lazily load to avoid perf when it is first needed and
    1.91 +    // creates getPluralForm function. The function it creates is based on the
    1.92 +    // value of pluralRule specified in the intl stringbundle.
    1.93 +    // See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
    1.94 +
    1.95 +    // Delete the getters to be overwritten
    1.96 +    delete PluralForm.numForms;
    1.97 +    delete PluralForm.get;
    1.98 +
    1.99 +    // Get the plural rule number from the intl stringbundle
   1.100 +    let ruleNum = Number(Cc["@mozilla.org/intl/stringbundle;1"].
   1.101 +      getService(Ci.nsIStringBundleService).createBundle(kIntlProperties).
   1.102 +      GetStringFromName("pluralRule"));
   1.103 +
   1.104 +    // Make the plural form get function and set it as the default get
   1.105 +    [PluralForm.get, PluralForm.numForms] = PluralForm.makeGetter(ruleNum);
   1.106 +    return PluralForm.get;
   1.107 +  },
   1.108 +
   1.109 +  /**
   1.110 +   * Create a pair of plural form functions for the given plural rule number.
   1.111 +   *
   1.112 +   * @param aRuleNum
   1.113 +   *        The plural rule number to create functions
   1.114 +   * @return A pair: [function that gets the right plural form,
   1.115 +   *                  function that returns the number of plural forms]
   1.116 +   */
   1.117 +  makeGetter: function(aRuleNum)
   1.118 +  {
   1.119 +    // Default to "all plural" if the value is out of bounds or invalid
   1.120 +    if (aRuleNum < 0 || aRuleNum >= gFunctions.length || isNaN(aRuleNum)) {
   1.121 +      log(["Invalid rule number: ", aRuleNum, " -- defaulting to 0"]);
   1.122 +      aRuleNum = 0;
   1.123 +    }
   1.124 +
   1.125 +    // Get the desired pluralRule function
   1.126 +    let [numForms, pluralFunc] = gFunctions[aRuleNum];
   1.127 +
   1.128 +    // Return functions that give 1) the number of forms and 2) gets the right
   1.129 +    // plural form
   1.130 +    return [function(aNum, aWords) {
   1.131 +      // Figure out which index to use for the semi-colon separated words
   1.132 +      let index = pluralFunc(aNum ? Number(aNum) : 0);
   1.133 +      let words = aWords ? aWords.split(/;/) : [""];
   1.134 +
   1.135 +      // Explicitly check bounds to avoid strict warnings
   1.136 +      let ret = index < words.length ? words[index] : undefined;
   1.137 +
   1.138 +      // Check for array out of bounds or empty strings
   1.139 +      if ((ret == undefined) || (ret == "")) {
   1.140 +        // Report the caller to help figure out who is causing badness
   1.141 +        let caller = PluralForm.get.caller ? PluralForm.get.caller.name : "top";
   1.142 +
   1.143 +        // Display a message in the error console
   1.144 +        log(["Index #", index, " of '", aWords, "' for value ", aNum,
   1.145 +            " is invalid -- plural rule #", aRuleNum, "; called by ", caller]);
   1.146 +
   1.147 +        // Default to the first entry (which might be empty, but not undefined)
   1.148 +        ret = words[0];
   1.149 +      }
   1.150 +
   1.151 +      return ret;
   1.152 +    }, function() numForms];
   1.153 +  },
   1.154 +
   1.155 +  /**
   1.156 +   * Get the number of forms for the current plural rule
   1.157 +   *
   1.158 +   * @return The number of forms
   1.159 +   */
   1.160 +  get numForms()
   1.161 +  {
   1.162 +    // We lazily load numForms, so trigger the init logic with get()
   1.163 +    PluralForm.get();
   1.164 +    return PluralForm.numForms;
   1.165 +  },
   1.166 +};
   1.167 +
   1.168 +/**
   1.169 + * Private helper function to log errors to the error console and command line
   1.170 + *
   1.171 + * @param aMsg
   1.172 + *        Error message to log or an array of strings to concat
   1.173 + */
   1.174 +function log(aMsg)
   1.175 +{
   1.176 +  let msg = "PluralForm.jsm: " + (aMsg.join ? aMsg.join("") : aMsg);
   1.177 +  Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
   1.178 +    logStringMessage(msg);
   1.179 +  dump(msg + "\n");
   1.180 +}

mercurial