twitter_cldr_js 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f742c2e22d518081be5a938b080d64766cc4a125f55e75f4c8e75c4d2e9f7f14
4
- data.tar.gz: 2168845da6ba9b3a7aee973176a70baa5585a7d58ed1c86b91a6f9b1a717c43e
3
+ metadata.gz: c7fa717f3763a3f4ca2593e5ff7104fea5f2cb56959bc89fc93f93219f34b82a
4
+ data.tar.gz: 2a1a5fe1a2f32f4eaf80f78b5aefe081b6fc85cce453940dfbe254a11d86becf
5
5
  SHA512:
6
- metadata.gz: f85ecdbdc0b8ba32057c1f053636bfa11ff83f9db712a82ebf7da61971b0bc56847c36e5557348d221579c007d2adf923abf9956d8ba2356743d7bceaf403933
7
- data.tar.gz: 4d8022ebbfca34498ae0d62d6d4efc25c6e8f3b928d0a3b92525b2674fb37a632638cef04496cbbc0a8f88957e9095f8d53fc0880587462038432d524a4b60bd
6
+ metadata.gz: 77e9dd6b9439d03d11c26de9027dc7080a5dcbd46519f58623931a864c2f09c11654ff8cc79855eb8e175b329613ea744aa24db235748fa03aff40f610b20040
7
+ data.tar.gz: 30a80da24b9d490da60530f0d0470264f766748d4fc2756235f2ee6edce5bf070831a5ed5c61b08b06bad732aca44f34b34499168fc1ab5a5d8f5f2a912a893f
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ source "https://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  group :test do
6
- gem 'rspec', '~> 2.11.0'
6
+ gem 'rspec', '~> 3.0'
7
7
  gem 'rr', '~> 1.0.4'
8
8
  end
9
9
 
data/README.md CHANGED
@@ -297,7 +297,7 @@ formatter.format(123, 'OrdinalRules', 'digits-ordinal')
297
297
  // '123rd'
298
298
  ```
299
299
 
300
- In comparision, here is what the Spanish formatting looks like
300
+ In comparison, here is what the Spanish formatting looks like
301
301
  ```javascript
302
302
  // include the es data bundle for the Spanish RBNF Formatter
303
303
  var formatter = new TwitterCldr.RBNF()
@@ -397,7 +397,7 @@ TwitterCldr.TerritoriesContainment.contains('419', 'FR') // false
397
397
 
398
398
  ### Unicode Regular Expressions
399
399
 
400
- Unicode regular expressions are an implmentaion of regular expressions that support all Unicode characters in the [BMP](http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane). They provide support for multi-character strings, Unicode character escapes, set operations (unions, intersections, and differences), and character sets.
400
+ Unicode regular expressions are an implementaion of regular expressions that support all Unicode characters in the [BMP](http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane). They provide support for multi-character strings, Unicode character escapes, set operations (unions, intersections, and differences), and character sets.
401
401
 
402
402
  #### Changes to Character Classes
403
403
 
@@ -429,7 +429,7 @@ regex3 = TwitterCldr.UnicodeRegex.compile("[[a-z]-[d-g]]+", "g");
429
429
  //supports the JavaScript RegExp modifiers
430
430
  ```
431
431
 
432
- Once compiled, instances of `UnicodeRegex` behave just like normal javascript regexes and support the `match` method:
432
+ Once compiled, instances of `UnicodeRegex` can be directly used to match against a string:
433
433
 
434
434
  ```javascript
435
435
 
@@ -438,6 +438,15 @@ regex2.match("ABCDfooABC"); // ["ABCD", "ABC"]
438
438
  regex3.match("dog"); // ["o"]
