testIntl.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285
  1. // Copyright (C) 2011 2012 Norbert Lindenberg. All rights reserved.
  2. // Copyright (C) 2012 2013 Mozilla Corporation. All rights reserved.
  3. // This code is governed by the BSD license found in the LICENSE file.
  4. /*---
  5. description: |
  6. This file contains shared functions for the tests in the conformance test
  7. suite for the ECMAScript Internationalization API.
  8. author: Norbert Lindenberg
  9. ---*/
  10. /**
  11. */
  12. /**
  13. * @description Calls the provided function for every service constructor in
  14. * the Intl object.
  15. * @param {Function} f the function to call for each service constructor in
  16. * the Intl object.
  17. * @param {Function} Constructor the constructor object to test with.
  18. */
  19. function testWithIntlConstructors(f) {
  20. var constructors = ["Collator", "NumberFormat", "DateTimeFormat"];
  21. // Optionally supported Intl constructors.
  22. ["PluralRules"].forEach(function(constructor) {
  23. if (typeof Intl[constructor] === "function") {
  24. constructors[constructors.length] = constructor;
  25. }
  26. });
  27. constructors.forEach(function (constructor) {
  28. var Constructor = Intl[constructor];
  29. try {
  30. f(Constructor);
  31. } catch (e) {
  32. e.message += " (Testing with " + constructor + ".)";
  33. throw e;
  34. }
  35. });
  36. }
  37. /**
  38. * Taints a named data property of the given object by installing
  39. * a setter that throws an exception.
  40. * @param {object} obj the object whose data property to taint
  41. * @param {string} property the property to taint
  42. */
  43. function taintDataProperty(obj, property) {
  44. Object.defineProperty(obj, property, {
  45. set: function(value) {
  46. $ERROR("Client code can adversely affect behavior: setter for " + property + ".");
  47. },
  48. enumerable: false,
  49. configurable: true
  50. });
  51. }
  52. /**
  53. * Taints a named method of the given object by replacing it with a function
  54. * that throws an exception.
  55. * @param {object} obj the object whose method to taint
  56. * @param {string} property the name of the method to taint
  57. */
  58. function taintMethod(obj, property) {
  59. Object.defineProperty(obj, property, {
  60. value: function() {
  61. $ERROR("Client code can adversely affect behavior: method " + property + ".");
  62. },
  63. writable: true,
  64. enumerable: false,
  65. configurable: true
  66. });
  67. }
  68. /**
  69. * Taints the given properties (and similarly named properties) by installing
  70. * setters on Object.prototype that throw exceptions.
  71. * @param {Array} properties an array of property names to taint
  72. */
  73. function taintProperties(properties) {
  74. properties.forEach(function (property) {
  75. var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"];
  76. adaptedProperties.forEach(function (property) {
  77. taintDataProperty(Object.prototype, property);
  78. });
  79. });
  80. }
  81. /**
  82. * Taints the Array object by creating a setter for the property "0" and
  83. * replacing some key methods with functions that throw exceptions.
  84. */
  85. function taintArray() {
  86. taintDataProperty(Array.prototype, "0");
  87. taintMethod(Array.prototype, "indexOf");
  88. taintMethod(Array.prototype, "join");
  89. taintMethod(Array.prototype, "push");
  90. taintMethod(Array.prototype, "slice");
  91. taintMethod(Array.prototype, "sort");
  92. }
  93. /**
  94. * Gets locale support info for the given constructor object, which must be one
  95. * of Intl constructors.
  96. * @param {object} Constructor the constructor for which to get locale support info
  97. * @return {object} locale support info with the following properties:
  98. * supported: array of fully supported language tags
  99. * byFallback: array of language tags that are supported through fallbacks
  100. * unsupported: array of unsupported language tags
  101. */
  102. function getLocaleSupportInfo(Constructor) {
  103. var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"];
  104. var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant"];
  105. var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG"];
  106. var allTags = [];
  107. var i, j, k;
  108. var language, script, country;
  109. for (i = 0; i < languages.length; i++) {
  110. language = languages[i];
  111. allTags.push(language);
  112. for (j = 0; j < scripts.length; j++) {
  113. script = scripts[j];
  114. allTags.push(language + "-" + script);
  115. for (k = 0; k < countries.length; k++) {
  116. country = countries[k];
  117. allTags.push(language + "-" + script + "-" + country);
  118. }
  119. }
  120. for (k = 0; k < countries.length; k++) {
  121. country = countries[k];
  122. allTags.push(language + "-" + country);
  123. }
  124. }
  125. var supported = [];
  126. var byFallback = [];
  127. var unsupported = [];
  128. for (i = 0; i < allTags.length; i++) {
  129. var request = allTags[i];
  130. var result = new Constructor([request], {localeMatcher: "lookup"}).resolvedOptions().locale;
  131. if (request === result) {
  132. supported.push(request);
  133. } else if (request.indexOf(result) === 0) {
  134. byFallback.push(request);
  135. } else {
  136. unsupported.push(request);
  137. }
  138. }
  139. return {
  140. supported: supported,
  141. byFallback: byFallback,
  142. unsupported: unsupported
  143. };
  144. }
  145. /**
  146. * Returns an array of strings for which IsStructurallyValidLanguageTag() returns false
  147. */
  148. function getInvalidLanguageTags() {
  149. var invalidLanguageTags = [
  150. "", // empty tag
  151. "i", // singleton alone
  152. "x", // private use without subtag
  153. "u", // extension singleton in first place
  154. "419", // region code in first place
  155. "u-nu-latn-cu-bob", // extension sequence without language
  156. "hans-cmn-cn", // "hans" could theoretically be a 4-letter language code,
  157. // but those can't be followed by extlang codes.
  158. "cmn-hans-cn-u-u", // duplicate singleton
  159. "cmn-hans-cn-t-u-ca-u", // duplicate singleton
  160. "de-gregory-gregory", // duplicate variant
  161. "*", // language range
  162. "de-*", // language range
  163. "中文", // non-ASCII letters
  164. "en-ß", // non-ASCII letters
  165. "ıd", // non-ASCII letters
  166. "es-Latn-latn", // two scripts
  167. "pl-PL-pl", // two regions
  168. "u-ca-gregory", // extension in first place
  169. "de-1996-1996", // duplicate numeric variant
  170. "pt-u-ca-gregory-u-nu-latn", // duplicate singleton subtag
  171. // underscores in different parts of the language tag
  172. "de_DE",
  173. "DE_de",
  174. "cmn_Hans",
  175. "cmn-hans_cn",
  176. "es_419",
  177. "es-419-u-nu-latn-cu_bob",
  178. "i_klingon",
  179. "cmn-hans-cn-t-ca-u-ca-x_t-u",
  180. "enochian_enochian",
  181. "de-gregory_u-ca-gregory",
  182. "en\u0000", // null-terminator sequence
  183. " en", // leading whitespace
  184. "en ", // trailing whitespace
  185. "it-IT-Latn", // country before script tag
  186. "de-u", // incomplete Unicode extension sequences
  187. "de-u-",
  188. "de-u-ca-",
  189. "de-u-ca-gregory-",
  190. "si-x", // incomplete private-use tags
  191. "x-",
  192. "x-y-",
  193. ];
  194. // make sure the data above is correct
  195. for (var i = 0; i < invalidLanguageTags.length; ++i) {
  196. var invalidTag = invalidLanguageTags[i];
  197. assert(
  198. !isCanonicalizedStructurallyValidLanguageTag(invalidTag),
  199. "Test data \"" + invalidTag + "\" is a canonicalized and structurally valid language tag."
  200. );
  201. }
  202. return invalidLanguageTags;
  203. }
  204. /**
  205. * @description Tests whether locale is a String value representing a
  206. * structurally valid and canonicalized BCP 47 language tag, as defined in
  207. * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API
  208. * Specification.
  209. * @param {String} locale the string to be tested.
  210. * @result {Boolean} whether the test succeeded.
  211. */
  212. function isCanonicalizedStructurallyValidLanguageTag(locale) {
  213. /**
  214. * Regular expression defining BCP 47 language tags.
  215. *
  216. * Spec: RFC 5646 section 2.1.
  217. */
  218. var alpha = "[a-zA-Z]",
  219. digit = "[0-9]",
  220. alphanum = "(" + alpha + "|" + digit + ")",
  221. regular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)",
  222. irregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)",
  223. grandfathered = "(" + irregular + "|" + regular + ")",
  224. privateuse = "(x(-[a-z0-9]{1,8})+)",
  225. singleton = "(" + digit + "|[A-WY-Za-wy-z])",
  226. extension = "(" + singleton + "(-" + alphanum + "{2,8})+)",
  227. variant = "(" + alphanum + "{5,8}|(" + digit + alphanum + "{3}))",
  228. region = "(" + alpha + "{2}|" + digit + "{3})",
  229. script = "(" + alpha + "{4})",
  230. extlang = "(" + alpha + "{3}(-" + alpha + "{3}){0,2})",
  231. language = "(" + alpha + "{2,3}(-" + extlang + ")?|" + alpha + "{4}|" + alpha + "{5,8})",
  232. langtag = language + "(-" + script + ")?(-" + region + ")?(-" + variant + ")*(-" + extension + ")*(-" + privateuse + ")?",
  233. languageTag = "^(" + langtag + "|" + privateuse + "|" + grandfathered + ")$",
  234. languageTagRE = new RegExp(languageTag, "i");
  235. var duplicateSingleton = "-" + singleton + "-(.*-)?\\1(?!" + alphanum + ")",
  236. duplicateSingletonRE = new RegExp(duplicateSingleton, "i"),
  237. duplicateVariant = "(" + alphanum + "{2,8}-)+" + variant + "-(" + alphanum + "{2,8}-)*\\3(?!" + alphanum + ")",
  238. duplicateVariantRE = new RegExp(duplicateVariant, "i");
  239. /**
  240. * Verifies that the given string is a well-formed BCP 47 language tag
  241. * with no duplicate variant or singleton subtags.
  242. *
  243. * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2.
  244. */
  245. function isStructurallyValidLanguageTag(locale) {
  246. if (!languageTagRE.test(locale)) {
  247. return false;
  248. }
  249. locale = locale.split(/-x-/)[0];
  250. return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale);
  251. }
  252. /**
  253. * Mappings from complete tags to preferred values.
  254. *
  255. * Spec: IANA Language Subtag Registry.
  256. */
  257. var __tagMappings = {
  258. // property names must be in lower case; values in canonical form
  259. // grandfathered tags from IANA language subtag registry, file date 2018-04-23
  260. "art-lojban": "jbo",
  261. "cel-gaulish": "cel-gaulish",
  262. "en-gb-oed": "en-GB-oxendict",
  263. "i-ami": "ami",
  264. "i-bnn": "bnn",
  265. "i-default": "i-default",
  266. "i-enochian": "i-enochian",
  267. "i-hak": "hak",
  268. "i-klingon": "tlh",
  269. "i-lux": "lb",
  270. "i-mingo": "i-mingo",
  271. "i-navajo": "nv",
  272. "i-pwn": "pwn",
  273. "i-tao": "tao",
  274. "i-tay": "tay",
  275. "i-tsu": "tsu",
  276. "no-bok": "nb",
  277. "no-nyn": "nn",
  278. "sgn-be-fr": "sfb",
  279. "sgn-be-nl": "vgt",
  280. "sgn-ch-de": "sgg",
  281. "zh-guoyu": "cmn",
  282. "zh-hakka": "hak",
  283. "zh-min": "zh-min",
  284. "zh-min-nan": "nan",
  285. "zh-xiang": "hsn",
  286. // deprecated redundant tags from IANA language subtag registry, file date 2018-04-23
  287. "sgn-br": "bzs",
  288. "sgn-co": "csn",
  289. "sgn-de": "gsg",
  290. "sgn-dk": "dsl",
  291. "sgn-es": "ssp",
  292. "sgn-fr": "fsl",
  293. "sgn-gb": "bfi",
  294. "sgn-gr": "gss",
  295. "sgn-ie": "isg",
  296. "sgn-it": "ise",
  297. "sgn-jp": "jsl",
  298. "sgn-mx": "mfs",
  299. "sgn-ni": "ncs",
  300. "sgn-nl": "dse",
  301. "sgn-no": "nsl",
  302. "sgn-pt": "psr",
  303. "sgn-se": "swl",
  304. "sgn-us": "ase",
  305. "sgn-za": "sfs",
  306. "zh-cmn": "cmn",
  307. "zh-cmn-hans": "cmn-Hans",
  308. "zh-cmn-hant": "cmn-Hant",
  309. "zh-gan": "gan",
  310. "zh-wuu": "wuu",
  311. "zh-yue": "yue",
  312. // deprecated variant with prefix from IANA language subtag registry, file date 2018-04-23
  313. "ja-latn-hepburn-heploc": "ja-Latn-alalc97"
  314. };
  315. /**
  316. * Mappings from non-extlang subtags to preferred values.
  317. *
  318. * Spec: IANA Language Subtag Registry.
  319. */
  320. var __subtagMappings = {
  321. // property names and values must be in canonical case
  322. // language subtags with Preferred-Value mappings from IANA language subtag registry, file date 2018-04-23
  323. "in": "id",
  324. "iw": "he",
  325. "ji": "yi",
  326. "jw": "jv",
  327. "mo": "ro",
  328. "aam": "aas",
  329. "adp": "dz",
  330. "aue": "ktz",
  331. "ayx": "nun",
  332. "bgm": "bcg",
  333. "bjd": "drl",
  334. "ccq": "rki",
  335. "cjr": "mom",
  336. "cka": "cmr",
  337. "cmk": "xch",
  338. "coy": "pij",
  339. "cqu": "quh",
  340. "drh": "khk",
  341. "drw": "prs",
  342. "gav": "dev",
  343. "gfx": "vaj",
  344. "ggn": "gvr",
  345. "gti": "nyc",
  346. "guv": "duz",
  347. "hrr": "jal",
  348. "ibi": "opa",
  349. "ilw": "gal",
  350. "jeg": "oyb",
  351. "kgc": "tdf",
  352. "kgh": "kml",
  353. "koj": "kwv",
  354. "krm": "bmf",
  355. "ktr": "dtp",
  356. "kvs": "gdj",
  357. "kwq": "yam",
  358. "kxe": "tvd",
  359. "kzj": "dtp",
  360. "kzt": "dtp",
  361. "lii": "raq",
  362. "lmm": "rmx",
  363. "meg": "cir",
  364. "mst": "mry",
  365. "mwj": "vaj",
  366. "myt": "mry",
  367. "nad": "xny",
  368. "ncp": "kdz",
  369. "nnx": "ngv",
  370. "nts": "pij",
  371. "oun": "vaj",
  372. "pcr": "adx",
  373. "pmc": "huw",
  374. "pmu": "phr",
  375. "ppa": "bfy",
  376. "ppr": "lcq",
  377. "pry": "prt",
  378. "puz": "pub",
  379. "sca": "hle",
  380. "skk": "oyb",
  381. "tdu": "dtp",
  382. "thc": "tpo",
  383. "thx": "oyb",
  384. "tie": "ras",
  385. "tkk": "twm",
  386. "tlw": "weo",
  387. "tmp": "tyj",
  388. "tne": "kak",
  389. "tnf": "prs",
  390. "tsf": "taj",
  391. "uok": "ema",
  392. "xba": "cax",
  393. "xia": "acn",
  394. "xkh": "waw",
  395. "xsj": "suj",
  396. "ybd": "rki",
  397. "yma": "lrr",
  398. "ymt": "mtm",
  399. "yos": "zom",
  400. "yuu": "yug",
  401. // region subtags with Preferred-Value mappings from IANA language subtag registry, file date 2018-04-23
  402. "BU": "MM",
  403. "DD": "DE",
  404. "FX": "FR",
  405. "TP": "TL",
  406. "YD": "YE",
  407. "ZR": "CD"
  408. };
  409. /**
  410. * Mappings from extlang subtags to preferred values.
  411. *
  412. * Spec: IANA Language Subtag Registry.
  413. */
  414. var __extlangMappings = {
  415. // extlang subtags with Preferred-Value mappings from IANA language subtag registry, file date 2018-04-23
  416. // values are arrays with [0] the replacement value, [1] (if present) the prefix to be removed
  417. "aao": ["aao", "ar"],
  418. "abh": ["abh", "ar"],
  419. "abv": ["abv", "ar"],
  420. "acm": ["acm", "ar"],
  421. "acq": ["acq", "ar"],
  422. "acw": ["acw", "ar"],
  423. "acx": ["acx", "ar"],
  424. "acy": ["acy", "ar"],
  425. "adf": ["adf", "ar"],
  426. "ads": ["ads", "sgn"],
  427. "aeb": ["aeb", "ar"],
  428. "aec": ["aec", "ar"],
  429. "aed": ["aed", "sgn"],
  430. "aen": ["aen", "sgn"],
  431. "afb": ["afb", "ar"],
  432. "afg": ["afg", "sgn"],
  433. "ajp": ["ajp", "ar"],
  434. "apc": ["apc", "ar"],
  435. "apd": ["apd", "ar"],
  436. "arb": ["arb", "ar"],
  437. "arq": ["arq", "ar"],
  438. "ars": ["ars", "ar"],
  439. "ary": ["ary", "ar"],
  440. "arz": ["arz", "ar"],
  441. "ase": ["ase", "sgn"],
  442. "asf": ["asf", "sgn"],
  443. "asp": ["asp", "sgn"],
  444. "asq": ["asq", "sgn"],
  445. "asw": ["asw", "sgn"],
  446. "auz": ["auz", "ar"],
  447. "avl": ["avl", "ar"],
  448. "ayh": ["ayh", "ar"],
  449. "ayl": ["ayl", "ar"],
  450. "ayn": ["ayn", "ar"],
  451. "ayp": ["ayp", "ar"],
  452. "bbz": ["bbz", "ar"],
  453. "bfi": ["bfi", "sgn"],
  454. "bfk": ["bfk", "sgn"],
  455. "bjn": ["bjn", "ms"],
  456. "bog": ["bog", "sgn"],
  457. "bqn": ["bqn", "sgn"],
  458. "bqy": ["bqy", "sgn"],
  459. "btj": ["btj", "ms"],
  460. "bve": ["bve", "ms"],
  461. "bvl": ["bvl", "sgn"],
  462. "bvu": ["bvu", "ms"],
  463. "bzs": ["bzs", "sgn"],
  464. "cdo": ["cdo", "zh"],
  465. "cds": ["cds", "sgn"],
  466. "cjy": ["cjy", "zh"],
  467. "cmn": ["cmn", "zh"],
  468. "coa": ["coa", "ms"],
  469. "cpx": ["cpx", "zh"],
  470. "csc": ["csc", "sgn"],
  471. "csd": ["csd", "sgn"],
  472. "cse": ["cse", "sgn"],
  473. "csf": ["csf", "sgn"],
  474. "csg": ["csg", "sgn"],
  475. "csl": ["csl", "sgn"],
  476. "csn": ["csn", "sgn"],
  477. "csq": ["csq", "sgn"],
  478. "csr": ["csr", "sgn"],
  479. "czh": ["czh", "zh"],
  480. "czo": ["czo", "zh"],
  481. "doq": ["doq", "sgn"],
  482. "dse": ["dse", "sgn"],
  483. "dsl": ["dsl", "sgn"],
  484. "dup": ["dup", "ms"],
  485. "ecs": ["ecs", "sgn"],
  486. "esl": ["esl", "sgn"],
  487. "esn": ["esn", "sgn"],
  488. "eso": ["eso", "sgn"],
  489. "eth": ["eth", "sgn"],
  490. "fcs": ["fcs", "sgn"],
  491. "fse": ["fse", "sgn"],
  492. "fsl": ["fsl", "sgn"],
  493. "fss": ["fss", "sgn"],
  494. "gan": ["gan", "zh"],
  495. "gds": ["gds", "sgn"],
  496. "gom": ["gom", "kok"],
  497. "gse": ["gse", "sgn"],
  498. "gsg": ["gsg", "sgn"],
  499. "gsm": ["gsm", "sgn"],
  500. "gss": ["gss", "sgn"],
  501. "gus": ["gus", "sgn"],
  502. "hab": ["hab", "sgn"],
  503. "haf": ["haf", "sgn"],
  504. "hak": ["hak", "zh"],
  505. "hds": ["hds", "sgn"],
  506. "hji": ["hji", "ms"],
  507. "hks": ["hks", "sgn"],
  508. "hos": ["hos", "sgn"],
  509. "hps": ["hps", "sgn"],
  510. "hsh": ["hsh", "sgn"],
  511. "hsl": ["hsl", "sgn"],
  512. "hsn": ["hsn", "zh"],
  513. "icl": ["icl", "sgn"],
  514. "iks": ["iks", "sgn"],
  515. "ils": ["ils", "sgn"],
  516. "inl": ["inl", "sgn"],
  517. "ins": ["ins", "sgn"],
  518. "ise": ["ise", "sgn"],
  519. "isg": ["isg", "sgn"],
  520. "isr": ["isr", "sgn"],
  521. "jak": ["jak", "ms"],
  522. "jax": ["jax", "ms"],
  523. "jcs": ["jcs", "sgn"],
  524. "jhs": ["jhs", "sgn"],
  525. "jls": ["jls", "sgn"],
  526. "jos": ["jos", "sgn"],
  527. "jsl": ["jsl", "sgn"],
  528. "jus": ["jus", "sgn"],
  529. "kgi": ["kgi", "sgn"],
  530. "knn": ["knn", "kok"],
  531. "kvb": ["kvb", "ms"],
  532. "kvk": ["kvk", "sgn"],
  533. "kvr": ["kvr", "ms"],
  534. "kxd": ["kxd", "ms"],
  535. "lbs": ["lbs", "sgn"],
  536. "lce": ["lce", "ms"],
  537. "lcf": ["lcf", "ms"],
  538. "liw": ["liw", "ms"],
  539. "lls": ["lls", "sgn"],
  540. "lsg": ["lsg", "sgn"],
  541. "lsl": ["lsl", "sgn"],
  542. "lso": ["lso", "sgn"],
  543. "lsp": ["lsp", "sgn"],
  544. "lst": ["lst", "sgn"],
  545. "lsy": ["lsy", "sgn"],
  546. "ltg": ["ltg", "lv"],
  547. "lvs": ["lvs", "lv"],
  548. "lws": ["lws", "sgn"],
  549. "lzh": ["lzh", "zh"],
  550. "max": ["max", "ms"],
  551. "mdl": ["mdl", "sgn"],
  552. "meo": ["meo", "ms"],
  553. "mfa": ["mfa", "ms"],
  554. "mfb": ["mfb", "ms"],
  555. "mfs": ["mfs", "sgn"],
  556. "min": ["min", "ms"],
  557. "mnp": ["mnp", "zh"],
  558. "mqg": ["mqg", "ms"],
  559. "mre": ["mre", "sgn"],
  560. "msd": ["msd", "sgn"],
  561. "msi": ["msi", "ms"],
  562. "msr": ["msr", "sgn"],
  563. "mui": ["mui", "ms"],
  564. "mzc": ["mzc", "sgn"],
  565. "mzg": ["mzg", "sgn"],
  566. "mzy": ["mzy", "sgn"],
  567. "nan": ["nan", "zh"],
  568. "nbs": ["nbs", "sgn"],
  569. "ncs": ["ncs", "sgn"],
  570. "nsi": ["nsi", "sgn"],
  571. "nsl": ["nsl", "sgn"],
  572. "nsp": ["nsp", "sgn"],
  573. "nsr": ["nsr", "sgn"],
  574. "nzs": ["nzs", "sgn"],
  575. "okl": ["okl", "sgn"],
  576. "orn": ["orn", "ms"],
  577. "ors": ["ors", "ms"],
  578. "pel": ["pel", "ms"],
  579. "pga": ["pga", "ar"],
  580. "pgz": ["pgz", "sgn"],
  581. "pks": ["pks", "sgn"],
  582. "prl": ["prl", "sgn"],
  583. "prz": ["prz", "sgn"],
  584. "psc": ["psc", "sgn"],
  585. "psd": ["psd", "sgn"],
  586. "pse": ["pse", "ms"],
  587. "psg": ["psg", "sgn"],
  588. "psl": ["psl", "sgn"],
  589. "pso": ["pso", "sgn"],
  590. "psp": ["psp", "sgn"],
  591. "psr": ["psr", "sgn"],
  592. "pys": ["pys", "sgn"],
  593. "rms": ["rms", "sgn"],
  594. "rsi": ["rsi", "sgn"],
  595. "rsl": ["rsl", "sgn"],
  596. "rsm": ["rsm", "sgn"],
  597. "sdl": ["sdl", "sgn"],
  598. "sfb": ["sfb", "sgn"],
  599. "sfs": ["sfs", "sgn"],
  600. "sgg": ["sgg", "sgn"],
  601. "sgx": ["sgx", "sgn"],
  602. "shu": ["shu", "ar"],
  603. "slf": ["slf", "sgn"],
  604. "sls": ["sls", "sgn"],
  605. "sqk": ["sqk", "sgn"],
  606. "sqs": ["sqs", "sgn"],
  607. "ssh": ["ssh", "ar"],
  608. "ssp": ["ssp", "sgn"],
  609. "ssr": ["ssr", "sgn"],
  610. "svk": ["svk", "sgn"],
  611. "swc": ["swc", "sw"],
  612. "swh": ["swh", "sw"],
  613. "swl": ["swl", "sgn"],
  614. "syy": ["syy", "sgn"],
  615. "szs": ["szs", "sgn"],
  616. "tmw": ["tmw", "ms"],
  617. "tse": ["tse", "sgn"],
  618. "tsm": ["tsm", "sgn"],
  619. "tsq": ["tsq", "sgn"],
  620. "tss": ["tss", "sgn"],
  621. "tsy": ["tsy", "sgn"],
  622. "tza": ["tza", "sgn"],
  623. "ugn": ["ugn", "sgn"],
  624. "ugy": ["ugy", "sgn"],
  625. "ukl": ["ukl", "sgn"],
  626. "uks": ["uks", "sgn"],
  627. "urk": ["urk", "ms"],
  628. "uzn": ["uzn", "uz"],
  629. "uzs": ["uzs", "uz"],
  630. "vgt": ["vgt", "sgn"],
  631. "vkk": ["vkk", "ms"],
  632. "vkt": ["vkt", "ms"],
  633. "vsi": ["vsi", "sgn"],
  634. "vsl": ["vsl", "sgn"],
  635. "vsv": ["vsv", "sgn"],
  636. "wbs": ["wbs", "sgn"],
  637. "wuu": ["wuu", "zh"],
  638. "xki": ["xki", "sgn"],
  639. "xml": ["xml", "sgn"],
  640. "xmm": ["xmm", "ms"],
  641. "xms": ["xms", "sgn"],
  642. "yds": ["yds", "sgn"],
  643. "ygs": ["ygs", "sgn"],
  644. "yhs": ["yhs", "sgn"],
  645. "ysl": ["ysl", "sgn"],
  646. "yue": ["yue", "zh"],
  647. "zib": ["zib", "sgn"],
  648. "zlm": ["zlm", "ms"],
  649. "zmi": ["zmi", "ms"],
  650. "zsl": ["zsl", "sgn"],
  651. "zsm": ["zsm", "ms"],
  652. };
  653. /**
  654. * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags.
  655. *
  656. * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3.
  657. * Spec: RFC 5646, section 4.5.
  658. */
  659. function canonicalizeLanguageTag(locale) {
  660. // start with lower case for easier processing, and because most subtags will need to be lower case anyway
  661. locale = locale.toLowerCase();
  662. // handle mappings for complete tags
  663. if (__tagMappings.hasOwnProperty(locale)) {
  664. return __tagMappings[locale];
  665. }
  666. var subtags = locale.split("-");
  667. var i = 0;
  668. // handle standard part: all subtags before first singleton or "x"
  669. while (i < subtags.length) {
  670. var subtag = subtags[i];
  671. if (subtag.length === 1 && (i > 0 || subtag === "x")) {
  672. break;
  673. } else if (i !== 0 && subtag.length === 2) {
  674. subtag = subtag.toUpperCase();
  675. } else if (subtag.length === 4) {
  676. subtag = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase();
  677. }
  678. if (__subtagMappings.hasOwnProperty(subtag)) {
  679. subtag = __subtagMappings[subtag];
  680. } else if (__extlangMappings.hasOwnProperty(subtag)) {
  681. subtag = __extlangMappings[subtag][0];
  682. if (i === 1 && __extlangMappings[subtag][1] === subtags[0]) {
  683. subtags.shift();
  684. i--;
  685. }
  686. }
  687. subtags[i] = subtag;
  688. i++;
  689. }
  690. var normal = subtags.slice(0, i).join("-");
  691. // handle extensions
  692. var extensions = [];
  693. while (i < subtags.length && subtags[i] !== "x") {
  694. var extensionStart = i;
  695. i++;
  696. while (i < subtags.length && subtags[i].length > 1) {
  697. i++;
  698. }
  699. var extension = subtags.slice(extensionStart, i).join("-");
  700. extensions.push(extension);
  701. }
  702. extensions.sort();
  703. // handle private use
  704. var privateUse;
  705. if (i < subtags.length) {
  706. privateUse = subtags.slice(i).join("-");
  707. }
  708. // put everything back together
  709. var canonical = normal;
  710. if (extensions.length > 0) {
  711. canonical += "-" + extensions.join("-");
  712. }
  713. if (privateUse !== undefined) {
  714. if (canonical.length > 0) {
  715. canonical += "-" + privateUse;
  716. } else {
  717. canonical = privateUse;
  718. }
  719. }
  720. return canonical;
  721. }
  722. return typeof locale === "string" && isStructurallyValidLanguageTag(locale) &&
  723. canonicalizeLanguageTag(locale) === locale;
  724. }
  725. /**
  726. * Returns an array of error cases handled by CanonicalizeLocaleList().
  727. */
  728. function getInvalidLocaleArguments() {
  729. function CustomError() {}
  730. var topLevelErrors = [
  731. // fails ToObject
  732. [null, TypeError],
  733. // fails Get
  734. [{ get length() { throw new CustomError(); } }, CustomError],
  735. // fail ToLength
  736. [{ length: Symbol.toPrimitive }, TypeError],
  737. [{ length: { get [Symbol.toPrimitive]() { throw new CustomError(); } } }, CustomError],
  738. [{ length: { [Symbol.toPrimitive]() { throw new CustomError(); } } }, CustomError],
  739. [{ length: { get valueOf() { throw new CustomError(); } } }, CustomError],
  740. [{ length: { valueOf() { throw new CustomError(); } } }, CustomError],
  741. [{ length: { get toString() { throw new CustomError(); } } }, CustomError],
  742. [{ length: { toString() { throw new CustomError(); } } }, CustomError],
  743. // fail type check
  744. [[undefined], TypeError],
  745. [[null], TypeError],
  746. [[true], TypeError],
  747. [[Symbol.toPrimitive], TypeError],
  748. [[1], TypeError],
  749. [[0.1], TypeError],
  750. [[NaN], TypeError],
  751. ];
  752. var invalidLanguageTags = [
  753. "", // empty tag
  754. "i", // singleton alone
  755. "x", // private use without subtag
  756. "u", // extension singleton in first place
  757. "419", // region code in first place
  758. "u-nu-latn-cu-bob", // extension sequence without language
  759. "hans-cmn-cn", // "hans" could theoretically be a 4-letter language code,
  760. // but those can't be followed by extlang codes.
  761. "cmn-hans-cn-u-u", // duplicate singleton
  762. "cmn-hans-cn-t-u-ca-u", // duplicate singleton
  763. "de-gregory-gregory", // duplicate variant
  764. "*", // language range
  765. "de-*", // language range
  766. "中文", // non-ASCII letters
  767. "en-ß", // non-ASCII letters
  768. "ıd" // non-ASCII letters
  769. ];
  770. return topLevelErrors.concat(
  771. invalidLanguageTags.map(tag => [tag, RangeError]),
  772. invalidLanguageTags.map(tag => [[tag], RangeError]),
  773. invalidLanguageTags.map(tag => [["en", tag], RangeError]),
  774. )
  775. }
  776. /**
  777. * Tests whether the named options property is correctly handled by the given constructor.
  778. * @param {object} Constructor the constructor to test.
  779. * @param {string} property the name of the options property to test.
  780. * @param {string} type the type that values of the property are expected to have
  781. * @param {Array} [values] an array of allowed values for the property. Not needed for boolean.
  782. * @param {any} fallback the fallback value that the property assumes if not provided.
  783. * @param {object} testOptions additional options:
  784. * @param {boolean} isOptional whether support for this property is optional for implementations.
  785. * @param {boolean} noReturn whether the resulting value of the property is not returned.
  786. * @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent.
  787. * @param {object} extra additional option to pass along, properties are value -> {option: value}.
  788. */
  789. function testOption(Constructor, property, type, values, fallback, testOptions) {
  790. var isOptional = testOptions !== undefined && testOptions.isOptional === true;
  791. var noReturn = testOptions !== undefined && testOptions.noReturn === true;
  792. var isILD = testOptions !== undefined && testOptions.isILD === true;
  793. function addExtraOptions(options, value, testOptions) {
  794. if (testOptions !== undefined && testOptions.extra !== undefined) {
  795. var extra;
  796. if (value !== undefined && testOptions.extra[value] !== undefined) {
  797. extra = testOptions.extra[value];
  798. } else if (testOptions.extra.any !== undefined) {
  799. extra = testOptions.extra.any;
  800. }
  801. if (extra !== undefined) {
  802. Object.getOwnPropertyNames(extra).forEach(function (prop) {
  803. options[prop] = extra[prop];
  804. });
  805. }
  806. }
  807. }
  808. var testValues, options, obj, expected, actual, error;
  809. // test that the specified values are accepted. Also add values that convert to specified values.
  810. if (type === "boolean") {
  811. if (values === undefined) {
  812. values = [true, false];
  813. }
  814. testValues = values.slice(0);
  815. testValues.push(888);
  816. testValues.push(0);
  817. } else if (type === "string") {
  818. testValues = values.slice(0);
  819. testValues.push({toString: function () { return values[0]; }});
  820. }
  821. testValues.forEach(function (value) {
  822. options = {};
  823. options[property] = value;
  824. addExtraOptions(options, value, testOptions);
  825. obj = new Constructor(undefined, options);
  826. if (noReturn) {
  827. if (obj.resolvedOptions().hasOwnProperty(property)) {
  828. $ERROR("Option property " + property + " is returned, but shouldn't be.");
  829. }
  830. } else {
  831. actual = obj.resolvedOptions()[property];
  832. if (isILD) {
  833. if (actual !== undefined && values.indexOf(actual) === -1) {
  834. $ERROR("Invalid value " + actual + " returned for property " + property + ".");
  835. }
  836. } else {
  837. if (type === "boolean") {
  838. expected = Boolean(value);
  839. } else if (type === "string") {
  840. expected = String(value);
  841. }
  842. if (actual !== expected && !(isOptional && actual === undefined)) {
  843. $ERROR("Option value " + value + " for property " + property +
  844. " was not accepted; got " + actual + " instead.");
  845. }
  846. }
  847. }
  848. });
  849. // test that invalid values are rejected
  850. if (type === "string") {
  851. var invalidValues = ["invalidValue", -1, null];
  852. // assume that we won't have values in caseless scripts
  853. if (values[0].toUpperCase() !== values[0]) {
  854. invalidValues.push(values[0].toUpperCase());
  855. } else {
  856. invalidValues.push(values[0].toLowerCase());
  857. }
  858. invalidValues.forEach(function (value) {
  859. options = {};
  860. options[property] = value;
  861. addExtraOptions(options, value, testOptions);
  862. error = undefined;
  863. try {
  864. obj = new Constructor(undefined, options);
  865. } catch (e) {
  866. error = e;
  867. }
  868. if (error === undefined) {
  869. $ERROR("Invalid option value " + value + " for property " + property + " was not rejected.");
  870. } else if (error.name !== "RangeError") {
  871. $ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + ".");
  872. }
  873. });
  874. }
  875. // test that fallback value or another valid value is used if no options value is provided
  876. if (!noReturn) {
  877. options = {};
  878. addExtraOptions(options, undefined, testOptions);
  879. obj = new Constructor(undefined, options);
  880. actual = obj.resolvedOptions()[property];
  881. if (!(isOptional && actual === undefined)) {
  882. if (fallback !== undefined) {
  883. if (actual !== fallback) {
  884. $ERROR("Option fallback value " + fallback + " for property " + property +
  885. " was not used; got " + actual + " instead.");
  886. }
  887. } else {
  888. if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) {
  889. $ERROR("Invalid value " + actual + " returned for property " + property + ".");
  890. }
  891. }
  892. }
  893. }
  894. }
  895. /**
  896. * Properties of the RegExp constructor that may be affected by use of regular
  897. * expressions, and the default values of these properties. Properties are from
  898. * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Deprecated_and_obsolete_features#RegExp_Properties
  899. */
  900. var regExpProperties = ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9",
  901. "$_", "$*", "$&", "$+", "$`", "$'",
  902. "input", "lastMatch", "lastParen", "leftContext", "rightContext"
  903. ];
  904. var regExpPropertiesDefaultValues = (function () {
  905. var values = Object.create(null);
  906. regExpProperties.forEach(function (property) {
  907. values[property] = RegExp[property];
  908. });
  909. return values;
  910. }());
  911. /**
  912. * Tests that executing the provided function (which may use regular expressions
  913. * in its implementation) does not create or modify unwanted properties on the
  914. * RegExp constructor.
  915. */
  916. function testForUnwantedRegExpChanges(testFunc) {
  917. regExpProperties.forEach(function (property) {
  918. RegExp[property] = regExpPropertiesDefaultValues[property];
  919. });
  920. testFunc();
  921. regExpProperties.forEach(function (property) {
  922. if (RegExp[property] !== regExpPropertiesDefaultValues[property]) {
  923. $ERROR("RegExp has unexpected property " + property + " with value " +
  924. RegExp[property] + ".");
  925. }
  926. });
  927. }
  928. /**
  929. * Tests whether name is a valid BCP 47 numbering system name
  930. * and not excluded from use in the ECMAScript Internationalization API.
  931. * @param {string} name the name to be tested.
  932. * @return {boolean} whether name is a valid BCP 47 numbering system name and
  933. * allowed for use in the ECMAScript Internationalization API.
  934. */
  935. function isValidNumberingSystem(name) {
  936. // source: CLDR file common/bcp47/number.xml; version CLDR 32.
  937. var numberingSystems = [
  938. "adlm",
  939. "ahom",
  940. "arab",
  941. "arabext",
  942. "armn",
  943. "armnlow",
  944. "bali",
  945. "beng",
  946. "bhks",
  947. "brah",
  948. "cakm",
  949. "cham",
  950. "cyrl",
  951. "deva",
  952. "ethi",
  953. "finance",
  954. "fullwide",
  955. "geor",
  956. "gonm",
  957. "grek",
  958. "greklow",
  959. "gujr",
  960. "guru",
  961. "hanidays",
  962. "hanidec",
  963. "hans",
  964. "hansfin",
  965. "hant",
  966. "hantfin",
  967. "hebr",
  968. "hmng",
  969. "java",
  970. "jpan",
  971. "jpanfin",
  972. "kali",
  973. "khmr",
  974. "knda",
  975. "lana",
  976. "lanatham",
  977. "laoo",
  978. "latn",
  979. "lepc",
  980. "limb",
  981. "mathbold",
  982. "mathdbl",
  983. "mathmono",
  984. "mathsanb",
  985. "mathsans",
  986. "mlym",
  987. "modi",
  988. "mong",
  989. "mroo",
  990. "mtei",
  991. "mymr",
  992. "mymrshan",
  993. "mymrtlng",
  994. "native",
  995. "newa",
  996. "nkoo",
  997. "olck",
  998. "orya",
  999. "osma",
  1000. "roman",
  1001. "romanlow",
  1002. "saur",
  1003. "shrd",
  1004. "sind",
  1005. "sinh",
  1006. "sora",
  1007. "sund",
  1008. "takr",
  1009. "talu",
  1010. "taml",
  1011. "tamldec",
  1012. "telu",
  1013. "thai",
  1014. "tirh",
  1015. "tibt",
  1016. "traditio",
  1017. "vaii",
  1018. "wara",
  1019. ];
  1020. var excluded = [
  1021. "finance",
  1022. "native",
  1023. "traditio"
  1024. ];
  1025. return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1;
  1026. }
  1027. /**
  1028. * Provides the digits of numbering systems with simple digit mappings,
  1029. * as specified in 11.3.2.
  1030. */
  1031. var numberingSystemDigits = {
  1032. arab: "٠١٢٣٤٥٦٧٨٩",
  1033. arabext: "۰۱۲۳۴۵۶۷۸۹",
  1034. bali: "\u1B50\u1B51\u1B52\u1B53\u1B54\u1B55\u1B56\u1B57\u1B58\u1B59",
  1035. beng: "০১২৩৪৫৬৭৮৯",
  1036. deva: "०१२३४५६७८९",
  1037. fullwide: "0123456789",
  1038. gujr: "૦૧૨૩૪૫૬૭૮૯",
  1039. guru: "੦੧੨੩੪੫੬੭੮੯",
  1040. hanidec: "〇一二三四五六七八九",
  1041. khmr: "០១២៣៤៥៦៧៨៩",
  1042. knda: "೦೧೨೩೪೫೬೭೮೯",
  1043. laoo: "໐໑໒໓໔໕໖໗໘໙",
  1044. latn: "0123456789",
  1045. limb: "\u1946\u1947\u1948\u1949\u194A\u194B\u194C\u194D\u194E\u194F",
  1046. mlym: "൦൧൨൩൪൫൬൭൮൯",
  1047. mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙",
  1048. mymr: "၀၁၂၃၄၅၆၇၈၉",
  1049. orya: "୦୧୨୩୪୫୬୭୮୯",
  1050. tamldec: "௦௧௨௩௪௫௬௭௮௯",
  1051. telu: "౦౧౨౩౪౫౬౭౮౯",
  1052. thai: "๐๑๒๓๔๕๖๗๘๙",
  1053. tibt: "༠༡༢༣༤༥༦༧༨༩"
  1054. };
  1055. /**
  1056. * Tests that number formatting is handled correctly. The function checks that the
  1057. * digit sequences in formatted output are as specified, converted to the
  1058. * selected numbering system, and embedded in consistent localized patterns.
  1059. * @param {Array} locales the locales to be tested.
  1060. * @param {Array} numberingSystems the numbering systems to be tested.
  1061. * @param {Object} options the options to pass to Intl.NumberFormat. Options
  1062. * must include {useGrouping: false}, and must cause 1.1 to be formatted
  1063. * pre- and post-decimal digits.
  1064. * @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings
  1065. * in unlocalized format with Western digits.
  1066. */
  1067. function testNumberFormat(locales, numberingSystems, options, testData) {
  1068. locales.forEach(function (locale) {
  1069. numberingSystems.forEach(function (numbering) {
  1070. var digits = numberingSystemDigits[numbering];
  1071. var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options);
  1072. function getPatternParts(positive) {
  1073. var n = positive ? 1.1 : -1.1;
  1074. var formatted = format.format(n);
  1075. var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)";
  1076. var match = formatted.match(new RegExp(oneoneRE));
  1077. if (match === null) {
  1078. $ERROR("Unexpected formatted " + n + " for " +
  1079. format.resolvedOptions().locale + " and options " +
  1080. JSON.stringify(options) + ": " + formatted);
  1081. }
  1082. return match;
  1083. }
  1084. function toNumbering(raw) {
  1085. return raw.replace(/[0-9]/g, function (digit) {
  1086. return digits[digit.charCodeAt(0) - "0".charCodeAt(0)];
  1087. });
  1088. }
  1089. function buildExpected(raw, patternParts) {
  1090. var period = raw.indexOf(".");
  1091. if (period === -1) {
  1092. return patternParts[1] + toNumbering(raw) + patternParts[3];
  1093. } else {
  1094. return patternParts[1] +
  1095. toNumbering(raw.substring(0, period)) +
  1096. patternParts[2] +
  1097. toNumbering(raw.substring(period + 1)) +
  1098. patternParts[3];
  1099. }
  1100. }
  1101. if (format.resolvedOptions().numberingSystem === numbering) {
  1102. // figure out prefixes, infixes, suffixes for positive and negative values
  1103. var posPatternParts = getPatternParts(true);
  1104. var negPatternParts = getPatternParts(false);
  1105. Object.getOwnPropertyNames(testData).forEach(function (input) {
  1106. var rawExpected = testData[input];
  1107. var patternParts;
  1108. if (rawExpected[0] === "-") {
  1109. patternParts = negPatternParts;
  1110. rawExpected = rawExpected.substring(1);
  1111. } else {
  1112. patternParts = posPatternParts;
  1113. }
  1114. var expected = buildExpected(rawExpected, patternParts);
  1115. var actual = format.format(input);
  1116. if (actual !== expected) {
  1117. $ERROR("Formatted value for " + input + ", " +
  1118. format.resolvedOptions().locale + " and options " +
  1119. JSON.stringify(options) + " is " + actual + "; expected " + expected + ".");
  1120. }
  1121. });
  1122. }
  1123. });
  1124. });
  1125. }
  1126. /**
  1127. * Return the components of date-time formats.
  1128. * @return {Array} an array with all date-time components.
  1129. */
  1130. function getDateTimeComponents() {
  1131. return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
  1132. }
  1133. /**
  1134. * Return the valid values for the given date-time component, as specified
  1135. * by the table in section 12.1.1.
  1136. * @param {string} component a date-time component.
  1137. * @return {Array} an array with the valid values for the component.
  1138. */
  1139. function getDateTimeComponentValues(component) {
  1140. var components = {
  1141. weekday: ["narrow", "short", "long"],
  1142. era: ["narrow", "short", "long"],
  1143. year: ["2-digit", "numeric"],
  1144. month: ["2-digit", "numeric", "narrow", "short", "long"],
  1145. day: ["2-digit", "numeric"],
  1146. hour: ["2-digit", "numeric"],
  1147. minute: ["2-digit", "numeric"],
  1148. second: ["2-digit", "numeric"],
  1149. timeZoneName: ["short", "long"]
  1150. };
  1151. var result = components[component];
  1152. if (result === undefined) {
  1153. $ERROR("Internal error: No values defined for date-time component " + component + ".");
  1154. }
  1155. return result;
  1156. }
  1157. /**
  1158. * @description Tests whether timeZone is a String value representing a
  1159. * structurally valid and canonicalized time zone name, as defined in
  1160. * sections 6.4.1 and 6.4.2 of the ECMAScript Internationalization API
  1161. * Specification.
  1162. * @param {String} timeZone the string to be tested.
  1163. * @result {Boolean} whether the test succeeded.
  1164. */
  1165. function isCanonicalizedStructurallyValidTimeZoneName(timeZone) {
  1166. /**
  1167. * Regular expression defining IANA Time Zone names.
  1168. *
  1169. * Spec: IANA Time Zone Database, Theory file
  1170. */
  1171. var fileNameComponent = "(?:[A-Za-z_]|\\.(?!\\.?(?:/|$)))[A-Za-z.\\-_]{0,13}";
  1172. var fileName = fileNameComponent + "(?:/" + fileNameComponent + ")*";
  1173. var etcName = "(?:Etc/)?GMT[+-]\\d{1,2}";
  1174. var systemVName = "SystemV/[A-Z]{3}\\d{1,2}(?:[A-Z]{3})?";
  1175. var legacyName = etcName + "|" + systemVName + "|CST6CDT|EST5EDT|MST7MDT|PST8PDT|NZ";
  1176. var zoneNamePattern = new RegExp("^(?:" + fileName + "|" + legacyName + ")$");
  1177. if (typeof timeZone !== "string") {
  1178. return false;
  1179. }
  1180. // 6.4.2 CanonicalizeTimeZoneName (timeZone), step 3
  1181. if (timeZone === "UTC") {
  1182. return true;
  1183. }
  1184. // 6.4.2 CanonicalizeTimeZoneName (timeZone), step 3
  1185. if (timeZone === "Etc/UTC" || timeZone === "Etc/GMT") {
  1186. return false;
  1187. }
  1188. return zoneNamePattern.test(timeZone);
  1189. }