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 +}