ruby-cldr 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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