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