ruby-cldr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/LICENSE +20 -0
  2. data/README.textile +104 -0
  3. data/Rakefile +18 -0
  4. data/TODO +68 -0
  5. data/VERSION +1 -0
  6. data/cldr.thor +5 -0
  7. data/lib/cldr/data/base.rb +66 -0
  8. data/lib/cldr/data/calendars/gregorian.rb +124 -0
  9. data/lib/cldr/data/calendars.rb +12 -0
  10. data/lib/cldr/data/currencies.rb +26 -0
  11. data/lib/cldr/data/delimiters.rb +21 -0
  12. data/lib/cldr/data/languages.rb +17 -0
  13. data/lib/cldr/data/numbers.rb +60 -0
  14. data/lib/cldr/data/plurals/cldr_grammar.treetop +50 -0
  15. data/lib/cldr/data/plurals/grammar.rb +536 -0
  16. data/lib/cldr/data/plurals/rules.rb +113 -0
  17. data/lib/cldr/data/plurals.rb +39 -0
  18. data/lib/cldr/data/territories.rb +17 -0
  19. data/lib/cldr/data/timezones.rb +25 -0
  20. data/lib/cldr/data/units.rb +25 -0
  21. data/lib/cldr/data.rb +34 -0
  22. data/lib/cldr/download.rb +20 -0
  23. data/lib/cldr/export/ruby.rb +16 -0
  24. data/lib/cldr/export/yaml.rb +39 -0
  25. data/lib/cldr/export.rb +69 -0
  26. data/lib/cldr/format/currency.rb +11 -0
  27. data/lib/cldr/format/date.rb +144 -0
  28. data/lib/cldr/format/datetime/base.rb +32 -0
  29. data/lib/cldr/format/datetime.rb +28 -0
  30. data/lib/cldr/format/decimal/base.rb +18 -0
  31. data/lib/cldr/format/decimal/fraction.rb +28 -0
  32. data/lib/cldr/format/decimal/integer.rb +46 -0
  33. data/lib/cldr/format/decimal/number.rb +44 -0
  34. data/lib/cldr/format/decimal.rb +32 -0
  35. data/lib/cldr/format/percent.rb +11 -0
  36. data/lib/cldr/format/time.rb +71 -0
  37. data/lib/cldr/format.rb +113 -0
  38. data/lib/cldr/thor.rb +33 -0
  39. data/lib/cldr.rb +7 -0
  40. data/lib/core_ext/hash/deep_merge.rb +7 -0
  41. data/lib/core_ext/hash/deep_stringify_keys.rb +11 -0
  42. data/lib/core_ext/hash/symbolize_keys.rb +10 -0
  43. data/lib/core_ext/string/camelize.rb +5 -0
  44. data/lib/core_ext/string/underscore.rb +9 -0
  45. data/test/all.rb +3 -0
  46. data/test/data/all.rb +3 -0
  47. data/test/data/calendars_test.rb +149 -0
  48. data/test/data/currencies_test.rb +47 -0
  49. data/test/data/delimiters_test.rb +31 -0
  50. data/test/data/languages_test.rb +67 -0
  51. data/test/data/numbers_test.rb +61 -0
  52. data/test/data/plurals_test.rb +132 -0
  53. data/test/data/territories_test.rb +51 -0
  54. data/test/data/timezones_test.rb +56 -0
  55. data/test/data/units_test.rb +36 -0
  56. data/test/export_test.rb +57 -0
  57. data/test/formats/all.rb +3 -0
  58. data/test/formats/datetime/all.rb +3 -0
  59. data/test/formats/datetime/date_test.rb +31 -0
  60. data/test/formats/datetime/datetime_test.rb +18 -0
  61. data/test/formats/datetime/day_test.rb +41 -0
  62. data/test/formats/datetime/hour_test.rb +79 -0
  63. data/test/formats/datetime/minute_test.rb +25 -0
  64. data/test/formats/datetime/month_test.rb +76 -0
  65. data/test/formats/datetime/period_test.rb +20 -0
  66. data/test/formats/datetime/quarter_test.rb +118 -0
  67. data/test/formats/datetime/second_test.rb +80 -0
  68. data/test/formats/datetime/time_test.rb +33 -0
  69. data/test/formats/datetime/timezone_test.rb +23 -0
  70. data/test/formats/datetime/year_test.rb +57 -0
  71. data/test/formats/decimal/fraction_test.rb +17 -0
  72. data/test/formats/decimal/integer_test.rb +67 -0
  73. data/test/formats/decimal/number_test.rb +77 -0
  74. data/test/formats/decimal_test.rb +19 -0
  75. data/test/formats/format_test.rb +66 -0
  76. data/test/formats/rails_compat/all.rb +3 -0
  77. data/test/formats/rails_compat/format_integer_test.rb +56 -0
  78. data/test/formats/rails_compat/format_number_test.rb +134 -0
  79. data/test/test_helper.rb +5 -0
  80. metadata +169 -0
