1 /* 2 * Locale.js - Locale specifier definition 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 var ilib = require("./ilib.js"); 21 var JSUtils = require("./JSUtils.js"); 22 23 /** 24 * @class 25 * Create a new locale instance. Locales are specified either with a specifier string 26 * that follows the BCP-47 convention (roughly: "language-region-script-variant") or 27 * with 4 parameters that specify the language, region, variant, and script individually.<p> 28 * 29 * The language is given as an ISO 639-1 two-letter, lower-case language code. You 30 * can find a full list of these codes at 31 * <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes</a><p> 32 * 33 * The region is given as an ISO 3166-1 two-letter, upper-case region code. You can 34 * find a full list of these codes at 35 * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2</a>.<p> 36 * 37 * The variant is any string that does not contain a dash which further differentiates 38 * locales from each other.<p> 39 * 40 * The script is given as the ISO 15924 four-letter script code. In some locales, 41 * text may be validly written in more than one script. For example, Serbian is often 42 * written in both Latin and Cyrillic, though not usually mixed together. You can find a 43 * full list of these codes at 44 * <a href="http://en.wikipedia.org/wiki/ISO_15924#List_of_codes">http://en.wikipedia.org/wiki/ISO_15924#List_of_codes</a>.<p> 45 * 46 * As an example in ilib, the script can be used in the date formatter. Dates formatted 47 * in Serbian could have day-of-week names or month names written in the Latin 48 * or Cyrillic script. Often one script is default such that sr-SR-Latn is the same 49 * as sr-SR so the script code "Latn" can be left off of the locale spec.<p> 50 * 51 * Each part is optional, and an empty string in the specifier before or after a 52 * dash or as a parameter to the constructor denotes an unspecified value. In this 53 * case, many of the ilib functions will treat the locale as generic. For example 54 * the locale "en-" is equivalent to "en" and to "en--" and denotes a locale 55 * of "English" with an unspecified region and variant, which typically matches 56 * any region or variant.<p> 57 * 58 * Without any arguments to the constructor, this function returns the locale of 59 * the host Javascript engine.<p> 60 * 61 * 62 * @constructor 63 * @param {?string|Locale=} language the ISO 639 2-letter code for the language, or a full 64 * locale spec in BCP-47 format, or another Locale instance to copy from 65 * @param {string=} region the ISO 3166 2-letter code for the region 66 * @param {string=} variant the name of the variant of this locale, if any 67 * @param {string=} script the ISO 15924 code of the script for this locale, if any 68 */ 69 var Locale = function(language, region, variant, script) { 70 if (typeof(region) === 'undefined' && typeof(variant) === 'undefined' && typeof(script) === 'undefined') { 71 var spec = language || ilib.getLocale(); 72 if (typeof(spec) === 'string') { 73 var parts = spec.split('-'); 74 for ( var i = 0; i < parts.length; i++ ) { 75 if (Locale._isLanguageCode(parts[i])) { 76 /** 77 * @private 78 * @type {string|undefined} 79 */ 80 this.language = parts[i]; 81 } else if (Locale._isRegionCode(parts[i])) { 82 /** 83 * @private 84 * @type {string|undefined} 85 */ 86 this.region = parts[i]; 87 } else if (Locale._isScriptCode(parts[i])) { 88 /** 89 * @private 90 * @type {string|undefined} 91 */ 92 this.script = parts[i]; 93 } else { 94 /** 95 * @private 96 * @type {string|undefined} 97 */ 98 this.variant = parts[i]; 99 } 100 } 101 this.language = this.language || undefined; 102 this.region = this.region || undefined; 103 this.script = this.script || undefined; 104 this.variant = this.variant || undefined; 105 } else if (typeof(spec) === 'object') { 106 this.language = spec.language || undefined; 107 this.region = spec.region || undefined; 108 this.script = spec.script || undefined; 109 this.variant = spec.variant || undefined; 110 } 111 } else { 112 if (language && typeof(language) === "string") { 113 language = language.trim(); 114 this.language = language.length > 0 ? language.toLowerCase() : undefined; 115 } else { 116 this.language = undefined; 117 } 118 if (region && typeof(region) === "string") { 119 region = region.trim(); 120 this.region = region.length > 0 ? region.toUpperCase() : undefined; 121 } else { 122 this.region = undefined; 123 } 124 if (variant && typeof(variant) === "string") { 125 variant = variant.trim(); 126 this.variant = variant.length > 0 ? variant : undefined; 127 } else { 128 this.variant = undefined; 129 } 130 if (script && typeof(script) === "string") { 131 script = script.trim(); 132 this.script = script.length > 0 ? script : undefined; 133 } else { 134 this.script = undefined; 135 } 136 } 137 this._genSpec(); 138 }; 139 140 // from http://en.wikipedia.org/wiki/ISO_3166-1 141 Locale.a2toa3regmap = { 142 "AF": "AFG", 143 "AX": "ALA", 144 "AL": "ALB", 145 "DZ": "DZA", 146 "AS": "ASM", 147 "AD": "AND", 148 "AO": "AGO", 149 "AI": "AIA", 150 "AQ": "ATA", 151 "AG": "ATG", 152 "AR": "ARG", 153 "AM": "ARM", 154 "AW": "ABW", 155 "AU": "AUS", 156 "AT": "AUT", 157 "AZ": "AZE", 158 "BS": "BHS", 159 "BH": "BHR", 160 "BD": "BGD", 161 "BB": "BRB", 162 "BY": "BLR", 163 "BE": "BEL", 164 "BZ": "BLZ", 165 "BJ": "BEN", 166 "BM": "BMU", 167 "BT": "BTN", 168 "BO": "BOL", 169 "BQ": "BES", 170 "BA": "BIH", 171 "BW": "BWA", 172 "BV": "BVT", 173 "BR": "BRA", 174 "IO": "IOT", 175 "BN": "BRN", 176 "BG": "BGR", 177 "BF": "BFA", 178 "BI": "BDI", 179 "KH": "KHM", 180 "CM": "CMR", 181 "CA": "CAN", 182 "CV": "CPV", 183 "KY": "CYM", 184 "CF": "CAF", 185 "TD": "TCD", 186 "CL": "CHL", 187 "CN": "CHN", 188 "CX": "CXR", 189 "CC": "CCK", 190 "CO": "COL", 191 "KM": "COM", 192 "CG": "COG", 193 "CD": "COD", 194 "CK": "COK", 195 "CR": "CRI", 196 "CI": "CIV", 197 "HR": "HRV", 198 "CU": "CUB", 199 "CW": "CUW", 200 "CY": "CYP", 201 "CZ": "CZE", 202 "DK": "DNK", 203 "DJ": "DJI", 204 "DM": "DMA", 205 "DO": "DOM", 206 "EC": "ECU", 207 "EG": "EGY", 208 "SV": "SLV", 209 "GQ": "GNQ", 210 "ER": "ERI", 211 "EE": "EST", 212 "ET": "ETH", 213 "FK": "FLK", 214 "FO": "FRO", 215 "FJ": "FJI", 216 "FI": "FIN", 217 "FR": "FRA", 218 "GF": "GUF", 219 "PF": "PYF", 220 "TF": "ATF", 221 "GA": "GAB", 222 "GM": "GMB", 223 "GE": "GEO", 224 "DE": "DEU", 225 "GH": "GHA", 226 "GI": "GIB", 227 "GR": "GRC", 228 "GL": "GRL", 229 "GD": "GRD", 230 "GP": "GLP", 231 "GU": "GUM", 232 "GT": "GTM", 233 "GG": "GGY", 234 "GN": "GIN", 235 "GW": "GNB", 236 "GY": "GUY", 237 "HT": "HTI", 238 "HM": "HMD", 239 "VA": "VAT", 240 "HN": "HND", 241 "HK": "HKG", 242 "HU": "HUN", 243 "IS": "ISL", 244 "IN": "IND", 245 "ID": "IDN", 246 "IR": "IRN", 247 "IQ": "IRQ", 248 "IE": "IRL", 249 "IM": "IMN", 250 "IL": "ISR", 251 "IT": "ITA", 252 "JM": "JAM", 253 "JP": "JPN", 254 "JE": "JEY", 255 "JO": "JOR", 256 "KZ": "KAZ", 257 "KE": "KEN", 258 "KI": "KIR", 259 "KP": "PRK", 260 "KR": "KOR", 261 "KW": "KWT", 262 "KG": "KGZ", 263 "LA": "LAO", 264 "LV": "LVA", 265 "LB": "LBN", 266 "LS": "LSO", 267 "LR": "LBR", 268 "LY": "LBY", 269 "LI": "LIE", 270 "LT": "LTU", 271 "LU": "LUX", 272 "MO": "MAC", 273 "MK": "MKD", 274 "MG": "MDG", 275 "MW": "MWI", 276 "MY": "MYS", 277 "MV": "MDV", 278 "ML": "MLI", 279 "MT": "MLT", 280 "MH": "MHL", 281 "MQ": "MTQ", 282 "MR": "MRT", 283 "MU": "MUS", 284 "YT": "MYT", 285 "MX": "MEX", 286 "FM": "FSM", 287 "MD": "MDA", 288 "MC": "MCO", 289 "MN": "MNG", 290 "ME": "MNE", 291 "MS": "MSR", 292 "MA": "MAR", 293 "MZ": "MOZ", 294 "MM": "MMR", 295 "NA": "NAM", 296 "NR": "NRU", 297 "NP": "NPL", 298 "NL": "NLD", 299 "NC": "NCL", 300 "NZ": "NZL", 301 "NI": "NIC", 302 "NE": "NER", 303 "NG": "NGA", 304 "NU": "NIU", 305 "NF": "NFK", 306 "MP": "MNP", 307 "NO": "NOR", 308 "OM": "OMN", 309 "PK": "PAK", 310 "PW": "PLW", 311 "PS": "PSE", 312 "PA": "PAN", 313 "PG": "PNG", 314 "PY": "PRY", 315 "PE": "PER", 316 "PH": "PHL", 317 "PN": "PCN", 318 "PL": "POL", 319 "PT": "PRT", 320 "PR": "PRI", 321 "QA": "QAT", 322 "RE": "REU", 323 "RO": "ROU", 324 "RU": "RUS", 325 "RW": "RWA", 326 "BL": "BLM", 327 "SH": "SHN", 328 "KN": "KNA", 329 "LC": "LCA", 330 "MF": "MAF", 331 "PM": "SPM", 332 "VC": "VCT", 333 "WS": "WSM", 334 "SM": "SMR", 335 "ST": "STP", 336 "SA": "SAU", 337 "SN": "SEN", 338 "RS": "SRB", 339 "SC": "SYC", 340 "SL": "SLE", 341 "SG": "SGP", 342 "SX": "SXM", 343 "SK": "SVK", 344 "SI": "SVN", 345 "SB": "SLB", 346 "SO": "SOM", 347 "ZA": "ZAF", 348 "GS": "SGS", 349 "SS": "SSD", 350 "ES": "ESP", 351 "LK": "LKA", 352 "SD": "SDN", 353 "SR": "SUR", 354 "SJ": "SJM", 355 "SZ": "SWZ", 356 "SE": "SWE", 357 "CH": "CHE", 358 "SY": "SYR", 359 "TW": "TWN", 360 "TJ": "TJK", 361 "TZ": "TZA", 362 "TH": "THA", 363 "TL": "TLS", 364 "TG": "TGO", 365 "TK": "TKL", 366 "TO": "TON", 367 "TT": "TTO", 368 "TN": "TUN", 369 "TR": "TUR", 370 "TM": "TKM", 371 "TC": "TCA", 372 "TV": "TUV", 373 "UG": "UGA", 374 "UA": "UKR", 375 "AE": "ARE", 376 "GB": "GBR", 377 "US": "USA", 378 "UM": "UMI", 379 "UY": "URY", 380 "UZ": "UZB", 381 "VU": "VUT", 382 "VE": "VEN", 383 "VN": "VNM", 384 "VG": "VGB", 385 "VI": "VIR", 386 "WF": "WLF", 387 "EH": "ESH", 388 "YE": "YEM", 389 "ZM": "ZMB", 390 "ZW": "ZWE" 391 }; 392 393 394 Locale.a1toa3langmap = { 395 "ab": "abk", 396 "aa": "aar", 397 "af": "afr", 398 "ak": "aka", 399 "sq": "sqi", 400 "am": "amh", 401 "ar": "ara", 402 "an": "arg", 403 "hy": "hye", 404 "as": "asm", 405 "av": "ava", 406 "ae": "ave", 407 "ay": "aym", 408 "az": "aze", 409 "bm": "bam", 410 "ba": "bak", 411 "eu": "eus", 412 "be": "bel", 413 "bn": "ben", 414 "bh": "bih", 415 "bi": "bis", 416 "bs": "bos", 417 "br": "bre", 418 "bg": "bul", 419 "my": "mya", 420 "ca": "cat", 421 "ch": "cha", 422 "ce": "che", 423 "ny": "nya", 424 "zh": "zho", 425 "cv": "chv", 426 "kw": "cor", 427 "co": "cos", 428 "cr": "cre", 429 "hr": "hrv", 430 "cs": "ces", 431 "da": "dan", 432 "dv": "div", 433 "nl": "nld", 434 "dz": "dzo", 435 "en": "eng", 436 "eo": "epo", 437 "et": "est", 438 "ee": "ewe", 439 "fo": "fao", 440 "fj": "fij", 441 "fi": "fin", 442 "fr": "fra", 443 "ff": "ful", 444 "gl": "glg", 445 "ka": "kat", 446 "de": "deu", 447 "el": "ell", 448 "gn": "grn", 449 "gu": "guj", 450 "ht": "hat", 451 "ha": "hau", 452 "he": "heb", 453 "hz": "her", 454 "hi": "hin", 455 "ho": "hmo", 456 "hu": "hun", 457 "ia": "ina", 458 "id": "ind", 459 "ie": "ile", 460 "ga": "gle", 461 "ig": "ibo", 462 "ik": "ipk", 463 "io": "ido", 464 "is": "isl", 465 "it": "ita", 466 "iu": "iku", 467 "ja": "jpn", 468 "jv": "jav", 469 "kl": "kal", 470 "kn": "kan", 471 "kr": "kau", 472 "ks": "kas", 473 "kk": "kaz", 474 "km": "khm", 475 "ki": "kik", 476 "rw": "kin", 477 "ky": "kir", 478 "kv": "kom", 479 "kg": "kon", 480 "ko": "kor", 481 "ku": "kur", 482 "kj": "kua", 483 "la": "lat", 484 "lb": "ltz", 485 "lg": "lug", 486 "li": "lim", 487 "ln": "lin", 488 "lo": "lao", 489 "lt": "lit", 490 "lu": "lub", 491 "lv": "lav", 492 "gv": "glv", 493 "mk": "mkd", 494 "mg": "mlg", 495 "ms": "msa", 496 "ml": "mal", 497 "mt": "mlt", 498 "mi": "mri", 499 "mr": "mar", 500 "mh": "mah", 501 "mn": "mon", 502 "na": "nau", 503 "nv": "nav", 504 "nb": "nob", 505 "nd": "nde", 506 "ne": "nep", 507 "ng": "ndo", 508 "nn": "nno", 509 "no": "nor", 510 "ii": "iii", 511 "nr": "nbl", 512 "oc": "oci", 513 "oj": "oji", 514 "cu": "chu", 515 "om": "orm", 516 "or": "ori", 517 "os": "oss", 518 "pa": "pan", 519 "pi": "pli", 520 "fa": "fas", 521 "pl": "pol", 522 "ps": "pus", 523 "pt": "por", 524 "qu": "que", 525 "rm": "roh", 526 "rn": "run", 527 "ro": "ron", 528 "ru": "rus", 529 "sa": "san", 530 "sc": "srd", 531 "sd": "snd", 532 "se": "sme", 533 "sm": "smo", 534 "sg": "sag", 535 "sr": "srp", 536 "gd": "gla", 537 "sn": "sna", 538 "si": "sin", 539 "sk": "slk", 540 "sl": "slv", 541 "so": "som", 542 "st": "sot", 543 "es": "spa", 544 "su": "sun", 545 "sw": "swa", 546 "ss": "ssw", 547 "sv": "swe", 548 "ta": "tam", 549 "te": "tel", 550 "tg": "tgk", 551 "th": "tha", 552 "ti": "tir", 553 "bo": "bod", 554 "tk": "tuk", 555 "tl": "tgl", 556 "tn": "tsn", 557 "to": "ton", 558 "tr": "tur", 559 "ts": "tso", 560 "tt": "tat", 561 "tw": "twi", 562 "ty": "tah", 563 "ug": "uig", 564 "uk": "ukr", 565 "ur": "urd", 566 "uz": "uzb", 567 "ve": "ven", 568 "vi": "vie", 569 "vo": "vol", 570 "wa": "wln", 571 "cy": "cym", 572 "wo": "wol", 573 "fy": "fry", 574 "xh": "xho", 575 "yi": "yid", 576 "yo": "yor", 577 "za": "zha", 578 "zu": "zul" 579 }; 580 581 /** 582 * Tell whether or not the str does not start with a lower case ASCII char. 583 * @private 584 * @param {string} str the char to check 585 * @return {boolean} true if the char is not a lower case ASCII char 586 */ 587 Locale._notLower = function(str) { 588 // do this with ASCII only so we don't have to depend on the CType functions 589 var ch = str.charCodeAt(0); 590 return ch < 97 || ch > 122; 591 }; 592 593 /** 594 * Tell whether or not the str does not start with an upper case ASCII char. 595 * @private 596 * @param {string} str the char to check 597 * @return {boolean} true if the char is a not an upper case ASCII char 598 */ 599 Locale._notUpper = function(str) { 600 // do this with ASCII only so we don't have to depend on the CType functions 601 var ch = str.charCodeAt(0); 602 return ch < 65 || ch > 90; 603 }; 604 605 /** 606 * Tell whether or not the str does not start with a digit char. 607 * @private 608 * @param {string} str the char to check 609 * @return {boolean} true if the char is a not an upper case ASCII char 610 */ 611 Locale._notDigit = function(str) { 612 // do this with ASCII only so we don't have to depend on the CType functions 613 var ch = str.charCodeAt(0); 614 return ch < 48 || ch > 57; 615 }; 616 617 /** 618 * Tell whether or not the given string has the correct syntax to be 619 * an ISO 639 language code. 620 * 621 * @private 622 * @param {string} str the string to parse 623 * @return {boolean} true if the string could syntactically be a language code. 624 */ 625 Locale._isLanguageCode = function(str) { 626 if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) { 627 return false; 628 } 629 630 for (var i = 0; i < str.length; i++) { 631 if (Locale._notLower(str.charAt(i))) { 632 return false; 633 } 634 } 635 636 return true; 637 }; 638 639 /** 640 * Tell whether or not the given string has the correct syntax to be 641 * an ISO 3166 2-letter region code or M.49 3-digit region code. 642 * 643 * @private 644 * @param {string} str the string to parse 645 * @return {boolean} true if the string could syntactically be a language code. 646 */ 647 Locale._isRegionCode = function (str) { 648 var i; 649 650 if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) { 651 return false; 652 } 653 654 if (str.length === 2) { 655 for (i = 0; i < str.length; i++) { 656 if (Locale._notUpper(str.charAt(i))) { 657 return false; 658 } 659 } 660 } else { 661 for (i = 0; i < str.length; i++) { 662 if (Locale._notDigit(str.charAt(i))) { 663 return false; 664 } 665 } 666 } 667 668 return true; 669 }; 670 671 /** 672 * Tell whether or not the given string has the correct syntax to be 673 * an ISO 639 language code. 674 * 675 * @private 676 * @param {string} str the string to parse 677 * @return {boolean} true if the string could syntactically be a language code. 678 */ 679 Locale._isScriptCode = function(str) { 680 if (typeof(str) === 'undefined' || str.length !== 4 || Locale._notUpper(str.charAt(0))) { 681 return false; 682 } 683 684 for (var i = 1; i < 4; i++) { 685 if (Locale._notLower(str.charAt(i))) { 686 return false; 687 } 688 } 689 690 return true; 691 }; 692 693 /** 694 * Return the ISO-3166 alpha3 equivalent region code for the given ISO 3166 alpha2 695 * region code. If the given alpha2 code is not found, this function returns its 696 * argument unchanged. 697 * @static 698 * @param {string|undefined} alpha2 the alpha2 code to map 699 * @return {string|undefined} the alpha3 equivalent of the given alpha2 code, or the alpha2 700 * parameter if the alpha2 value is not found 701 */ 702 Locale.regionAlpha2ToAlpha3 = function(alpha2) { 703 return Locale.a2toa3regmap[alpha2] || alpha2; 704 }; 705 706 /** 707 * Return the ISO-639 alpha3 equivalent language code for the given ISO 639 alpha1 708 * language code. If the given alpha1 code is not found, this function returns its 709 * argument unchanged. 710 * @static 711 * @param {string|undefined} alpha1 the alpha1 code to map 712 * @return {string|undefined} the alpha3 equivalent of the given alpha1 code, or the alpha1 713 * parameter if the alpha1 value is not found 714 */ 715 Locale.languageAlpha1ToAlpha3 = function(alpha1) { 716 return Locale.a1toa3langmap[alpha1] || alpha1; 717 }; 718 719 Locale.prototype = { 720 /** 721 * @private 722 */ 723 _genSpec: function () { 724 this.spec = this.language || ""; 725 726 if (this.script) { 727 if (this.spec.length > 0) { 728 this.spec += "-"; 729 } 730 this.spec += this.script; 731 } 732 733 if (this.region) { 734 if (this.spec.length > 0) { 735 this.spec += "-"; 736 } 737 this.spec += this.region; 738 } 739 740 if (this.variant) { 741 if (this.spec.length > 0) { 742 this.spec += "-"; 743 } 744 this.spec += this.variant; 745 } 746 }, 747 748 /** 749 * Return the ISO 639 language code for this locale. 750 * @return {string|undefined} the language code for this locale 751 */ 752 getLanguage: function() { 753 return this.language; 754 }, 755 756 /** 757 * Return the language of this locale as an ISO-639-alpha3 language code 758 * @return {string|undefined} the alpha3 language code of this locale 759 */ 760 getLanguageAlpha3: function() { 761 return Locale.languageAlpha1ToAlpha3(this.language); 762 }, 763 764 /** 765 * Return the ISO 3166 region code for this locale. 766 * @return {string|undefined} the region code of this locale 767 */ 768 getRegion: function() { 769 return this.region; 770 }, 771 772 /** 773 * Return the region of this locale as an ISO-3166-alpha3 region code 774 * @return {string|undefined} the alpha3 region code of this locale 775 */ 776 getRegionAlpha3: function() { 777 return Locale.regionAlpha2ToAlpha3(this.region); 778 }, 779 780 /** 781 * Return the ISO 15924 script code for this locale 782 * @return {string|undefined} the script code of this locale 783 */ 784 getScript: function () { 785 return this.script; 786 }, 787 788 /** 789 * Return the variant code for this locale 790 * @return {string|undefined} the variant code of this locale, if any 791 */ 792 getVariant: function() { 793 return this.variant; 794 }, 795 796 /** 797 * Return the whole locale specifier as a string. 798 * @return {string} the locale specifier 799 */ 800 getSpec: function() { 801 if (!this.spec) this._genSpec(); 802 return this.spec; 803 }, 804 805 /** 806 * Return the language locale specifier. This includes the 807 * language and the script if it is available. This can be 808 * used to see whether the written language of two locales 809 * match each other regardless of the region or variant. 810 * 811 * @return {string} the language locale specifier 812 */ 813 getLangSpec: function() { 814 var spec = this.language; 815 if (spec && this.script) { 816 spec += "-" + this.script; 817 } 818 return spec || ""; 819 }, 820 821 /** 822 * Express this locale object as a string. Currently, this simply calls the getSpec 823 * function to represent the locale as its specifier. 824 * 825 * @return {string} the locale specifier 826 */ 827 toString: function() { 828 return this.getSpec(); 829 }, 830 831 /** 832 * Return true if the the other locale is exactly equal to the current one. 833 * @return {boolean} whether or not the other locale is equal to the current one 834 */ 835 equals: function(other) { 836 return this.language === other.language && 837 this.region === other.region && 838 this.script === other.script && 839 this.variant === other.variant; 840 }, 841 842 /** 843 * Return true if the current locale is the special pseudo locale. 844 * @return {boolean} true if the current locale is the special pseudo locale 845 */ 846 isPseudo: function () { 847 return JSUtils.indexOf(ilib.pseudoLocales, this.spec) > -1; 848 } 849 }; 850 851 // static functions 852 /** 853 * @private 854 */ 855 Locale.locales = []; 856 // !macro localelist 857 858 /** 859 * Return the list of available locales that this iLib file supports. 860 * If this copy of ilib is pre-assembled with locale data, then the 861 * list locales may be much smaller 862 * than the list of all available locales in the iLib repository. The 863 * assembly tool will automatically fill in the list for an assembled 864 * copy of iLib. If this copy is being used with dynamically loaded 865 * data, then you 866 * can load any locale that iLib supports. You can form a locale with any 867 * combination of a language and region tags that exist in the locale 868 * data directory. Language tags are in the root of the locale data dir, 869 * and region tags can be found underneath the "und" directory. (The 870 * region tags are separated into a different dir because the region names 871 * conflict with language names on file systems that are case-insensitive.) 872 * If you have culled the locale data directory to limit the size of 873 * your app, then this function should return only those files that actually exist 874 * according to the ilibmanifest.json file in the root of that locale 875 * data dir. Make sure your ilibmanifest.json file is up-to-date with 876 * respect to the list of files that exist in the locale data dir. 877 * 878 * @param {boolean} sync if false, load the list of available files from disk 879 * asynchronously, otherwise load them synchronously. (Default: true/synchronously) 880 * @param {Function} onLoad a callback function to call if asynchronous 881 * load was requested and the list of files have been loaded. 882 * @return {Array.<string>} this is an array of locale specs for which 883 * this iLib file has locale data for 884 */ 885 Locale.getAvailableLocales = function (sync, onLoad) { 886 var locales = []; 887 if (Locale.locales.length || typeof(ilib._load.listAvailableFiles) !== 'function') { 888 locales = Locale.locales; 889 if (onLoad && typeof(onLoad) === 'function') { 890 onLoad(locales); 891 } 892 } else { 893 if (typeof(sync) === 'undefined') { 894 sync = true; 895 } 896 ilib._load.listAvailableFiles(sync, function(manifest) { 897 if (manifest) { 898 for (var dir in manifest) { 899 var filelist = manifest[dir]; 900 for (var i = 0; i < filelist.length; i++) { 901 if (filelist[i].length > 15 && filelist[i].substr(-15) === "localeinfo.json") { 902 locales.push(filelist[i].substring(0,filelist[i].length-16).replace(/\//g, "-")); 903 } 904 } 905 } 906 } 907 if (onLoad && typeof(onLoad) === 'function') { 908 onLoad(locales); 909 } 910 }); 911 } 912 return locales; 913 }; 914 915 module.exports = Locale; 916