ruby-cldr 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -2
  3. data/Gemfile.lock +55 -5
  4. data/VERSION +1 -1
  5. data/lib/cldr/download.rb +15 -11
  6. data/lib/cldr/export.rb +64 -16
  7. data/lib/cldr/export/data.rb +18 -10
  8. data/lib/cldr/export/data/calendars/gregorian.rb +45 -22
  9. data/lib/cldr/export/data/currencies.rb +6 -1
  10. data/lib/cldr/export/data/currency_digits_and_rounding.rb +22 -0
  11. data/lib/cldr/export/data/layout.rb +22 -0
  12. data/lib/cldr/export/data/lists.rb +28 -0
  13. data/lib/cldr/export/data/metazones.rb +45 -0
  14. data/lib/cldr/export/data/numbering_systems.rb +41 -0
  15. data/lib/cldr/export/data/numbers.rb +9 -0
  16. data/lib/cldr/export/data/plurals.rb +8 -7
  17. data/lib/cldr/export/data/plurals/rules.rb +114 -31
  18. data/lib/cldr/export/data/rbnf.rb +64 -0
  19. data/lib/cldr/export/data/rbnf_root.rb +19 -0
  20. data/lib/cldr/export/data/timezones.rb +29 -6
  21. data/lib/cldr/export/data/units.rb +23 -6
  22. data/lib/cldr/export/data/windows_zones.rb +23 -0
  23. data/lib/cldr/export/yaml.rb +2 -1
  24. data/lib/cldr/format/datetime/base.rb +1 -1
  25. data/lib/cldr/format/time.rb +1 -1
  26. data/lib/cldr/thor.rb +1 -1
  27. data/lib/core_ext/string/camelize.rb +1 -1
  28. data/test/export/data/calendars_test.rb +23 -18
  29. data/test/export/data/currencies_test.rb +24 -24
  30. data/test/export/data/languages_test.rb +52 -48
  31. data/test/export/data/metazones_test.rb +17 -0
  32. data/test/export/data/numbers_test.rb +34 -3
  33. data/test/export/data/plurals_test.rb +133 -32
  34. data/test/export/data/territories_test.rb +22 -22
  35. data/test/export/data/timezones_test.rb +49 -26
  36. data/test/export/data/units_test.rb +16 -17
  37. data/test/export/data/windows_zones_test.rb +12 -0
  38. data/test/export_test.rb +4 -1
  39. data/test/format/date_test.rb +2 -2
  40. data/test/test_autotest.rb +1 -1
  41. metadata +94 -32