@@ -0,0 +1,25 @@
1
+ # I probably don't really get timezones.
2
+
3
+ class Cldr
4
+ module Data
5
+ class Timezones < Base
6
+ def initialize(locale)
7
+ super
8
+ self[:timezones] = timezones
9
+ end
10
+
11
+ def timezones
12
+ @timezones ||= select('dates/timeZoneNames/zone').inject({}) do |result, zone|
13
+ type = zone.attribute('type').value.to_sym
14
+ city = select(zone, 'exemplarCity').first
15
+ result[type] = {}
16
+ # see en.xml, Europe/London, does not have an exemplarCity element
17
+ # instead it has long and short daylight names which otherwise only
18
+ # have metazones. (??)
19
+ result[type][:city] = city.content if city
20
+ result
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ class Cldr
2
+ module Data
3
+ class Units < Base
4
+ def initialize(locale)
5
+ super
6
+ self[:units] = units
7
+ end
8
+
9
+ def units
10
+ select('units/unit').inject({}) do |result, node|
11
+ result[node.attribute('type').value.to_sym] = unit(node)
12
+ result
13
+ end
14
+ end
15
+
16
+ def unit(node)
17
+ node.xpath('unitPattern').inject({}) do |result, node|
18
+ count = node.attribute('count') ? node.attribute('count').value.to_sym : :one
19
+ result[count] = node.content unless draft?(node)
20
+ result
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/cldr/data.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'core_ext/string/camelize'
2
+
3
+ class Cldr
4
+ module Data
5
+ autoload :Base, 'cldr/data/base'
6
+ autoload :Calendars, 'cldr/data/calendars'
7
+ autoload :Currencies, 'cldr/data/currencies'
8
+ autoload :Delimiters, 'cldr/data/delimiters'
9
+ autoload :Languages, 'cldr/data/languages'
10
+ autoload :Numbers, 'cldr/data/numbers'
11
+ autoload :Plurals, 'cldr/data/plurals'
12
+ autoload :Territories, 'cldr/data/territories'
13
+ autoload :Timezones, 'cldr/data/timezones'
14
+ autoload :Units, 'cldr/data/units'
15
+
16
+ class << self
17
+ def dir
18
+ @dir ||= File.expand_path('./vendor/cldr/common')
19
+ end
20
+
21
+ def dir=(dir)
22
+ @dir = dir
23
+ end
24
+
25
+ def locales
26
+ Dir["#{dir}/main/*.xml"].map { |path| path =~ /([\w_-]+)\.xml/ && $1 }
27
+ end
28
+
29
+ def components
30
+ self.constants.sort - [:Base, :Export]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ require 'fileutils'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'tempfile'
5
+
6
+ class Cldr
7
+ class << self
8
+ def download(source = nil, target = nil)
9
+ source ||= 'http://unicode.org/Public/cldr/1.7.2/core.zip'
10
+ target ||= File.expand_path('./vendor/cldr')
11
+
12
+ source = URI.parse(source)
13
+ tempfile = Tempfile.new('cldr-core')
14
+
15
+ system("curl #{source} -o #{tempfile.path}")
16
+ FileUtils.mkdir_p(target)
17
+ system("unzip #{tempfile.path} -d #{target}")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ class Cldr
2
+ module Export
3
+ class Ruby
4
+ def export(locale, component, options = {})
5
+ data = Export.data(component, locale, options)
6
+ data = data.to_ruby if data.respond_to?(:to_ruby)
7
+ unless data.empty?
8
+ path = Export.path(locale, component, 'rb')
9
+ Export.write(path, data)
10
+ yield(component, locale, path) if block_given?
11
+ data
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ require 'ya2yaml'
2
+
3
+ class Cldr
4
+ module Export
5
+ class Yaml < Ya2YAML
6
+ def initialize
7
+ super(:syck_compatible => true)
8
+ end
9
+
10
+ def export(locale, component, options = {})
11
+ data = Export.data(component, locale, options)
12
+ unless data.empty?
13
+ data = { locale.to_s.gsub('_', '-') => data }.deep_stringify_keys
14
+ path = Export.path(locale, component, 'yml')
15
+ Export.write(path, yaml(data))
16
+ yield(component, locale, path) if block_given?
17
+ data
18
+ end
19
+ end
20
+
21
+ def yaml(data)
22
+ emit(data, 1)[1..-1]
23
+ end
24
+
25
+ def emit(object, level = 1)
26
+ result = object.is_a?(Symbol) ? object.inspect : super
27
+ result.gsub(/(\s{1})(no):/i) { %(#{$1}"#{$2}":) } # FIXME fucking spaghetti code
28
+ end
29
+
30
+ def is_one_plain_line?(str)
31
+ # removed REX_BOOL, REX_INT
32
+ str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/ &&
33
+ str !~ /[:\#\s\[\]\{\},]/ &&
34
+ str !~ /#{REX_ANY_LB}/ &&
35
+ str !~ /^(#{REX_FLOAT}|#{REX_MERGE}|#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,69 @@
1
+ require 'i18n'
2
+ require 'i18n/locale/fallbacks'
3
+ require 'core_ext/string/camelize'
4
+ require 'core_ext/string/underscore'
5
+ require 'core_ext/hash/deep_stringify_keys'
6
+
7
+ class Cldr
8
+ module Export
9
+ autoload :Ruby, 'cldr/export/ruby'
10
+ autoload :Yaml, 'cldr/export/yaml'
11
+
12
+ class << self
13
+ def base_path
14
+ @@base_path ||= File.expand_path('./data')
15
+ end
16
+
17
+ def base_path=(base_path)
18
+ @@base_path = base_path
19
+ end
20
+
21
+ def export(options = {}, &block)
22
+ locales = options[:locales] || Data.locales
23
+ components = options[:components] || Data.components
24
+ self.base_path = options[:target] if options[:target]
25
+
26
+ locales.each do |locale|
27
+ components.each do |component|
28
+ exporter(component, options[:format]).export(locale, component, options, &block)
29
+ end
30
+ end
31
+ end
32
+
33
+ def exporter(component, format)
34
+ name = format ? format : component.to_s == 'Plurals' ? 'ruby' : 'yaml'
35
+ const_get(name.to_s.camelize).new
36
+ end
37
+
38
+ def data(component, locale, options = {})
39
+ if component.to_s == 'Plurals'
40
+ Data.const_get(component.to_s.camelize).new(locale)
41
+ else
42
+ data = locales(locale, options).inject({}) do |result, locale|
43
+ data = Data.const_get(component.to_s.camelize).new(locale)
44
+ data ? data.deep_merge(result) : result
45
+ end
46
+ # data = resolve_links if options[:merge] TODO!!
47
+ data
48
+ end
49
+ end
50
+
51
+ def locales(locale, options)
52
+ locale = locale.to_s.gsub('_', '-')
53
+ locales = options[:merge] ? I18n::Locale::Fallbacks.new[locale.to_sym] : [locale.to_sym]
54
+ locales << :root
55
+ locales
56
+ end
57
+
58
+ def write(path, data)
59
+ FileUtils.rm(path) if File.exists?(path)
60
+ FileUtils.mkdir_p(File.dirname(path))
61
+ File.open(path, 'w+') { |f| f.write(data) }
62
+ end
63
+
64
+ def path(locale, component, extension)
65
+ "#{Export.base_path}/#{locale.to_s.gsub('_', '-')}/#{component.to_s.underscore}.#{extension}"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ class Cldr
4
+ module Format
5
+ class Currency < Decimal
6
+ def apply(number, options = {})
7
+ super.gsub('¤', options[:currency] || '$')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,144 @@
1
+ class Cldr
2
+ module Format
3
+ class Date < Datetime::Base
4
+ PATTERN = /G{1,5}|y+|Y+|Q{1,4}|q{1,5}|M{1,5}|L{1,5}|d{1,2}|F{1}|E{1,5}|e{1,5}|c{1,5}/
5
+ METHODS = { # ignoring u, l, g, j, A
6
+ 'G' => :era,
7
+ 'y' => :year,
8
+ 'Y' => :year_of_week_of_year,
9
+ 'Q' => :quarter,
10
+ 'q' => :quarter_stand_alone,
11
+ 'M' => :month,
12
+ 'L' => :month_stand_alone,
13
+ 'w' => :week_of_year,
14
+ 'W' => :week_of_month,
15
+ 'd' => :day,
16
+ 'D' => :day_of_month,
17
+ 'F' => :day_of_week_in_month,
18
+ 'E' => :weekday,
19
+ 'e' => :weekday_local,
20
+ 'c' => :weekday_local_stand_alone,
21
+ }
22
+
23
+ def era(date, pattern, length)
24
+ raise 'not implemented'
25
+ end
26
+
27
+ def year(date, pattern, length)
28
+ year = date.year.to_s
29
+ year = year.length == 1 ? year : year[-2, 2] if length == 2
30
+ year = year.rjust(length, '0') if length > 1
31
+ year
32
+ end
33
+
34
+ def year_of_week_of_year(date, pattern, length)
35
+ raise 'not implemented'
36
+ end
37
+
38
+ def day_of_week_in_month(date, pattern, length) # e.g. 2nd Wed in July
39
+ raise 'not implemented'
40
+ end
41
+
42
+ def quarter(date, pattern, length)
43
+ quarter = (date.month.to_i - 1) / 3 + 1
44
+ case length
45
+ when 1
46
+ quarter.to_s
47
+ when 2
48
+ quarter.to_s.rjust(length, '0')
49
+ when 3
50
+ calendar[:quarters][:format][:abbreviated][quarter]
51
+ when 4
52
+ calendar[:quarters][:format][:wide][quarter]
53
+ end
54
+ end
55
+
56
+ def quarter_stand_alone(date, pattern, length)
57
+ quarter = (date.month.to_i - 1) / 3 + 1
58
+ case length
59
+ when 1
60
+ quarter.to_s
61
+ when 2
62
+ quarter.to_s.rjust(length, '0')
63
+ when 3
64
+ raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
65
+ # calendar[:quarters][:'stand-alone'][:abbreviated][key]
66
+ when 4
67
+ raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
68
+ # calendar[:quarters][:'stand-alone'][:wide][key]
69
+ when 5
70
+ calendar[:quarters][:'stand-alone'][:narrow][quarter]
71
+ end
72
+ end
73
+
74
+ def month(date, pattern, length)
75
+ case length
76
+ when 1
77
+ date.month.to_s
78
+ when 2
79
+ date.month.to_s.rjust(length, '0')
80
+ when 3
81
+ calendar[:months][:format][:abbreviated][date.month]
82
+ when 4
83
+ calendar[:months][:format][:wide][date.month]
84
+ when 5
85
+ raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
86
+ calendar[:months][:format][:narrow][date.month]
87
+ else
88
+ # raise unknown date format
89
+ end
90
+ end
91
+
92
+ def month_stand_alone(date, pattern, length)
93
+ case length
94
+ when 1
95
+ date.month.to_s
96
+ when 2
97
+ date.month.to_s.rjust(length, '0')
98
+ when 3
99
+ raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
100
+ calendar[:months][:'stand-alone'][:abbreviated][date.month]
101
+ when 4
102
+ raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
103
+ calendar[:months][:'stand-alone'][:wide][date.month]
104
+ when 5
105
+ calendar[:months][:'stand-alone'][:narrow][date.month]
106
+ else
107
+ # raise unknown date format
108
+ end
109
+ end
110
+
111
+ def day(date, pattern, length)
112
+ case length
113
+ when 1
114
+ date.day.to_s
115
+ when 2
116
+ date.day.to_s.rjust(length, '0')
117
+ end
118
+ end
119
+
120
+ WEEKDAY_KEYS = [:sun, :mon, :tue, :wed, :thu, :fri, :sat]
121
+
122
+ def weekday(date, pattern, length)
123
+ key = WEEKDAY_KEYS[date.wday]
124
+ case length
125
+ when 1..3
126
+ calendar[:days][:format][:abbreviated][key]
127
+ when 4
128
+ calendar[:days][:format][:wide][key]
129
+ when 5
130
+ calendar[:days][:'stand-alone'][:narrow][key]
131
+ end
132
+ end
133
+
134
+ def weekday_local(date, pattern, length)
135
+ # "Like E except adds a numeric value depending on the local starting day of the week"
136
+ raise 'not implemented (need to defer a country to lookup the local first day of week from weekdata)'
137
+ end
138
+
139
+ def weekday_local_stand_alone(date, pattern, length)
140
+ raise 'not implemented (need to defer a country to lookup the local first day of week from weekdata)'
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,32 @@
1
+ class Cldr
2
+ module Format
3
+ class Datetime
4
+ class Base
5
+ attr_reader :calendar
6
+
7
+ def initialize(format, calendar)
8
+ @calendar = calendar
9
+ compile(format)
10
+ end
11
+
12
+ protected
13
+
14
+ def compile(format)
15
+ (class << self; self; end).class_eval <<-code
16
+ def apply(date, options = {}); #{compile_format(format)}; end
17
+ code
18
+ end
19
+
20
+ # compile_format("EEEE, d. MMMM y") # =>
21
+ # '' + weekday(date, "EEEE", 4) + ', ' + day(date, "d", 1) + '. ' +
22
+ # month(date, "MMMM", 4) + ' ' + year(date, "y", 1) + ''
23
+ def compile_format(format)
24
+ "'" + format.gsub(self.class.const_get(:PATTERN)) do |token|
25
+ method = self.class.const_get(:METHODS)[token[0, 1]]
26
+ "' + #{method}(date, #{token.inspect}, #{token.length}) + '"
27
+ end + "'"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ class Cldr
2
+ module Format
3
+ class Datetime
4
+ autoload :Base, 'cldr/format/datetime/base'
5
+
6
+ attr_reader :date, :time
7
+
8
+ def initialize(format, date, time)
9
+ @date, @time = date, time
10
+ compile(format)
11
+ end
12
+
13
+ protected
14
+
15
+ def compile(format)
16
+ (class << self; self; end).class_eval <<-code
17
+ def apply(datetime, options = {}); #{compile_format(format)}; end
18
+ code
19
+ end
20
+
21
+ def compile_format(format)
22
+ "'" + format.gsub(%r(\{(0|1)\})) do |token|
23
+ "' + #{token == '{0}' ? 'time' : 'date'}.apply(datetime, options) + '"
24
+ end + "'"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ class Cldr
2
+ module Format
3
+ class Decimal
4
+ class Base
5
+ def interpolate(string, value, orientation = :right)
6
+ value = value.to_s
7
+ length = value.length
8
+ start, pad = orientation == :left ? [0, :rjust] : [-length, :ljust]
9
+
10
+ string = string.dup
11
+ string = string.ljust(length, '#') if string.length < length
12
+ string[start, length] = value
13
+ string.gsub('#', '')
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ class Cldr
2
+ module Format
3
+ class Decimal
4
+ class Fraction < Base
5
+ attr_reader :format, :decimal, :precision
6
+
7
+ def initialize(format, symbols = {})
8
+ @format = format ? format.split('.').pop : ''
9
+ @decimal = symbols[:decimal] || '.'
10
+ @precision = @format.length
11
+ end
12
+
13
+ def apply(fraction, options = {})
14
+ precision = options[:precision] || self.precision
15
+ if precision > 0
16
+ decimal + interpolate(format(options), fraction, :left)
17
+ else
18
+ ''
19
+ end
20
+ end
21
+
22
+ def format(options)
23
+ options[:precision] ? '0' * options[:precision] : @format
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ class Cldr
2
+ module Format
3
+ class Decimal
4
+ class Integer < Base
5
+ attr_reader :format, :separator, :groups
6
+
7
+ def initialize(format, symbols = {})
8
+ format = format.split('.')[0]
9
+ @format = prepare_format(format, symbols)
10
+ @groups = parse_groups(format)
11
+ @separator = symbols[:group] || ','
12
+ end
13
+
14
+ def apply(number, options = {})
15
+ format_groups(interpolate(format, number.to_i))
16
+ end
17
+
18
+ def format_groups(string)
19
+ return string if groups.empty?
20
+ tokens = []
21
+ tokens << chop_group(string, groups.first)
22
+ tokens << chop_group(string, groups.last) while string.length > groups.last
23
+ tokens << string
24
+ tokens.compact.reverse.join(separator)
25
+ end
26
+
27
+ def parse_groups(format)
28
+ return [] unless index = format.rindex(',')
29
+ rest = format[0, index]
30
+ widths = [format.length - index - 1]
31
+ widths << rest.length - rest.rindex(',') - 1 if rest.rindex(',')
32
+ widths.compact.uniq
33
+ end
34
+
35
+ def chop_group(string, size)
36
+ string.slice!(-size, size) if string.length > size
37
+ end
38
+
39
+ def prepare_format(format, symbols)
40
+ signs = symbols.values_at(:plus_sign, :minus_sign)
41
+ format.tr(',', '').tr('+-', signs.join)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,44 @@
1
+ class Cldr
2
+ module Format
3
+ class Decimal
4
+ class Number
5
+ attr_reader :prefix, :suffix, :integer_format, :fraction_format, :symbols
6
+
7
+ DEFAULT_SYMBOLS = { :group => ',', :decimal => '.', :plus_sign => '+', :minus_sign => '-' }
8
+ FORMAT_PATTERN = /([^0#,\.]*)([0#,\.]+)([^0#,\.]*)$/
9
+
10
+ def initialize(format, symbols = {})
11
+ @symbols = DEFAULT_SYMBOLS.merge(symbols)
12
+ @prefix, @suffix, @integer_format, @fraction_format = *parse_format(format, symbols)
13
+ end
14
+
15
+ def apply(number, options = {})
16
+ int, fraction = parse_number(number, options)
17
+
18
+ result = integer_format.apply(int, options)
19
+ result << fraction_format.apply(fraction, options) if fraction
20
+ prefix + result + suffix
21
+ end
22
+
23
+ protected
24
+
25
+ def parse_format(format, symbols = {})
26
+ format =~ FORMAT_PATTERN
27
+ prefix, suffix, int, fraction = $1.to_s, $3.to_s, *$2.split('.')
28
+ [prefix, suffix, Integer.new(int, symbols), Fraction.new(fraction, symbols)]
29
+ end
30
+
31
+ def parse_number(number, options = {})
32
+ precision = options[:precision] || fraction_format.precision
33
+ number = round_to(number, precision)
34
+ number.abs.to_s.split('.')
35
+ end
36
+
37
+ def round_to(number, precision)
38
+ factor = 10 ** precision
39
+ (number * factor).round.to_f / factor
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ class Cldr
2
+ module Format
3
+ class Decimal
4
+ autoload :Base, 'cldr/format/decimal/base'
5
+ autoload :Fraction, 'cldr/format/decimal/fraction'
6
+ autoload :Integer, 'cldr/format/decimal/integer'
7
+ autoload :Number, 'cldr/format/decimal/number'
8
+
9
+ attr_reader :positive, :negative
10
+
11
+ def initialize(format, symbols = {})
12
+ @positive, @negative = parse_format(format, symbols)
13
+ end
14
+
15
+ def apply(number, options = {})
16
+ number = Float(number)
17
+ format = number.abs == number ? positive : negative
18
+ format.apply(number, options)
19
+ rescue TypeError, ArgumentError
20
+ number
21
+ end
22
+
23
+ protected
24
+
25
+ def parse_format(format, symbols)
26
+ formats = format.split(symbols[:list] || ';')
27
+ formats << "#{symbols[:minus] || '-'}#{format}" if formats.size == 1
28
+ formats.map { |format| Number.new(format, symbols) }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ class Cldr
4
+ module Format
5
+ class Percent < Decimal
6
+ def apply(number, options = {})
7
+ super.gsub('¤', options[:percent_sign] || '%')
8
+ end
9
+ end
10
+ end
11
+ end