439
439
  ```
440
440
 
441
+ Alternatively, you can convert a `UnicodeRegex` into a native JavaScript regex by calling its `to_regexp` method:
442
+
443
+ ```javascript
444
+
445
+ regex3.to_regexp(); // /(?:[\u0061-\u0063]|[\u0068-\u007a])+/g
446
+ regex3.to_regexp().test("a"); // true
447
+ regex3.to_regexp().test("d"); // false
448
+ ```
449
+
441
450
  Protip: Try to avoid negation in character classes (eg. [^abc] and \P{Lu}) as it tends to negatively affect both performance when constructing regexes as well as matching.
442
451
 
443
452
  ### Text Segmentation
@@ -16,10 +16,11 @@
16
16
  /*_lib/twitter_cldr_*/
17
17
 
18
18
  (function() {
19
- var TwitterCldr, key, obj, root,
19
+ var TwitterCldr, get_with_data_isolation, key, obj, proxy_or_primitive, root, with_data_isolation,
20
20
  __hasProp = {}.hasOwnProperty,
21
21
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
22
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
22
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
23
+ __slice = [].slice;
23
24
 
24
25
  TwitterCldr = {};
25
26
 
@@ -3607,7 +3608,7 @@
3607
3608
  };
3608
3609
 
3609
3610
  NumberFormatter.prototype.format = function(number, options) {
3610
- var fraction, fraction_format, integer_format, intg, key, opts, prefix, result, sign, suffix, tokens, tokens_sample, truncated_number, val, _ref, _ref1;
3611
+ var fraction, fraction_format, integer_format, intg, key, number_part, opts, prefix, result, rounded_num, sign, suffix, tokens, tokens_sample, truncated_number, val, _ref, _ref1;
3611
3612
  if (options == null) {
3612
3613
  options = {};
3613
3614
  }
@@ -3616,27 +3617,38 @@
3616
3617
  val = options[key];
3617
3618
  opts[key] = options[key] != null ? options[key] : opts[key];
3618
3619
  }
3620
+ number = this.round_to(number, opts.precision || 0);
3619
3621
  tokens = this.get_tokens(number, opts);
3620
3622
  if (tokens != null) {
3621
3623
  if (!(tokens instanceof Array)) {
3622
3624
  tokens_sample = tokens[Object.keys(tokens)[0]];
3623
3625
  truncated_number = this.truncate_number(number, tokens_sample[1].length);
3626
+ rounded_num = Number(this.parse_number(truncated_number, opts)[0]) * Number(this.get_key(number)) / (Math.pow(10, tokens_sample[1].length - 1));
3627
+ if (!opts._stack_depth && this.get_key(rounded_num) !== this.get_key(number)) {
3628
+ return this.format(rounded_num, Object.assign(opts, {
3629
+ _stack_depth: 1
3630
+ }));
3631
+ }
3624
3632
  if (opts.precision === 0) {
3625
3633
  truncated_number = Math.floor(truncated_number);
3626
3634
  }
3627
3635
  tokens = tokens[TwitterCldr.PluralRules.rule_for(truncated_number)];
3628
3636
  }
3629
3637
  _ref = this.partition_tokens(tokens), prefix = _ref[0], suffix = _ref[1], integer_format = _ref[2], fraction_format = _ref[3];
3630
- number = this.truncate_number(number, integer_format.format.length);
3631
- _ref1 = this.parse_number(number, opts), intg = _ref1[0], fraction = _ref1[1];
3638
+ number_part = this.truncate_number(number, integer_format.format.length);
3639
+ _ref1 = this.parse_number(number_part, opts), intg = _ref1[0], fraction = _ref1[1];
3632
3640
  result = integer_format.apply(parseFloat(intg), opts);
3633
3641
  if (fraction) {
3634
3642
  result += fraction_format.apply(fraction, opts);
3635
3643
  }
3636
- sign = number < 0 && prefix !== "-" ? this.symbols().minus_sign || this.default_symbols.minus_sign : "";
3644
+ sign = number_part < 0 && prefix !== "-" ? this.symbols().minus_sign || this.default_symbols.minus_sign : "";
3637
3645
  return "" + prefix + result + suffix;
3638
3646
  } else {
3639
- return number.toString();
3647
+ if (!(this instanceof TwitterCldr.DecimalFormatter)) {
3648
+ return new TwitterCldr.DecimalFormatter().format(number, opts);
3649
+ } else {
3650
+ return number.toString();
3651
+ }
3640
3652
  }
3641
3653
  };
3642
3654
 
@@ -3679,7 +3691,7 @@
3679
3691
  };
3680
3692
 
3681
3693
  NumberFormatter.prototype.get_tokens = function() {
3682
- throw "get_tokens() not implemented - use a derived class like PercentFormatter.";
3694
+ throw new Error("get_tokens() not implemented - use a derived class like PercentFormatter.");
3683
3695
  };
3684
3696
 
3685
3697
  return NumberFormatter;
@@ -3746,7 +3758,7 @@
3746
3758
  return result;
3747
3759
  } catch (_error) {
3748
3760
  error = _error;
3749
- return number;
3761
+ return number.toString();
3750
3762
  }
3751
3763
  };
3752
3764
 
@@ -5447,6 +5459,292 @@
5447
5459
 
5448
5460
  })();
5449
5461
 
5462
+ with_data_isolation = function(data) {
5463
+ return function(fn) {
5464
+ var original, result;
5465
+ original = TwitterCldr.data;
5466
+ TwitterCldr.data = data;
5467
+ result = fn();
5468
+ TwitterCldr.data = original;
5469
+ return result;
5470
+ };
5471
+ };
5472
+
5473
+ get_with_data_isolation = function(data) {
5474
+ return function(target, property_key, receiver) {
5475
+ var isolate_data;
5476
+ isolate_data = with_data_isolation(data);
5477
+ return isolate_data(function() {
5478
+ var fn, result;
5479
+ result = Reflect.get(target, property_key, receiver);
5480
+ if (typeof result === 'function') {
5481
+ fn = result;
5482
+ result = function() {
5483
+ var args;
5484
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
5485
+ return isolate_data(function() {
5486
+ return Reflect.apply(fn, target, args);
5487
+ });
5488
+ };
5489
+ }
5490
+ return result;
5491
+ });
5492
+ };
5493
+ };
5494
+
5495
+ proxy_or_primitive = function(value, handlers) {
5496
+ if (value instanceof Object) {
5497
+ return new Proxy(value, handlers);
5498
+ } else {
5499
+ return value;
5500
+ }
5501
+ };
5502
+
5503
+ TwitterCldr.clone = function(data) {
5504
+ var get, isolate_data;
5505
+ if (!data) {
5506
+ throw new Error('Cannot create a clone with no data');
5507
+ }
5508
+ get = get_with_data_isolation(data);
5509
+ isolate_data = with_data_isolation(data);
5510
+ return proxy_or_primitive(root, {
5511
+ get: function(target, property_key, receiver) {
5512
+ switch (property_key) {
5513
+ case 'set_data':
5514
+ return function() {
5515
+ throw new Error('Cannot set data on a TwitterCldr clone');
5516
+ };
5517
+ default:
5518
+ return isolate_data(function() {
5519
+ return proxy_or_primitive(Reflect.get(target, property_key, receiver), {
5520
+ get: get,
5521
+ apply: function() {
5522
+ var args;
5523
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
5524
+ return isolate_data(function() {
5525
+ return Reflect.apply.apply(Reflect, args);
5526
+ });
5527
+ },
5528
+ construct: function() {
5529
+ var args;
5530
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
5531
+ return isolate_data(function() {
5532
+ return proxy_or_primitive(Reflect.construct.apply(Reflect, args), {
5533
+ get: get
5534
+ });
5535
+ });
5536
+ }
5537
+ });
5538
+ });
5539
+ }
5540
+ },
5541
+ set: function() {
5542
+ throw new Error('Cannot set properties on a TwitterCldr clone');
5543
+ }
5544
+ });
5545
+ };
5546
+
5547
+ TwitterCldr.get_available_locales = function() {
5548
+ return [
5549
+ {
5550
+ "locale": "af",
5551
+ "file_name": "af"
5552
+ }, {
5553
+ "locale": "ar",
5554
+ "file_name": "ar"
5555
+ }, {
5556
+ "locale": "be",
5557
+ "file_name": "be"
5558
+ }, {
5559
+ "locale": "bg",
5560
+ "file_name": "bg"
5561
+ }, {
5562
+ "locale": "bn",
5563
+ "file_name": "bn"
5564
+ }, {
5565
+ "locale": "ca",
5566
+ "file_name": "ca"
5567
+ }, {
5568
+ "locale": "cs",
5569
+ "file_name": "cs"
5570
+ }, {
5571
+ "locale": "cy",
5572
+ "file_name": "cy"
5573
+ }, {
5574
+ "locale": "da",
5575
+ "file_name": "da"
5576
+ }, {
5577
+ "locale": "de",
5578
+ "file_name": "de"
5579
+ }, {
5580
+ "locale": "de-CH",
5581
+ "file_name": "de-CH"
5582
+ }, {
5583
+ "locale": "el",
5584
+ "file_name": "el"
5585
+ }, {
5586
+ "locale": "en",
5587
+ "file_name": "en"
5588
+ }, {
5589
+ "locale": "en-150",
5590
+ "file_name": "en-150"
5591
+ }, {
5592
+ "locale": "en-AU",
5593
+ "file_name": "en-AU"
5594
+ }, {
5595
+ "locale": "en-CA",
5596
+ "file_name": "en-CA"
5597
+ }, {
5598
+ "locale": "en-GB",
5599
+ "file_name": "en-GB"
5600
+ }, {
5601
+ "locale": "en-IE",
5602
+ "file_name": "en-IE"
5603
+ }, {
5604
+ "locale": "en-SG",
5605
+ "file_name": "en-SG"
5606
+ }, {
5607
+ "locale": "en-ZA",
5608
+ "file_name": "en-ZA"
5609
+ }, {
5610
+ "locale": "es",
5611
+ "file_name": "es"
5612
+ }, {
5613
+ "locale": "es-419",
5614
+ "file_name": "es-419"
5615
+ }, {
5616
+ "locale": "es-CO",
5617
+ "file_name": "es-CO"
5618
+ }, {
5619
+ "locale": "es-MX",
5620
+ "file_name": "es-MX"
5621
+ }, {
5622
+ "locale": "es-US",
5623
+ "file_name": "es-US"
5624
+ }, {
5625
+ "locale": "eu",
5626
+ "file_name": "eu"
5627
+ }, {
5628
+ "locale": "fa",
5629
+ "file_name": "fa"
5630
+ }, {
5631
+ "locale": "fi",
5632
+ "file_name": "fi"
5633
+ }, {
5634
+ "locale": "fil",
5635
+ "file_name": "fil"
5636
+ }, {
5637
+ "locale": "fr",
5638
+ "file_name": "fr"
5639
+ }, {
5640
+ "locale": "fr-BE",
5641
+ "file_name": "fr-BE"
5642
+ }, {
5643
+ "locale": "fr-CA",
5644
+ "file_name": "fr-CA"
5645
+ }, {
5646
+ "locale": "fr-CH",
5647
+ "file_name": "fr-CH"
5648
+ }, {
5649
+ "locale": "ga",
5650
+ "file_name": "ga"
5651
+ }, {
5652
+ "locale": "gl",
5653
+ "file_name": "gl"
5654
+ }, {
5655
+ "locale": "he",
5656
+ "file_name": "he"
5657
+ }, {
5658
+ "locale": "hi",
5659
+ "file_name": "hi"
5660
+ }, {
5661
+ "locale": "hr",
5662
+ "file_name": "hr"
5663
+ }, {
5664
+ "locale": "hu",
5665
+ "file_name": "hu"
5666
+ }, {
5667
+ "locale": "id",
5668
+ "file_name": "id"
5669
+ }, {
5670
+ "locale": "is",
5671
+ "file_name": "is"
5672
+ }, {
5673
+ "locale": "it",
5674
+ "file_name": "it"
5675
+ }, {
5676
+ "locale": "it-CH",
5677
+ "file_name": "it-CH"
5678
+ }, {
5679
+ "locale": "ja",
5680
+ "file_name": "ja"
5681
+ }, {
5682
+ "locale": "ko",
5683
+ "file_name": "ko"
5684
+ }, {
5685
+ "locale": "lv",
5686
+ "file_name": "lv"
5687
+ }, {
5688
+ "locale": "ms",
5689
+ "file_name": "msa"
5690
+ }, {
5691
+ "locale": "nb",
5692
+ "file_name": "no"
5693
+ }, {
5694
+ "locale": "nl",
5695
+ "file_name": "nl"
5696
+ }, {
5697
+ "locale": "pl",
5698
+ "file_name": "pl"
5699
+ }, {
5700
+ "locale": "pt",
5701
+ "file_name": "pt"
5702
+ }, {
5703
+ "locale": "ro",
5704
+ "file_name": "ro"
5705
+ }, {
5706
+ "locale": "ru",
5707
+ "file_name": "ru"
5708
+ }, {
5709
+ "locale": "sk",
5710
+ "file_name": "sk"
5711
+ }, {
5712
+ "locale": "sq",
5713
+ "file_name": "sq"
5714
+ }, {
5715
+ "locale": "sr",
5716
+ "file_name": "sr"
5717
+ }, {
5718
+ "locale": "sv",
5719
+ "file_name": "sv"
5720
+ }, {
5721
+ "locale": "ta",
5722
+ "file_name": "ta"
5723
+ }, {
5724
+ "locale": "th",
5725
+ "file_name": "th"
5726
+ }, {
5727
+ "locale": "tr",
5728
+ "file_name": "tr"
5729
+ }, {
5730
+ "locale": "uk",
5731
+ "file_name": "uk"
5732
+ }, {
5733
+ "locale": "ur",
5734
+ "file_name": "ur"
5735
+ }, {
5736
+ "locale": "vi",
5737
+ "file_name": "vi"
5738
+ }, {
5739
+ "locale": "zh",
5740
+ "file_name": "zh-cn"
5741
+ }, {
5742
+ "locale": "zh-Hant",
5743
+ "file_name": "zh-tw"
5744
+ }
5745
+ ];
5746
+ };
5747
+
5450
5748
  TwitterCldr.set_data = function(bundle) {
5451
5749
  TwitterCldr.data = bundle;
5452
5750
  return null;
@@ -89,6 +89,16 @@ module TwitterCldr
89
89
  def compile_implementation(options = {})
90
90
  bundle = TwitterCldr::Js::Renderers::Bundle.new
91
91
  bundle[:locale] = TwitterCldr::DEFAULT_LOCALE
92
+
93
+ available_locales = @locales.map do |locale|
94
+ {
95
+ locale: locale,
96
+ file_name: TwitterCldr.twitter_locale(locale),
97
+ }
98
+ end
99
+
100
+ bundle[:available_locales] = available_locales.sort_by { |x| x[:locale] }.to_json
101
+
92
102
  file = compile_bundle(bundle, @features, implementation_renderers, options)
93
103
 
94
104
  file.source
@@ -152,7 +162,8 @@ module TwitterCldr
152
162
  :plural => TwitterCldr::Js::Renderers::Implementation::Numbers::RBNF::PluralRenderer,
153
163
  :range => TwitterCldr::Js::Renderers::Implementation::Utils::RangeRenderer,
154
164
  :range_set => TwitterCldr::Js::Renderers::Implementation::Utils::RangeSetRenderer,
155
- :code_points => TwitterCldr::Js::Renderers::Implementation::Utils::CodePointsRenderer
165
+ :code_points => TwitterCldr::Js::Renderers::Implementation::Utils::CodePointsRenderer,
166
+ :clone => TwitterCldr::Js::Renderers::Implementation::Clone::CloneRenderer,
156
167
  }
157
168
  end
158
169
 
@@ -23,6 +23,8 @@ TwitterCldr = {}
23
23
  {{> utilities}}
24
24
  {{{contents}}}
25
25
 
26
+ TwitterCldr.get_available_locales = -> {{{available_locales}}}
27
+
26
28
  TwitterCldr.set_data = (bundle) ->
27
29
  TwitterCldr.data = bundle
28
30
  null
@@ -0,0 +1,58 @@
1
+ with_data_isolation = (data) -> (fn) ->
2
+ original = TwitterCldr.data
3
+ TwitterCldr.data = data
4
+ result = fn()
5
+ TwitterCldr.data = original
6
+ result
7
+
8
+ get_with_data_isolation = (data) -> (target, property_key, receiver) ->
9
+ isolate_data = with_data_isolation(data)
10
+
11
+ isolate_data(->
12
+ result = Reflect.get(target, property_key, receiver)
13
+
14
+ if typeof result == 'function'
15
+ fn = result
16
+
17
+ result = (args...) -> isolate_data(-> Reflect.apply(fn, target, args))
18
+
19
+ result
20
+ )
21
+
22
+ proxy_or_primitive = (value, handlers) ->
23
+ if value instanceof Object
24
+ new Proxy(value, handlers)
25
+ else
26
+ value
27
+
28
+ TwitterCldr.clone = (data) ->
29
+ if !data
30
+ throw new Error('Cannot create a clone with no data')
31
+
32
+ get = get_with_data_isolation(data)
33
+ isolate_data = with_data_isolation(data)
34
+
35
+ proxy_or_primitive(
36
+ root,
37
+ get: (target, property_key, receiver) ->
38
+ switch property_key
39
+ when 'set_data'
40
+ return -> throw new Error('Cannot set data on a TwitterCldr clone')
41
+ else
42
+ isolate_data(
43
+ -> proxy_or_primitive(
44
+ Reflect.get(target, property_key, receiver), {
45
+ get,
46
+ apply: (args...) ->
47
+ isolate_data(
48
+ -> Reflect.apply(args...)
49
+ )
50
+ construct: (args...) ->
51
+ isolate_data(
52
+ -> proxy_or_primitive(Reflect.construct(args...), { get })
53
+ )
54
+ }
55
+ )
56
+ )
57
+ set: () -> throw new Error('Cannot set properties on a TwitterCldr clone')
58
+ )
@@ -21,30 +21,47 @@ class TwitterCldr.NumberFormatter
21
21
  @constructor.data().symbols
22
22
 
23
23
  format: (number, options = {}) ->
24
- opts = this.default_format_options_for(number)
24
+ opts = @default_format_options_for(number)
25
25
 
26
26
  for key, val of options
27
27
  opts[key] = if options[key]? then options[key] else opts[key]
28
28
 
29
- tokens = this.get_tokens(number, opts)
29
+ number = @round_to(number, opts.precision || 0)
30
+
31
+ tokens = @get_tokens(number, opts)
30
32
 
31
33
  if tokens?
32
34
  if tokens not instanceof Array
33
35
  tokens_sample = tokens[Object.keys(tokens)[0]]
34
36
  truncated_number = @truncate_number(number, tokens_sample[1].length)
37
+
38
+ rounded_num = Number(@parse_number(truncated_number, opts)[0]) * Number(@get_key(number)) / (10 ** (tokens_sample[1].length - 1))
39
+
40
+ # Checking _stack_depth should be a redundant condition, as
41
+ # @get_key(rounded_num) should always equal @get_key(number) the second
42
+ # time around. We only check it to prevent infinite loops in case of
43
+ # bugs
44
+ if !opts._stack_depth && @get_key(rounded_num) != @get_key(number)
45
+ return @format(rounded_num, Object.assign(opts, { _stack_depth: 1 }))
46
+
35
47
  truncated_number = Math.floor(truncated_number) if opts.precision == 0
36
48
  tokens = tokens[TwitterCldr.PluralRules.rule_for(truncated_number)]
37
49
 
38
- [prefix, suffix, integer_format, fraction_format] = this.partition_tokens(tokens)
39
- number = this.truncate_number(number, integer_format.format.length)
40
- [intg, fraction] = this.parse_number(number, opts)
50
+ [prefix, suffix, integer_format, fraction_format] = @partition_tokens(tokens)
51
+ number_part = @truncate_number(number, integer_format.format.length)
52
+ [intg, fraction] = @parse_number(number_part, opts)
41
53
  result = integer_format.apply(parseFloat(intg), opts)
42
54
  result += fraction_format.apply(fraction, opts) if fraction
43
- sign = if number < 0 && prefix != "-" then @symbols().minus_sign || @default_symbols.minus_sign else ""
55
+ sign = if number_part < 0 && prefix != "-" then @symbols().minus_sign || @default_symbols.minus_sign else ""
44
56
  "#{prefix}#{result}#{suffix}"
45
57
  else
46
- # there's no specific formatting pattern for this number in current locale
47
- number.toString()
58
+ unless @ instanceof TwitterCldr.DecimalFormatter
59
+ # No specific formatting pattern for this number in current locale, so
60
+ # we fall back to DecimalFormatter
61
+ new TwitterCldr.DecimalFormatter().format(number, opts)
62
+ else
63
+ # Use default toString if current instance is already a DecimalFormatter
64
+ number.toString()
48
65
 
49
66
  truncate_number: (number, decimal_digits) ->
50
67
  number # noop for base class
@@ -75,7 +92,7 @@ class TwitterCldr.NumberFormatter
75
92
  Math.round(number * factor) / factor
76
93
 
77
94
  get_tokens: ->
78
- throw "get_tokens() not implemented - use a derived class like PercentFormatter."
95
+ throw new Error("get_tokens() not implemented - use a derived class like PercentFormatter.")
79
96
 
80
97
  class TwitterCldr.PercentFormatter extends TwitterCldr.NumberFormatter
81
98
  constructor: (options = {}) ->
@@ -106,7 +123,7 @@ class TwitterCldr.DecimalFormatter extends TwitterCldr.NumberFormatter
106
123
  result = transliterator.transliterate(result)
107
124
  result
108
125
  catch error
109
- number
126
+ number.toString()
110
127
 
111
128
  default_format_options_for: (number) ->
112
129
  precision: this.precision_from(number)
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright 2012 Twitter, Inc
4
+ # http://www.apache.org/licenses/LICENSE-2.0
5
+
6
+ module TwitterCldr
7
+ module Js
8
+ module Renderers
9
+ module Implementation
10
+ module Clone
11
+
12
+ class CloneRenderer < TwitterCldr::Js::Renderers::Base
13
+ set_template "mustache/implementation/clone/clone.coffee"
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -19,6 +19,7 @@ module TwitterCldr
19
19
  autoload :Numbers, 'twitter_cldr/js/renderers/implementation/numbers'
20
20
  autoload :Parsers, 'twitter_cldr/js/renderers/implementation/parsers'
21
21
  autoload :Tokenizers, 'twitter_cldr/js/renderers/implementation/tokenizers'
22
+ autoload :Clone, 'twitter_cldr/js/renderers/implementation/clone'
22
23
 
23
24
  module Shared
24
25
  autoload :BidiRenderer, 'twitter_cldr/js/renderers/implementation/shared/bidi_renderer'
@@ -5,6 +5,6 @@
5
5
 
6
6
  module TwitterCldr
7
7
  module Js
8
- VERSION = "3.2.0"
8
+ VERSION = "3.3.0"
9
9
  end
10
10
  end
@@ -0,0 +1,234 @@
1
+ const locales = "en,ar,ko,ru,hi".split(",");
2
+ const date = new Date(2000, 1, 1);
3
+
4
+ // Updated version of
5
+ // https://github.com/twitter/twitter-cldr-npm/blob/master/twitter_cldr.js
6
+ // that clones by default
7
+ const load = function (locale, options) {
8
+ var defaultOptions = { clone: true };
9
+ options = options || {};
10
+
11
+ for (var k in defaultOptions) {
12
+ if (!(k in options)) {
13
+ options[k] = defaultOptions[k];
14
+ }
15
+ }
16
+
17
+ var TwitterCldr = require("../../../lib/assets/javascripts/twitter_cldr/core");
18
+ var data = require("../../../lib/assets/javascripts/twitter_cldr/" + locale);
19
+
20
+ if (options.clone) {
21
+ return TwitterCldr.clone(data);
22
+ } else {
23
+ TwitterCldr.set_data(data);
24
+ return TwitterCldr;
25
+ }
26
+ };
27
+
28
+ const checkDataIsolation = ({ class: klass, ctorParams, methods, staticMethods, loadOptions }) => {
29
+ methods = methods || {}
30
+ staticMethods = staticMethods || {}
31
+ ctorParams = ctorParams || []
32
+
33
+ describe(klass, () => {
34
+ const allMethods = [
35
+ ...(Object.entries(methods)).map(x => [false, ...x]),
36
+ ...(Object.entries(staticMethods)).map(x => [true, ...x]),
37
+ ]
38
+
39
+ for (let [static, name, { params, compare }] of allMethods) {
40
+ params = params || []
41
+ compare = compare || ((x) => x)
42
+
43
+ describe(`${static ? "." : "#"}${name}`, () => {
44
+ const twitterCldrs = locales.map((locale) => {
45
+ const params = [locale, loadOptions].filter((x) => x != null);
46
+ const TwitterCldr = load(...params);
47
+
48
+ return { locale, TwitterCldr };
49
+ });
50
+
51
+ const formatters = twitterCldrs.map(({ locale, TwitterCldr }) => {
52
+ const formatter = static ? TwitterCldr[klass] : new TwitterCldr[klass](...ctorParams)
53
+
54
+ return { locale, formatter };
55
+ });
56
+
57
+ const formatFns = formatters.map(({ locale, formatter }) => ({
58
+ locale,
59
+ format: formatter[name].bind(formatter),
60
+ }));
61
+
62
+ const results = formatFns.map(({ locale, format }) => ({
63
+ locale,
64
+ result: compare(format(...params)),
65
+ }));
66
+
67
+ it(`sanity check - result for same locale is equal`, () => {
68
+ const { formatter } = formatters[0];
69
+ const format = formatter[name].bind(formatter);
70
+
71
+ expect(compare(format(...params))).toBe(compare(format(...params)));
72
+ });
73
+
74
+ for (const [idx, { locale, result }] of results.entries()) {
75
+ const prevs = results.slice(0, idx);
76
+
77
+ if (!prevs.length) continue;
78
+
79
+ for (const prev of prevs) {
80
+ it(`result for "${locale}" differs from result for "${prev.locale}"`, () => {
81
+ expect(result).not.toBe(prev.result);
82
+ });
83
+ }
84
+ }
85
+ });
86
+ }
87
+ });
88
+ };
89
+
90
+ const tests = [
91
+ {
92
+ class: "DateTimeFormatter",
93
+ methods: {
94
+ format: { params: [date, { type: "full" }] },
95
+ weekday_local_stand_alone: { params: [date, "cc", 2] },
96
+ },
97
+ },
98
+ {
99
+ class: "Languages",
100
+ staticMethods: {
101
+ from_code: { params: ["en"] },
102
+ all: { params: [], compare: (x) => x.ja },
103
+ },
104
+ },
105
+ {
106
+ class: "RBNF",
107
+ methods: {
108
+ format: { params: [1, "SpelloutRules", "spellout-numbering"] },
109
+ },
110
+ }
111
+ ];
112
+
113
+ describe("data isolation", () => {
114
+ for (const loadOptions of [
115
+ undefined, // no options param supplied
116
+ {}, // empty options - defaults to clone=true
117
+ { clone: true }, // explicitly specify clone=true
118
+ ]) {
119
+ tests.map((x) => ({ ...x, loadOptions })).forEach(checkDataIsolation);
120
+ }
121
+ });
122
+
123
+ describe("clone behavior", () => {
124
+ const TwitterCldr = require("../../../lib/assets/javascripts/twitter_cldr/core");
125
+ const en = require("../../../lib/assets/javascripts/twitter_cldr/en");
126
+ const ja = require("../../../lib/assets/javascripts/twitter_cldr/ja");
127
+
128
+ beforeEach(() => {
129
+ TwitterCldr.set_data(undefined);
130
+ })
131
+
132
+ afterEach(() => {
133
+ TwitterCldr.set_data(undefined);
134
+ });
135
+
136
+ const locale = (clone) => clone.get_data().Settings.locale;
137
+
138
+ it("can be cloned from base that has no data", () => {
139
+ const clone = TwitterCldr.clone(en);
140
+
141
+ expect(locale(clone)).toBe("en");
142
+ expect(TwitterCldr.is_data_set()).toBe(false);
143
+ expect(clone.is_data_set()).toBe(true);
144
+ });
145
+
146
+ it("returns undefined for properties not present on `root`", () => {
147
+ TwitterCldr.set_data(en);
148
+ const clone = TwitterCldr.clone(en);
149
+
150
+ expect(TwitterCldr.data).toBe(undefined);
151
+ expect(clone.data).toBe(undefined);
152
+ });
153
+
154
+ it("doesn't affect data of the base TwitterCldr", () => {
155
+ TwitterCldr.set_data(ja);
156
+ TwitterCldr.clone(en);
157
+
158
+ expect(locale(TwitterCldr)).toBe("ja");
159
+ });
160
+
161
+
162
+ it("isn't affected by data of the base TwitterCldr", () => {
163
+ const clone = TwitterCldr.clone(en);
164
+ TwitterCldr.set_data(ja);
165
+
166
+ expect(locale(clone)).toBe("en");
167
+ });
168
+
169
+ it("can be cloned indefinitely", () => {
170
+ const cloneEn1 = TwitterCldr.clone(en);
171
+ const cloneEn2 = cloneEn1.clone(en);
172
+ const cloneJa = cloneEn2.clone(ja);
173
+
174
+ expect(locale(cloneEn1)).toBe("en");
175
+ expect(locale(cloneEn2)).toBe(locale(cloneEn1));
176
+ expect(locale(cloneJa)).toBe("ja");
177
+ });
178
+
179
+ it("throws if attempting to create a clone with no data", () => {
180
+ expect(() => TwitterCldr.clone()).toThrow();
181
+ expect(() => TwitterCldr.clone(undefined)).toThrow();
182
+ expect(() => TwitterCldr.clone(null)).toThrow();
183
+
184
+ expect(() => TwitterCldr.clone({})).not.toThrow();
185
+ });
186
+
187
+ describe("cloned version throws if attempting to set data", () => {
188
+ const clone = TwitterCldr.clone(en);
189
+
190
+ it("using `set_data`", () => {
191
+ expect(() => clone.set_data({})).toThrow();
192
+ });
193
+
194
+ it("using `data = `", () => {
195
+ expect(() => clone.data = {}).toThrow();
196
+ });
197
+ });
198
+ });
199
+
200
+ describe("load(..., false)", () => {
201
+ describe("continues to use global state", () => {
202
+ const results = locales.map((locale) => {
203
+ const TwitterCldr = load(locale, { clone: false });
204
+ return new TwitterCldr.DateTimeFormatter();
205
+ }).map((formatter) => formatter.format(date, { type: "full" }));
206
+
207
+ describe("DateTimeFormatter", () => {
208
+ it("#format", () => {
209
+ expect(new Set(results).size).toEqual(1);
210
+ });
211
+ });
212
+ });
213
+
214
+ describe("works in environments without `Proxy` support", () => {
215
+ const originalProxy = globalThis.Proxy;
216
+
217
+ beforeEach(() => {
218
+ globalThis.Proxy = undefined;
219
+ });
220
+
221
+ afterEach(() => {
222
+ globalThis.Proxy = originalProxy;
223
+ });
224
+
225
+ describe("DateTimeFormatter", () => {
226
+ it("#weekday_local_stand_alone", () => {
227
+ const TwitterCldr = load("en", { clone: false });
228
+ const formatter = new TwitterCldr.DateTimeFormatter();
229
+
230
+ expect(formatter.weekday_local_stand_alone(date, "cc", 2)).toEqual("Tue");
231
+ });
232
+ });
233
+ });
234
+ });
@@ -0,0 +1,77 @@
1
+ const TwitterCldr = require("../../../lib/assets/javascripts/twitter_cldr/core.js");
2
+
3
+ const testData = {
4
+ Long: {
5
+ "en": [
6
+ // edge cases
7
+ { raw: 999_499, precisions: ["999 thousand", "999.5 thousand"] },
8
+ { raw: 999_500, precisions: ["1 million", "999.5 thousand"] },
9
+ { raw: 1_000_000, precisions: ["1 million", "1.0 million"] },
10
+
11
+ // fractional
12
+ { raw: 999.49, precisions: ["999", "999.5"], default: "999.49" },
13
+ { raw: 999.5, precisions: ["1 thousand", "999.5"], default: "999.5" },
14
+
15
+ // negative
16
+ { raw: -1_000_000, precisions: ["-1,000,000"] },
17
+ { raw: -999_500, precisions: ["-999,500"] },
18
+ ],
19
+ "zh-cn": [
20
+ // edge cases
21
+ { raw: 9499, precisions: ["9千", "9.5千"] },
22
+ { raw: 9500, precisions: ["1万", "9.5千"] },
23
+ { raw: 10_000, precisions: ["1万", "1.0万"] },
24
+ ],
25
+ "es": [
26
+ // pluralization
27
+ { raw: 1e3, precisions: ["1 mil", "1,0 mil"] },
28
+ { raw: 2e3, precisions: ["2 mil", "2,0 mil"] },
29
+ { raw: 1e6, precisions: ["1 millón", "1,0 millón"] },
30
+ { raw: 2e6, precisions: ["2 millones", "2,0 millones"] },
31
+ { raw: 1e9, precisions: ["1 mil millones", "1,0 mil millones"] },
32
+
33
+ // fractional
34
+ { raw: 999.49, precisions: ["999", "999,5"], default: "999,49" },
35
+ { raw: 999.5, precisions: ["1 mil", "999,5"], default: "999,5" },
36
+
37
+ // negative
38
+ { raw: -1_000_000, precisions: ["-1.000.000"] },
39
+ { raw: -999_500, precisions: ["-999.500"] },
40
+ ],
41
+ },
42
+ Short: {
43
+ "en": [
44
+ // edge cases
45
+ { raw: 999_499, precisions: ["999K", "999.5K"] },
46
+ { raw: 999_500, precisions: ["1M", "999.5K"] },
47
+ { raw: 1_000_000, precisions: ["1M", "1.0M"] },
48
+ ],
49
+ },
50
+ };
51
+
52
+ for (const [kind, locales] of Object.entries(testData)) {
53
+ for (const [localeFileName, tests] of Object.entries(locales)) {
54
+ describe(`${kind}DecimalFormatter (${localeFileName})`, () => {
55
+ let formatter;
56
+
57
+ beforeEach(() => {
58
+ TwitterCldr.set_data(require(`../../../lib/assets/javascripts/twitter_cldr/${localeFileName}`));
59
+ formatter = new TwitterCldr[`${kind}DecimalFormatter`]();
60
+ });
61
+
62
+ describe("#format", () => {
63
+ for (const { raw, precisions, default: _default } of tests) {
64
+ it(`formats ${raw} with no options supplied`, () => {
65
+ expect(formatter.format(raw)).toEqual(_default ?? precisions[0]);
66
+ });
67
+
68
+ for (const [precision, expected] of precisions.entries()) {
69
+ it(`formats ${raw} to precision ${precision}`, () => {
70
+ expect(formatter.format(raw, { precision })).toEqual(expected);
71
+ });
72
+ }
73
+ }
74
+ });
75
+ });
76
+ }
77
+ }
@@ -14,12 +14,11 @@ Gem::Specification.new do |s|
14
14
  s.summary = "Text formatting using data from Unicode's Common Locale Data Repository (CLDR)."
15
15
 
16
16
  s.add_dependency 'json'
17
- s.add_dependency 'railties', '>= 3.0', '< 5.0'
17
+ s.add_dependency 'railties', '>= 3.0'
18
18
 
19
19
  s.add_development_dependency 'rake'
20
20
  s.add_development_dependency 'mustache', '~> 0.99.4'
21
21
  s.add_development_dependency 'ruby_parser', '~> 2.3.1'
22
- s.add_development_dependency 'therubyracer', '~> 0.12.0'
23
22
  s.add_development_dependency 'uglifier', '~> 1.2.4'
24
23
  s.add_development_dependency 'coffee-script', '~> 2.2.0'
25
24
  s.add_development_dependency 'coffee-script-source', '~> 1.8.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitter_cldr_js
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-26 00:00:00.000000000 Z
11
+ date: 2023-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -31,9 +31,6 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '3.0'
34
- - - "<"
35
- - !ruby/object:Gem::Version
36
- version: '5.0'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -41,9 +38,6 @@ dependencies:
41
38
  - - ">="
42
39
  - !ruby/object:Gem::Version
43
40
  version: '3.0'
44
- - - "<"
45
- - !ruby/object:Gem::Version
46
- version: '5.0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rake
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -86,20 +80,6 @@ dependencies:
86
80
  - - "~>"
87
81
  - !ruby/object:Gem::Version
88
82
  version: 2.3.1
89
- - !ruby/object:Gem::Dependency
90
- name: therubyracer
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: 0.12.0
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: 0.12.0
103
83
  - !ruby/object:Gem::Dependency
104
84
  name: uglifier
105
85
  requirement: !ruby/object:Gem::Requirement
@@ -257,6 +237,7 @@ files:
257
237
  - lib/twitter_cldr/js/mustache/implementation/calendars/additional_date_format_selector.coffee
258
238
  - lib/twitter_cldr/js/mustache/implementation/calendars/datetime.coffee
259
239
  - lib/twitter_cldr/js/mustache/implementation/calendars/timespan.coffee
240
+ - lib/twitter_cldr/js/mustache/implementation/clone/clone.coffee
260
241
  - lib/twitter_cldr/js/mustache/implementation/numbers/numbers.coffee
261
242
  - lib/twitter_cldr/js/mustache/implementation/numbers/rbnf/formatters.coffee
262
243
  - lib/twitter_cldr/js/mustache/implementation/numbers/rbnf/number_data_reader.coffee
@@ -327,6 +308,7 @@ files:
327
308
  - lib/twitter_cldr/js/renderers/data/shared/list_renderer.rb
328
309
  - lib/twitter_cldr/js/renderers/data_bundle.rb
329
310
  - lib/twitter_cldr/js/renderers/implementation/calendars.rb
311
+ - lib/twitter_cldr/js/renderers/implementation/clone.rb
330
312
  - lib/twitter_cldr/js/renderers/implementation/numbers.rb
331
313
  - lib/twitter_cldr/js/renderers/implementation/parsers.rb
332
314
  - lib/twitter_cldr/js/renderers/implementation/plurals.rb
@@ -352,6 +334,7 @@ files:
352
334
  - spec/js/calendars/datetime.spec.js
353
335
  - spec/js/calendars/timespan.ru.spec.js
354
336
  - spec/js/calendars/timespan.spec.js
337
+ - spec/js/clone/clone.spec.js
355
338
  - spec/js/numbers/abbreviated/abbreviated_number.spec.js
356
339
  - spec/js/numbers/abbreviated/long_decimal.ko.spec.js
357
340
  - spec/js/numbers/abbreviated/long_decimal.ru.spec.js
@@ -364,6 +347,7 @@ files:
364
347
  - spec/js/numbers/decimal.spec.js
365
348
  - spec/js/numbers/helpers/fraction.spec.js
366
349
  - spec/js/numbers/helpers/integer.spec.js
350
+ - spec/js/numbers/long_short_decimal.spec.js
367
351
  - spec/js/numbers/number.spec.js
368
352
  - spec/js/numbers/percent.spec.js
369
353
  - spec/js/numbers/rbnf.spec.js
@@ -416,7 +400,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
416
400
  - !ruby/object:Gem::Version
417
401
  version: '0'
418
402
  requirements: []
419
- rubygems_version: 3.0.4
403
+ rubygems_version: 3.1.6
420
404
  signing_key:
421
405
  specification_version: 4
422
406
  summary: Text formatting using data from Unicode's Common Locale Data Repository (CLDR).