@@ -0,0 +1,22 @@
1
+ module Cldr
2
+ module Export
3
+ module Data
4
+ class Layout < Base
5
+ def initialize(locale)
6
+ super
7
+ update(:layout => layout)
8
+ end
9
+
10
+ def layout
11
+ result = { :orientation => { :character_order => nil } }
12
+
13
+ if node = select("layout/orientation/characterOrder/text()").first
14
+ result[:orientation][:character_order] = node.text
15
+ end
16
+
17
+ result
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module Cldr
2
+ module Export
3
+ module Data
4
+ class Lists < Base
5
+ def initialize(locale)
6
+ super
7
+ update(:lists => lists)
8
+ end
9
+
10
+ def lists
11
+ select('listPatterns/listPattern').inject({}) do |list_pattern_ret, list_pattern|
12
+ pattern_type = if attribute = list_pattern.attribute('type')
13
+ attribute.value.to_sym
14
+ else
15
+ :default
16
+ end
17
+
18
+ list_pattern_ret[pattern_type] = select(list_pattern, 'listPatternPart').inject({}) do |part_ret, part|
19
+ part_ret[part.attribute('type').value.to_sym] = part.content
20
+ part_ret
21
+ end
22
+ list_pattern_ret
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ require 'nokogiri'
2
+ require 'date'
3
+
4
+ module Cldr
5
+ module Export
6
+ module Data
7
+ class Metazones < Hash
8
+ def initialize
9
+ path = "#{Cldr::Export::Data.dir}/supplemental/metaZones.xml"
10
+ doc = File.open(path) { |file| Nokogiri::XML(file) }
11
+ self[:timezones] = doc.xpath('//metaZones/metazoneInfo/timezone').inject({}) do |result, node|
12
+ timezone = node.attr('type').to_sym
13
+ result[timezone] = metazone(node.xpath('usesMetazone'))
14
+ result[timezone].sort_by! { |zone| [zone['from'] ? zone['from'] : DateTime.new, zone['to'] ? zone['to'] : DateTime.now] }
15
+ result
16
+ end
17
+ self[:primaryzones] = doc.xpath('//primaryZones/primaryZone').inject({}) do |result, node|
18
+ territory = node.attr('iso3166').to_sym
19
+ result[territory] = node.content
20
+ result
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def metazone(nodes)
27
+ nodes.inject([]) do |result, node|
28
+ mzone = node.attr('mzone')
29
+ from = node.attr('from')
30
+ to = node.attr('to')
31
+ data = { 'metazone' => mzone }
32
+ data['from'] = parse_date(from) if from
33
+ data['to'] = parse_date(to) if to
34
+ result << data
35
+ result
36
+ end
37
+ end
38
+
39
+ def parse_date(date)
40
+ DateTime.strptime(date + ' UTC', '%F %R %Z')
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+
3
+ module Cldr
4
+ module Export
5
+ module Data
6
+ class NumberingSystems < Base
7
+
8
+ def initialize(*args)
9
+ super(nil)
10
+ update(:numbering_systems => numbering_systems)
11
+ end
12
+
13
+ def numbering_systems
14
+ doc.xpath("supplementalData/numberingSystems/numberingSystem").inject({}) do |ret, numbering|
15
+ system_name = numbering.attribute("id").value
16
+ type = numbering.attribute("type").value
17
+
18
+ param = case type
19
+ when "numeric"
20
+ "digits"
21
+ when "algorithmic"
22
+ "rules"
23
+ end
24
+
25
+ ret[system_name] = {
26
+ :type => type,
27
+ param => numbering.attribute(param).value
28
+ }
29
+
30
+ ret
31
+ end
32
+ end
33
+
34
+ def path
35
+ @path ||= "#{Cldr::Export::Data.dir}/supplemental/numberingSystems.xml"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -9,15 +9,19 @@ module Cldr
9
9
  :symbols => symbols,
