1 /* 2 * LocaleInfo.js - Encode locale-specific defaults 3 * 4 * Copyright © 2012-2015, 2018, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 // !data localeinfo 21 22 var ilib = require("./ilib.js"); 23 var Utils = require("./Utils.js"); 24 var Locale = require("./Locale.js"); 25 26 /** 27 * @class 28 * Create a new locale info instance. Locale info instances give information about 29 * the default settings for a particular locale. These settings may be overridden 30 * by various parts of the code, and should be used as a fall-back setting of last 31 * resort. <p> 32 * 33 * The optional options object holds extra parameters if they are necessary. The 34 * current list of supported options are: 35 * 36 * <ul> 37 * <li><i>onLoad</i> - a callback function to call when the locale info object is fully 38 * loaded. When the onLoad option is given, the localeinfo object will attempt to 39 * load any missing locale data using the ilib loader callback. 40 * When the constructor is done (even if the data is already preassembled), the 41 * onLoad function is called with the current instance as a parameter, so this 42 * callback can be used with preassembled or dynamic loading or a mix of the two. 43 * 44 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 45 * asynchronously. If this option is given as "false", then the "onLoad" 46 * callback must be given, as the instance returned from this constructor will 47 * not be usable for a while. 48 * 49 * <li><i>loadParams</i> - an object containing parameters to pass to the 50 * loader callback function when locale data is missing. The parameters are not 51 * interpretted or modified in any way. They are simply passed along. The object 52 * may contain any property/value pairs as long as the calling code is in 53 * agreement with the loader callback function as to what those parameters mean. 54 * </ul> 55 * 56 * If this copy of ilib is pre-assembled and all the data is already available, 57 * or if the data was already previously loaded, then this constructor will call 58 * the onLoad callback immediately when the initialization is done. 59 * If the onLoad option is not given, this class will only attempt to load any 60 * missing locale data synchronously. 61 * 62 * 63 * @constructor 64 * @see {ilib.setLoaderCallback} for information about registering a loader callback 65 * function 66 * @param {Locale|string=} locale the locale for which the info is sought, or undefined for 67 * @param {Object=} options the locale for which the info is sought, or undefined for 68 * the current locale 69 */ 70 var LocaleInfo = function(locale, options) { 71 var sync = true, 72 loadParams = undefined; 73 74 /** 75 @private 76 @type {{ 77 calendar:string, 78 clock:string, 79 currency:string, 80 delimiter: {quotationStart:string,quotationEnd:string,alternateQuotationStart:string,alternateQuotationEnd:string}, 81 firstDayOfWeek:number, 82 meridiems:string, 83 numfmt:{ 84 currencyFormats:{common:string,commonNegative:string,iso:string,isoNegative:string}, 85 decimalChar:string, 86 exponential:string, 87 groupChar:string, 88 negativenumFmt:string, 89 negativepctFmt:string, 90 pctChar:string, 91 pctFmt:string, 92 prigroupSize:number, 93 roundingMode:string, 94 script:string, 95 secgroupSize:number, 96 useNative:boolean 97 }, 98 timezone:string, 99 units:string, 100 weekendEnd:number, 101 weekendStart:number, 102 paperSizes:{regular:string} 103 }} 104 */ 105 this.info = LocaleInfo.defaultInfo; 106 107 switch (typeof(locale)) { 108 case "string": 109 this.locale = new Locale(locale); 110 break; 111 default: 112 case "undefined": 113 this.locale = new Locale(); 114 break; 115 case "object": 116 this.locale = locale; 117 break; 118 } 119 120 if (options) { 121 if (typeof(options.sync) !== 'undefined') { 122 sync = !!options.sync; 123 } 124 125 if (typeof(options.loadParams) !== 'undefined') { 126 loadParams = options.loadParams; 127 } 128 } 129 130 Utils.loadData({ 131 object: "LocaleInfo", 132 locale: this.locale, 133 name: "localeinfo.json", 134 sync: sync, 135 loadParams: loadParams, 136 callback: ilib.bind(this, function (info) { 137 this.info = info || LocaleInfo.defaultInfo; 138 if (options && typeof(options.onLoad) === 'function') { 139 options.onLoad(this); 140 } 141 }) 142 }); 143 }; 144 145 LocaleInfo.defaultInfo = ilib.data.localeinfo; 146 LocaleInfo.defaultInfo = LocaleInfo.defaultInfo || { 147 "calendar": "gregorian", 148 "clock": "24", 149 "currency": "USD", 150 "delimiter": { 151 "quotationStart": "“", 152 "quotationEnd": "”", 153 "alternateQuotationStart": "‘", 154 "alternateQuotationEnd": "’" 155 }, 156 "firstDayOfWeek": 1, 157 "meridiems": "gregorian", 158 "numfmt": { 159 "script": "Latn", 160 "decimalChar": ".", 161 "groupChar": ",", 162 "pctChar": "%", 163 "exponential": "E", 164 "prigroupSize": 3, 165 "currencyFormats": { 166 "common": "{s} {n}", 167 "commonNegative": "-{s} {n}", 168 "iso": "{s} {n}", 169 "isoNegative": "({s} {n})" 170 }, 171 "negativenumFmt": "-{n}", 172 "pctFmt": "{n}%", 173 "negativepctFmt": "-{n}%", 174 "roundingMode": "halfdown", 175 "secGroupSize": null, 176 "useNative": false 177 }, 178 "paperSizes": { 179 "regular": "A4" 180 }, 181 "timezone": "Etc/UTC", 182 "units": "metric", 183 "weekendEnd": 0, 184 "weekendStart": 6 185 }; 186 187 LocaleInfo.prototype = { 188 /** 189 * Return the name of the locale's language in English. 190 * @returns {string} the name of the locale's language in English 191 */ 192 getLanguageName: function () { 193 return this.info["language.name"]; 194 }, 195 196 /** 197 * Return the name of the locale's region in English. If the locale 198 * has no region, this returns undefined. 199 * 200 * @returns {string|undefined} the name of the locale's region in English 201 */ 202 getRegionName: function () { 203 return this.info["region.name"]; 204 }, 205 206 /** 207 * Return whether this locale commonly uses the 12- or the 24-hour clock. 208 * 209 * @returns {string} "12" if the locale commonly uses a 12-hour clock, or "24" 210 * if the locale commonly uses a 24-hour clock. 211 */ 212 getClock: function() { 213 return this.info.clock; 214 }, 215 216 /** 217 * Return the locale that this info object was created with. 218 * @returns {Locale} The locale spec of the locale used to construct this info instance 219 */ 220 getLocale: function () { 221 return this.locale; 222 }, 223 224 /** 225 * Return the name of the measuring system that is commonly used in the given locale. 226 * Valid values are "uscustomary", "imperial", and "metric". 227 * 228 * @returns {string} The name of the measuring system commonly used in the locale 229 */ 230 getUnits: function () { 231 return this.info.units; 232 }, 233 234 /** 235 * Return the name of the calendar that is commonly used in the given locale. 236 * 237 * @returns {string} The name of the calendar commonly used in the locale 238 */ 239 getCalendar: function () { 240 return this.info.calendar; 241 }, 242 243 /** 244 * Return the day of week that starts weeks in the current locale. Days are still 245 * numbered the standard way with 0 for Sunday through 6 for Saturday, but calendars 246 * should be displayed and weeks calculated with the day of week returned from this 247 * function as the first day of the week. 248 * 249 * @returns {number} the day of the week that starts weeks in the current locale. 250 */ 251 getFirstDayOfWeek: function () { 252 return this.info.firstDayOfWeek; 253 }, 254 255 /** 256 * Return the day of week that starts weekend in the current locale. Days are still 257 * numbered the standard way with 0 for Sunday through 6 for Saturday. 258 * 259 * @returns {number} the day of the week that starts weeks in the current locale. 260 */ 261 getWeekEndStart: function () { 262 return this.info.weekendStart; 263 }, 264 265 /** 266 * Return the day of week that starts weekend in the current locale. Days are still 267 * numbered the standard way with 0 for Sunday through 6 for Saturday. 268 * 269 * @returns {number} the day of the week that starts weeks in the current locale. 270 */ 271 getWeekEndEnd: function () { 272 return this.info.weekendEnd; 273 }, 274 275 /** 276 * Return the default time zone for this locale. Many locales span across multiple 277 * time zones. In this case, the time zone with the largest population is chosen 278 * to represent the locale. This is obviously not that accurate, but then again, 279 * this method's return value should only be used as a default anyways. 280 * @returns {string} the default time zone for this locale. 281 */ 282 getTimeZone: function () { 283 return this.info.timezone; 284 }, 285 286 /** 287 * Return the decimal separator for formatted numbers in this locale. 288 * @returns {string} the decimal separator char 289 */ 290 getDecimalSeparator: function () { 291 return this.info.numfmt.decimalChar; 292 }, 293 294 /** 295 * Return the decimal separator for formatted numbers in this locale for native script. 296 * @returns {string} the decimal separator char 297 */ 298 getNativeDecimalSeparator: function () { 299 return (this.info.native_numfmt && this.info.native_numfmt.decimalChar) || this.info.numfmt.decimalChar; 300 }, 301 302 /** 303 * Return the separator character used to separate groups of digits on the 304 * integer side of the decimal character. 305 * @returns {string} the grouping separator char 306 */ 307 getGroupingSeparator: function () { 308 return this.info.numfmt.groupChar; 309 }, 310 311 /** 312 * Return the separator character used to separate groups of digits on the 313 * integer side of the decimal character for the native script if present other than the default script. 314 * @returns {string} the grouping separator char 315 */ 316 getNativeGroupingSeparator: function () { 317 return (this.info.native_numfmt && this.info.native_numfmt.groupChar) || this.info.numfmt.groupChar; 318 }, 319 320 /** 321 * Return the minimum number of digits grouped together on the integer side 322 * for the first (primary) group. 323 * In western European cultures, groupings are in 1000s, so the number of digits 324 * is 3. 325 * @returns {number} the number of digits in a primary grouping, or 0 for no grouping 326 */ 327 getPrimaryGroupingDigits: function () { 328 return (typeof(this.info.numfmt.prigroupSize) !== 'undefined' && this.info.numfmt.prigroupSize) || 0; 329 }, 330 331 /** 332 * Return the minimum number of digits grouped together on the integer side 333 * for the second or more (secondary) group.<p> 334 * 335 * In western European cultures, all groupings are by 1000s, so the secondary 336 * size should be 0 because there is no secondary size. In general, if this 337 * method returns 0, then all groupings are of the primary size.<p> 338 * 339 * For some other cultures, the first grouping (primary) 340 * is 3 and any subsequent groupings (secondary) are two. So, 100000 would be 341 * written as: "1,00,000". 342 * 343 * @returns {number} the number of digits in a secondary grouping, or 0 for no 344 * secondary grouping. 345 */ 346 getSecondaryGroupingDigits: function () { 347 return this.info.numfmt.secgroupSize || 0; 348 }, 349 350 /** 351 * Return the format template used to format percentages in this locale. 352 * @returns {string} the format template for formatting percentages 353 */ 354 getPercentageFormat: function () { 355 return this.info.numfmt.pctFmt; 356 }, 357 358 /** 359 * Return the format template used to format percentages in this locale 360 * with negative amounts. 361 * @returns {string} the format template for formatting percentages 362 */ 363 getNegativePercentageFormat: function () { 364 return this.info.numfmt.negativepctFmt; 365 }, 366 367 /** 368 * Return the symbol used for percentages in this locale. 369 * @returns {string} the symbol used for percentages in this locale 370 */ 371 getPercentageSymbol: function () { 372 return this.info.numfmt.pctChar || "%"; 373 }, 374 375 /** 376 * Return the symbol used for exponential in this locale. 377 * @returns {string} the symbol used for exponential in this locale 378 */ 379 getExponential: function () { 380 return this.info.numfmt.exponential; 381 }, 382 383 /** 384 * Return the symbol used for exponential in this locale for native script. 385 * @returns {string} the symbol used for exponential in this locale for native script 386 */ 387 getNativeExponential: function () { 388 return (this.info.native_numfmt && this.info.native_numfmt.exponential) || this.info.numfmt.exponential; 389 }, 390 391 /** 392 * Return the symbol used for percentages in this locale for native script. 393 * @returns {string} the symbol used for percentages in this locale for native script 394 */ 395 getNativePercentageSymbol: function () { 396 return (this.info.native_numfmt && this.info.native_numfmt.pctChar) || this.info.numfmt.pctChar || "%"; 397 398 }, 399 /** 400 * Return the format template used to format negative numbers in this locale. 401 * @returns {string} the format template for formatting negative numbers 402 */ 403 getNegativeNumberFormat: function () { 404 return this.info.numfmt.negativenumFmt; 405 }, 406 407 /** 408 * Return an object containing the format templates for formatting currencies 409 * in this locale. The object has a number of properties in it that each are 410 * a particular style of format. Normally, this contains a "common" and an "iso" 411 * style, but may contain others in the future. 412 * @returns {Object} an object containing the format templates for currencies 413 */ 414 getCurrencyFormats: function () { 415 return this.info.numfmt.currencyFormats; 416 }, 417 418 /** 419 * Return the currency that is legal in the locale, or which is most commonly 420 * used in regular commerce. 421 * @returns {string} the ISO 4217 code for the currency of this locale 422 */ 423 getCurrency: function () { 424 return this.info.currency; 425 }, 426 427 /** 428 * Return a string that describes the style of digits used by this locale. 429 * Possible return values are: 430 * <ul> 431 * <li><i>western</i> - uses the regular western 10-based digits 0 through 9 432 * <li><i>optional</i> - native 10-based digits exist, but in modern usage, 433 * this locale most often uses western digits 434 * <li><i>native</i> - native 10-based native digits exist and are used 435 * regularly by this locale 436 * <li><i>custom</i> - uses native digits by default that are not 10-based 437 * </ul> 438 * @returns {string} string that describes the style of digits used in this locale 439 */ 440 getDigitsStyle: function () { 441 if (this.info.numfmt && this.info.numfmt.useNative) { 442 return "native"; 443 } 444 if (typeof(this.info.native_numfmt) !== 'undefined') { 445 return "optional"; 446 } 447 return "western"; 448 }, 449 450 /** 451 * Return the digits of the default script if they are defined. 452 * If not defined, the default should be the regular "Arabic numerals" 453 * used in the Latin script. (0-9) 454 * @returns {string|undefined} the digits used in the default script 455 */ 456 getDigits: function () { 457 return this.info.numfmt.digits; 458 }, 459 460 /** 461 * Return the digits of the native script if they are defined. 462 * @returns {string|undefined} the digits used in the default script 463 */ 464 getNativeDigits: function () { 465 return (this.info.numfmt.useNative && this.info.numfmt.digits) || (this.info.native_numfmt && this.info.native_numfmt.digits); 466 }, 467 468 /** 469 * If this locale typically uses a different type of rounding for numeric 470 * formatting other than halfdown, especially for currency, then it can be 471 * specified in the localeinfo. If the locale uses the default, then this 472 * method returns undefined. The locale's rounding method overrides the 473 * rounding method for the currency itself, which can sometimes shared 474 * between various locales so it is less specific. 475 * @returns {string} the name of the rounding mode typically used in this 476 * locale, or "halfdown" if the locale does not override the default 477 */ 478 getRoundingMode: function () { 479 return this.info.numfmt.roundingMode; 480 }, 481 482 /** 483 * Return the default script used to write text in the language of this 484 * locale. Text for most languages is written in only one script, but there 485 * are some languages where the text can be written in a number of scripts, 486 * depending on a variety of things such as the region, ethnicity, religion, 487 * etc. of the author. This method returns the default script for the 488 * locale, in which the language is most commonly written.<p> 489 * 490 * The script is returned as an ISO 15924 4-letter code. 491 * 492 * @returns {string} the ISO 15924 code for the default script used to write 493 * text in this locale 494 */ 495 getDefaultScript: function() { 496 return (this.info.scripts) ? this.info.scripts[0] : "Latn"; 497 }, 498 499 /** 500 * Return the script used for the current locale. If the current locale 501 * explicitly defines a script, then this script is returned. If not, then 502 * the default script for the locale is returned. 503 * 504 * @see LocaleInfo.getDefaultScript 505 * @returns {string} the ISO 15924 code for the script used to write 506 * text in this locale 507 */ 508 getScript: function() { 509 return this.locale.getScript() || this.getDefaultScript(); 510 }, 511 512 /** 513 * Return an array of script codes which are used to write text in the current 514 * language. Text for most languages is written in only one script, but there 515 * are some languages where the text can be written in a number of scripts, 516 * depending on a variety of things such as the region, ethnicity, religion, 517 * etc. of the author. This method returns an array of script codes in which 518 * the language is commonly written. 519 * 520 * @returns {Array.<string>} an array of ISO 15924 codes for the scripts used 521 * to write text in this language 522 */ 523 getAllScripts: function() { 524 return this.info.scripts || ["Latn"]; 525 }, 526 527 /** 528 * Return the default style of meridiems used in this locale. Meridiems are 529 * times of day like AM/PM. In a few locales with some calendars, for example 530 * Amharic/Ethiopia using the Ethiopic calendar, the times of day may be 531 * split into different segments than simple AM/PM as in the Gregorian 532 * calendar. Only a few locales are like that. For most locales, formatting 533 * a Gregorian date will use the regular Gregorian AM/PM meridiems. 534 * 535 * @returns {string} the default meridiems style used in this locale. Possible 536 * values are "gregorian", "chinese", and "ethiopic" 537 */ 538 getMeridiemsStyle: function () { 539 return this.info.meridiems || "gregorian"; 540 }, 541 /** 542 * Return the default PaperSize information in this locale. 543 * @returns {string} default PaperSize in this locale 544 */ 545 getPaperSize: function () { 546 return this.info.paperSizes.regular; 547 }, 548 /** 549 * Return the default Delimiter QuotationStart information in this locale. 550 * @returns {string} default QuotationStart in this locale 551 */ 552 getDelimiterQuotationStart: function () { 553 return this.info.delimiter.quotationStart; 554 }, 555 /** 556 * Return the default Delimiter QuotationEnd information in this locale. 557 * @returns {string} default QuotationEnd in this locale 558 */ 559 getDelimiterQuotationEnd: function () { 560 return this.info.delimiter.quotationEnd; 561 } 562 }; 563 564 module.exports = LocaleInfo; 565