10
10
  :formats => {
11
11
  :decimal => {
12
+ :number_system => number_system('decimal'),
12
13
  :patterns => format('decimal')
13
14
  },
14
15
  :scientific => {
16
+ :number_system => number_system('scientific'),
15
17
  :patterns => format('scientific')
16
18
  },
17
19
  :percent => {
20
+ :number_system => number_system('percent'),
18
21
  :patterns => format('percent')
19
22
  },
20
23
  :currency => {
24
+ :number_system => number_system('currency'),
21
25
  :patterns => format('currency'),
22
26
  :unit => unit
23
27
  }
@@ -54,6 +58,11 @@ module Cldr
54
58
  result
55
59
  end
56
60
 
61
+ def number_system(type)
62
+ node = select("numbers/#{type}Formats").first
63
+ node.attribute('numberSystem').value rescue "latn"
64
+ end
65
+
57
66
  def unit
58
67
  @unit ||= select("numbers/currencyFormats/unitPattern").inject({}) do |result, node|
59
68
  count = node.attribute('count').value rescue 'one'
@@ -3,7 +3,7 @@ require 'fileutils'
3
3
  module Cldr
4
4
  module Export
5
5
  module Data
6
- class Plurals < String
6
+ class Plurals < Hash
7
7
  autoload :Grammar, 'cldr/export/data/plurals/grammar'
8
8
  autoload :Parser, 'cldr/export/data/plurals/grammar'
9
9
  autoload :Rules, 'cldr/export/data/plurals/rules'
@@ -20,18 +20,19 @@ module Cldr
20
20
  File.read("#{Cldr::Export::Data.dir}/supplemental/plurals.xml")
21
21
  end
22
22
  end
23
-
23
+
24
24
  attr_reader :locale
25
25
 
26
26
  def initialize(locale)
27
27
  @locale = locale
28
- super(rule ? ruby : "")
28
+ self.merge!(rule ? to_hash : {})
29
29
  end
30
-
31
- def ruby
32
- "{ :#{locale} => { :i18n => {:plural => { :keys => #{rule.keys.inspect}, :rule => #{rule.to_ruby} } } } }"
30
+
31
+ def to_hash
32
+ rule_rb = rule ? rule.to_ruby : nil
33
+ { :keys => (rule || {}).keys, :rule => rule_rb }
33
34
  end
34
-
35
+
35
36
  def rule
36
37
  @rule = Plurals.rules.rule(locale)
37
38
  end
@@ -17,15 +17,15 @@ module Cldr
17
17
  rules
18
18
  end
19
19
  end
20
-
20
+
21
21
  def locales
22
22
  @locales ||= map { |rule| rule.locales }.flatten.map(&:to_s).sort.map(&:to_sym)
23
23
  end
24
-
24
+
25
25
  def rule(locale)
26
26
  detect { |rule| rule.locales.include?(locale.to_sym) }
27
27
  end
28
-
28
+
29
29
  def to_ruby(options = {})
30
30
  namespaces = options[:namespaces] || [:i18n]
31
31
  code = locales.map do |locale|
@@ -38,25 +38,44 @@ module Cldr
38
38
  "{\n" + code + "\n}"
39
39
  end
40
40
  end
41
-
41
+
42
42
  class Rule < Array
43
43
  class << self
44
+
45
+ def parse_list(str)
46
+ values = []
47
+ ranges = []
48
+ str.split(',').each do |value|
49
+ parts = value.split('..')
50
+ if parts.count == 1
51
+ values << value.to_i
52
+ else
53
+ ranges << (parts.first.to_i..parts.last.to_i)
54
+ end
55
+ end
56
+ [values, ranges]
57
+ end
58
+
44
59
  def parse(code)
60
+ code = code.split('@').first.to_s
61
+ operand = /(n|i|f|t|v|w)/i
62
+ expr = /#{operand}(?:\s+(?:mod|%)\s+([\d]+))?/i
63
+ range = /(?:\d+\.\.\d+|\d+)/i
64
+ range_list = /(#{range}(?:\s*,\s*#{range})*)/i
45
65
  case code
46
- when /and/
47
- code.split(/and/).inject(Proposition.new('&&')) { |rule, code| rule << parse(code) }
48
- when /or/
49
- code.split(/or/).inject(Proposition.new('||')) { |rule, code| rule << parse(code) }
50
- when /n( mod ([\d]+))? is (not )?([\d]+)/
51
- Expression.new(:is, $2, !!$3, $4)
52
- when /n( mod ([\d]+))?( not)? in ([\d]+\.\.[\d]+)/
53
- Expression.new(:in, $2, !!$3, eval($4).to_a.inspect)
54
- when /n within ([\d]+)\.\.([\d]+)/
55
- Expression.new(:within, nil, nil, [$1, $2])
56
- when /n/
66
+ when /or/i
67
+ code.split(/or/i).inject(Proposition.new('||')) { |rule, code| rule << parse(code) }
68
+ when /and/i
69
+ code.split(/and/i).inject(Proposition.new('&&')) { |rule, code| rule << parse(code) }
70
+ when /^\s*#{expr}\s+(?:is(\s+not)?|(not\s+)?in|(!)?=)\s+#{range_list}\s*$/i
71
+ list = parse_list($6)
72
+ Expression.new((list.first.count == 1 && list.last.count == 0) ? :is : :in, $2, !($3.nil? && $4.nil? && $5.nil?), (list.first.count == 1 && list.last.count == 0) ? list.first.first : list, $1)
73
+ when /^\s*#{expr}\s+(not\s+)?within\s+#{range_list}\s*$/i
74
+ Expression.new(:within, $2, !($3==nil), parse_list($4).last.first, $1)
75
+ when /^\s*$/
57
76
  Expression.new
58
77
  else
59
- raise "can not parse #{code}"
78
+ raise "can not parse '#{code}'"
60
79
  end
61
80
  end
62
81
  end
@@ -66,15 +85,19 @@ module Cldr
66
85
  def initialize(locales)
67
86
  @locales = locales.map { |locale| locale.gsub('_', '-').to_sym }
68
87
  end
69
-
88
+
70
89
  def keys
71
- inject([]) { |keys, (key, code)| keys << key.to_sym } << :other
90
+ inject([]) { |keys, (key, code)| keys << key.to_sym }
72
91
  end
73
-
92
+
74
93
  def to_ruby
75
- @condition ||= 'lambda { |n| ' + reverse.inject(':other') do |result, (key, code)|
94
+ @condition ||= 'lambda { |n| n = n.respond_to?(:abs) ? n.abs : ((m = n.to_s)[0] == "-" ? m[1,m.length] : m); ' + reverse.inject(':other') do |result, (key, code)|
76
95
  code = self.class.parse(code).to_ruby
77
- "#{code} ? :#{key} : #{result}"
96
+ if code
97
+ "#{code} ? :#{key} : #{result}"
98
+ else
99
+ ':' << key.to_s
100
+ end
78
101
  end + ' }'
79
102
  end
80
103
  end
@@ -85,30 +108,90 @@ module Cldr
85
108
  end
86
109
 
87
110
  def to_ruby
88
- @ruby ||= map { |expr| expr.to_ruby }.join(" #{@type} ")
111
+ @ruby ||= '(' << map { |expr| expr.to_ruby }.join(" #{@type} ") << ')'
89
112
  end
90
113
  end
91
114
 
92
115
  class Expression
93
- attr_reader :operator, :operand, :mod, :negate
116
+ attr_reader :operator, :operand, :mod, :negate, :type
94
117
 
95
- def initialize(operator = nil, mod = nil, negate = nil, operand = nil)
96
- @operator, @mod, @negate, @operand = operator, mod, negate, operand
118
+ def initialize(operator = nil, mod = nil, negate = nil, operand = nil, type = nil)
119
+ @operator, @mod, @negate, @operand, @type = operator, mod, negate, operand, type
97
120
  end
98
121
 
122
+ # Symbol Value
123
+ # n absolute value of the source number (integer and decimals).
124
+ # i integer digits of n.
125
+ # v number of visible fraction digits in n, with trailing zeros.
126
+ # w number of visible fraction digits in n, without trailing zeros.
127
+ # f visible fractional digits in n, with trailing zeros.
128
+ # t visible fractional digits in n, without trailing zeros.
129
+ #
130
+ # http://www.unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
99
131
  def to_ruby
100
132
  @ruby ||= begin
101
- op = 'n'
102
- op << " % #{@mod}" if mod
133
+ return nil unless @operator
134
+ enclose = false
135
+ fraction = false
136
+ case @type
137
+ when 'i'
138
+ op = 'n.to_i'
139
+ when 'f'
140
+ op = '(f = n.to_s.split(".")[1]) ? f.to_i : 0'
141
+ enclose = true
142
+ when 't'
143
+ op = '(t = n.to_s.split(".")[1]) ? t.gsub(/0+$/, "").to_i : 0'
144
+ enclose = true
145
+ when 'v'
146
+ op = '(v = n.to_s.split(".")[1]) ? v.length : 0'
147
+ enclose = true
148
+ when 'w'
149
+ op = '(w = n.to_s.split(".")[1]) ? w.gsub(/0+$/, "").length : 0'
150
+ enclose = true
151
+ else
152
+ fraction = true
153
+ op = 'n.to_f'
154
+ end
155
+ if @mod
156
+ op = '(' << op << ')' if enclose
157
+ op << ' % ' << @mod.to_s
158
+ enclose = false
159
+ end
103
160
  case @operator
104
161
  when :is
105
- op + (@negate ? ' != ' : ' == ') + @operand
162
+ op = '(' << op << ')' if enclose
163
+ op << (@negate ? ' != ' : ' == ') << @operand.to_s
106
164
  when :in
107
- (@negate ? '!' : '') + "#{@operand}.include?(#{op})"
165
+ values = @operand.first
166
+ ranges = @operand.last
167
+ prepend = (@negate ? '!' : '')
168
+ str = ''
169
+ bop = op
170
+ bop = '(' << bop << ')' if enclose || @mod
171
+ if values.count == 1
172
+ str = bop + (@negate ? ' != ' : ' == ') << values.first.to_s
173
+ elsif values.count > 1
174
+ str = prepend + "#{values.inspect}.include?(#{op})"
175
+ end
176
+ enclose = ranges.count > 1 || (values.count > 0 && ranges.count > 0)
177
+ if ranges.count > 0
178
+ str << ' || ' if values.count > 0
179
+ str << "((#{bop} % 1).zero? && " if fraction
180
+ str << '(' if ranges.count > 1
181
+ str << prepend + "(#{ranges.shift.inspect}).include?(#{op})"
182
+ ranges.each do |range|
183
+ str << ' || ' << prepend + "(#{range.inspect}).include?(#{op})"
184
+ end
185
+ str << ')' if ranges.count > 0
186
+ str << ')' if fraction
187
+ end
188
+ str = '(' << str << ')' if enclose
189
+ str
108
190
  when :within
109
- "#{op}.between?(#{@operand.first}, #{@operand.last})"
191
+ op = '(' << op << ')' if enclose || @mod
192
+ (@negate ? '!' : '') + "#{op}.between?(#{@operand.first}, #{@operand.last})"
110
193
  else
111
- op
194
+ raise "unknown operator '#{@operator}'"
112
195
  end
113
196
  end
114
197
  end
@@ -0,0 +1,64 @@
1
+ # encoding: UTF-8
2
+
3
+ module Cldr
4
+ module Export
5
+ module Data
6
+ class Rbnf < Base
7
+
8
+ def initialize(*args)
9
+ super
10
+ update(:rbnf => { :grouping => rule_groups })
11
+ end
12
+
13
+ def rule_groups
14
+ if File.exist?(path)
15
+ select("rbnf/rulesetGrouping").map do |grouping_node|
16
+ {
17
+ :type => grouping_node.attribute("type").value,
18
+ :ruleset => (grouping_node / "ruleset").map do |ruleset_node|
19
+ rule_set(ruleset_node)
20
+ end
21
+ }
22
+ end
23
+ else
24
+ {}
25
+ end
26
+ end
27
+
28
+ def rule_set(ruleset_node)
29
+ attrs = {
30
+ :type => ruleset_node.attribute("type").value,
31
+ :rules => (ruleset_node / "rbnfrule").map do |rule_node|
32
+ radix = if radix_attr = rule_node.attribute("radix")
33
+ radix_attr.value
34
+ else
35
+ nil
36
+ end
37
+
38
+ attrs = {
39
+ :value => rule_node.attribute("value").value,
40
+ :rule => fix_rule(rule_node.text)
41
+ }
42
+
43
+ attrs[:radix] = radix if radix
44
+ attrs
45
+ end
46
+ }
47
+
48
+ access = ruleset_node.attribute("access")
49
+ attrs[:access] = access.value if access
50
+ attrs
51
+ end
52
+
53
+ def fix_rule(rule)
54
+ rule.gsub(/\A'/, '').gsub("←", '<').gsub("→", '>')
55
+ end
56
+
57
+ def path
58
+ @path ||= "#{Cldr::Export::Data.dir}/rbnf/#{locale.to_s.gsub('-', '_')}.xml"
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+ end