twitter_cldr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +177 -0
- data/NOTICE +26 -0
- data/README.md +83 -0
- data/Rakefile +36 -0
- data/lib/ext/calendars/date.rb +20 -0
- data/lib/ext/calendars/datetime.rb +36 -0
- data/lib/ext/calendars/time.rb +20 -0
- data/lib/ext/localized_object.rb +15 -0
- data/lib/ext/numbers/bignum.rb +3 -0
- data/lib/ext/numbers/fixnum.rb +3 -0
- data/lib/ext/numbers/float.rb +3 -0
- data/lib/ext/numbers/localized_number.rb +53 -0
- data/lib/ext/strings/symbol.rb +17 -0
- data/lib/formatters/base.rb +36 -0
- data/lib/formatters/calendars/date_formatter.rb +9 -0
- data/lib/formatters/calendars/datetime_formatter.rb +217 -0
- data/lib/formatters/calendars/time_formatter.rb +9 -0
- data/lib/formatters/numbers/currency_formatter.rb +25 -0
- data/lib/formatters/numbers/decimal_formatter.rb +22 -0
- data/lib/formatters/numbers/helpers/base.rb +18 -0
- data/lib/formatters/numbers/helpers/fraction.rb +29 -0
- data/lib/formatters/numbers/helpers/integer.rb +46 -0
- data/lib/formatters/numbers/number_formatter.rb +48 -0
- data/lib/formatters/numbers/percent_formatter.rb +18 -0
- data/lib/formatters/plurals/rules.rb +34 -0
- data/lib/shared/currencies.rb +33 -0
- data/lib/shared/languages.rb +48 -0
- data/lib/shared/resources.rb +45 -0
- data/lib/shared/timezones.rb +1 -0
- data/lib/tokenizers/base.rb +112 -0
- data/lib/tokenizers/calendars/date_tokenizer.rb +24 -0
- data/lib/tokenizers/calendars/datetime_tokenizer.rb +46 -0
- data/lib/tokenizers/calendars/time_tokenizer.rb +24 -0
- data/lib/tokenizers/key_path.rb +30 -0
- data/lib/tokenizers/numbers/number_tokenizer.rb +50 -0
- data/lib/tokenizers/token.rb +17 -0
- data/lib/twitter_cldr.rb +104 -0
- data/lib/version.rb +3 -0
- data/resources/ar/calendars.yml +145 -0
- data/resources/ar/languages.yml +498 -0
- data/resources/ar/numbers.yml +35 -0
- data/resources/ar/plurals.yml +1 -0
- data/resources/da/calendars.yml +157 -0
- data/resources/da/languages.yml +508 -0
- data/resources/da/numbers.yml +31 -0
- data/resources/da/plurals.yml +1 -0
- data/resources/de/calendars.yml +152 -0
- data/resources/de/languages.yml +508 -0
- data/resources/de/numbers.yml +31 -0
- data/resources/de/plurals.yml +1 -0
- data/resources/en/calendars.yml +145 -0
- data/resources/en/languages.yml +510 -0
- data/resources/en/numbers.yml +31 -0
- data/resources/en/plurals.yml +1 -0
- data/resources/es/calendars.yml +145 -0
- data/resources/es/languages.yml +508 -0
- data/resources/es/numbers.yml +30 -0
- data/resources/es/plurals.yml +1 -0
- data/resources/fa/calendars.yml +150 -0
- data/resources/fa/languages.yml +484 -0
- data/resources/fa/numbers.yml +30 -0
- data/resources/fa/plurals.yml +1 -0
- data/resources/fi/calendars.yml +176 -0
- data/resources/fi/languages.yml +508 -0
- data/resources/fi/numbers.yml +31 -0
- data/resources/fi/plurals.yml +1 -0
- data/resources/fil/calendars.yml +159 -0
- data/resources/fil/languages.yml +115 -0
- data/resources/fil/numbers.yml +31 -0
- data/resources/fil/plurals.yml +1 -0
- data/resources/fr/calendars.yml +149 -0
- data/resources/fr/languages.yml +508 -0
- data/resources/fr/numbers.yml +31 -0
- data/resources/fr/plurals.yml +1 -0
- data/resources/he/calendars.yml +145 -0
- data/resources/he/languages.yml +266 -0
- data/resources/he/numbers.yml +31 -0
- data/resources/he/plurals.yml +1 -0
- data/resources/hi/calendars.yml +144 -0
- data/resources/hi/languages.yml +505 -0
- data/resources/hi/numbers.yml +31 -0
- data/resources/hi/plurals.yml +1 -0
- data/resources/hu/calendars.yml +145 -0
- data/resources/hu/languages.yml +508 -0
- data/resources/hu/numbers.yml +30 -0
- data/resources/hu/plurals.yml +1 -0
- data/resources/id/calendars.yml +145 -0
- data/resources/id/languages.yml +506 -0
- data/resources/id/numbers.yml +30 -0
- data/resources/id/plurals.yml +1 -0
- data/resources/it/calendars.yml +164 -0
- data/resources/it/languages.yml +503 -0
- data/resources/it/numbers.yml +30 -0
- data/resources/it/plurals.yml +1 -0
- data/resources/ja/calendars.yml +157 -0
- data/resources/ja/languages.yml +502 -0
- data/resources/ja/numbers.yml +30 -0
- data/resources/ja/plurals.yml +1 -0
- data/resources/ko/calendars.yml +133 -0
- data/resources/ko/languages.yml +505 -0
- data/resources/ko/numbers.yml +30 -0
- data/resources/ko/plurals.yml +1 -0
- data/resources/ms/calendars.yml +145 -0
- data/resources/ms/languages.yml +54 -0
- data/resources/ms/numbers.yml +30 -0
- data/resources/ms/plurals.yml +1 -0
- data/resources/nl/calendars.yml +145 -0
- data/resources/nl/languages.yml +508 -0
- data/resources/nl/numbers.yml +31 -0
- data/resources/nl/plurals.yml +1 -0
- data/resources/no/calendars.yml +122 -0
- data/resources/no/languages.yml +508 -0
- data/resources/no/numbers.yml +30 -0
- data/resources/no/plurals.yml +1 -0
- data/resources/pl/calendars.yml +161 -0
- data/resources/pl/languages.yml +504 -0
- data/resources/pl/numbers.yml +31 -0
- data/resources/pl/plurals.yml +1 -0
- data/resources/pt/calendars.yml +145 -0
- data/resources/pt/languages.yml +508 -0
- data/resources/pt/numbers.yml +31 -0
- data/resources/pt/plurals.yml +1 -0
- data/resources/ru/calendars.yml +176 -0
- data/resources/ru/languages.yml +508 -0
- data/resources/ru/numbers.yml +30 -0
- data/resources/ru/plurals.yml +1 -0
- data/resources/shared/currencies.yml +451 -0
- data/resources/sv/calendars.yml +145 -0
- data/resources/sv/languages.yml +508 -0
- data/resources/sv/numbers.yml +31 -0
- data/resources/sv/plurals.yml +1 -0
- data/resources/th/calendars.yml +145 -0
- data/resources/th/languages.yml +507 -0
- data/resources/th/numbers.yml +30 -0
- data/resources/th/plurals.yml +1 -0
- data/resources/tr/calendars.yml +145 -0
- data/resources/tr/languages.yml +508 -0
- data/resources/tr/numbers.yml +30 -0
- data/resources/tr/plurals.yml +1 -0
- data/resources/ur/calendars.yml +133 -0
- data/resources/ur/languages.yml +81 -0
- data/resources/ur/numbers.yml +31 -0
- data/resources/ur/plurals.yml +1 -0
- data/resources/zh/calendars.yml +169 -0
- data/resources/zh/languages.yml +506 -0
- data/resources/zh/numbers.yml +30 -0
- data/resources/zh/plurals.yml +1 -0
- data/resources/zh-Hant/calendars.yml +141 -0
- data/resources/zh-Hant/languages.yml +409 -0
- data/resources/zh-Hant/numbers.yml +30 -0
- data/spec/ext/calendars/date_spec.rb +45 -0
- data/spec/ext/calendars/datetime_spec.rb +43 -0
- data/spec/ext/calendars/time_spec.rb +45 -0
- data/spec/ext/numbers/bignum_spec.rb +19 -0
- data/spec/ext/numbers/fixnum_spec.rb +19 -0
- data/spec/ext/numbers/float_spec.rb +19 -0
- data/spec/ext/numbers/localized_number_spec.rb +53 -0
- data/spec/ext/strings/symbol_spec.rb +23 -0
- data/spec/formatters/base_spec.rb +12 -0
- data/spec/formatters/calendars/datetime_formatter_spec.rb +324 -0
- data/spec/formatters/numbers/currency_formatter_spec.rb +27 -0
- data/spec/formatters/numbers/decimal_formatter_spec.rb +30 -0
- data/spec/formatters/numbers/helpers/fraction_spec.rb +22 -0
- data/spec/formatters/numbers/helpers/integer_spec.rb +99 -0
- data/spec/formatters/numbers/number_formatter_spec.rb +79 -0
- data/spec/formatters/numbers/percent_formatter_spec.rb +12 -0
- data/spec/formatters/plurals/rules_spec.rb +73 -0
- data/spec/shared/currencies_spec.rb +55 -0
- data/spec/shared/languages_spec.rb +82 -0
- data/spec/shared/resources_spec.rb +44 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/tokenizers/base_spec.rb +134 -0
- data/spec/tokenizers/calendars/date_tokenizer_spec.rb +36 -0
- data/spec/tokenizers/calendars/datetime_tokenizer_spec.rb +52 -0
- data/spec/tokenizers/calendars/time_tokenizer_spec.rb +34 -0
- data/spec/tokenizers/key_path_spec.rb +44 -0
- data/spec/tokenizers/numbers/number_tokenizer_spec.rb +60 -0
- data/spec/tokenizers/token_spec.rb +18 -0
- data/spec/twitter_cldr_spec.rb +53 -0
- metadata +293 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
# This class has been adapted from Sven Fuch's ruby-cldr gem
|
2
|
+
# See LICENSE for the accompanying license for his contributions
|
3
|
+
|
4
|
+
module TwitterCldr
|
5
|
+
module Formatters
|
6
|
+
class DateTimeFormatter < Base
|
7
|
+
WEEKDAY_KEYS = [:sun, :mon, :tue, :wed, :thu, :fri, :sat]
|
8
|
+
METHODS = { # ignoring u, l, g, j, A
|
9
|
+
'G' => :era,
|
10
|
+
'y' => :year,
|
11
|
+
'Y' => :year_of_week_of_year,
|
12
|
+
'Q' => :quarter,
|
13
|
+
'q' => :quarter_stand_alone,
|
14
|
+
'M' => :month,
|
15
|
+
'L' => :month_stand_alone,
|
16
|
+
'w' => :week_of_year,
|
17
|
+
'W' => :week_of_month,
|
18
|
+
'd' => :day,
|
19
|
+
'D' => :day_of_month,
|
20
|
+
'F' => :day_of_week_in_month,
|
21
|
+
'E' => :weekday,
|
22
|
+
'e' => :weekday_local,
|
23
|
+
'c' => :weekday_local_stand_alone,
|
24
|
+
'a' => :period,
|
25
|
+
'h' => :hour,
|
26
|
+
'H' => :hour,
|
27
|
+
'K' => :hour,
|
28
|
+
'k' => :hour,
|
29
|
+
'm' => :minute,
|
30
|
+
's' => :second,
|
31
|
+
'S' => :second_fraction,
|
32
|
+
'z' => :timezone,
|
33
|
+
'Z' => :timezone,
|
34
|
+
'v' => :timezone_generic_non_location,
|
35
|
+
'V' => :timezone_metazone
|
36
|
+
}
|
37
|
+
|
38
|
+
def initialize(options = {})
|
39
|
+
@tokenizer = TwitterCldr::Tokenizers::DateTimeTokenizer.new(:locale => extract_locale(options))
|
40
|
+
end
|
41
|
+
|
42
|
+
def result_for_token(token, index, date)
|
43
|
+
self.send(METHODS[token.value[0].chr], date, token.value, token.value.size)
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def era(date, pattern, length)
|
49
|
+
raise 'not implemented'
|
50
|
+
end
|
51
|
+
|
52
|
+
def year(date, pattern, length)
|
53
|
+
year = date.year.to_s
|
54
|
+
year = year.length == 1 ? year : year[-2, 2] if length == 2
|
55
|
+
year = year.rjust(length, '0') if length > 1
|
56
|
+
year
|
57
|
+
end
|
58
|
+
|
59
|
+
def year_of_week_of_year(date, pattern, length)
|
60
|
+
raise 'not implemented'
|
61
|
+
end
|
62
|
+
|
63
|
+
def day_of_week_in_month(date, pattern, length) # e.g. 2nd Wed in July
|
64
|
+
raise 'not implemented'
|
65
|
+
end
|
66
|
+
|
67
|
+
def quarter(date, pattern, length)
|
68
|
+
quarter = (date.month.to_i - 1) / 3 + 1
|
69
|
+
case length
|
70
|
+
when 1
|
71
|
+
quarter.to_s
|
72
|
+
when 2
|
73
|
+
quarter.to_s.rjust(length, '0')
|
74
|
+
when 3
|
75
|
+
@tokenizer.calendar[:quarters][:format][:abbreviated][quarter]
|
76
|
+
when 4
|
77
|
+
@tokenizer.calendar[:quarters][:format][:wide][quarter]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def quarter_stand_alone(date, pattern, length)
|
82
|
+
quarter = (date.month.to_i - 1) / 3 + 1
|
83
|
+
case length
|
84
|
+
when 1
|
85
|
+
quarter.to_s
|
86
|
+
when 2
|
87
|
+
quarter.to_s.rjust(length, '0')
|
88
|
+
when 3
|
89
|
+
raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
90
|
+
# @tokenizer.calendar[:quarters][:'stand-alone'][:abbreviated][key]
|
91
|
+
when 4
|
92
|
+
raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
93
|
+
# @tokenizer.calendar[:quarters][:'stand-alone'][:wide][key]
|
94
|
+
when 5
|
95
|
+
@tokenizer.calendar[:quarters][:'stand-alone'][:narrow][quarter]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def month(date, pattern, length)
|
100
|
+
case length
|
101
|
+
when 1
|
102
|
+
date.month.to_s
|
103
|
+
when 2
|
104
|
+
date.month.to_s.rjust(length, '0')
|
105
|
+
when 3
|
106
|
+
@tokenizer.calendar[:months][:format][:abbreviated][date.month]
|
107
|
+
when 4
|
108
|
+
@tokenizer.calendar[:months][:format][:wide][date.month]
|
109
|
+
when 5
|
110
|
+
raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
111
|
+
@tokenizer.calendar[:months][:format][:narrow][date.month]
|
112
|
+
else
|
113
|
+
# raise unknown date format
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def month_stand_alone(date, pattern, length)
|
118
|
+
case length
|
119
|
+
when 1
|
120
|
+
date.month.to_s
|
121
|
+
when 2
|
122
|
+
date.month.to_s.rjust(length, '0')
|
123
|
+
when 3
|
124
|
+
raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
125
|
+
@tokenizer.calendar[:months][:'stand-alone'][:abbreviated][date.month]
|
126
|
+
when 4
|
127
|
+
raise 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
128
|
+
@tokenizer.calendar[:months][:'stand-alone'][:wide][date.month]
|
129
|
+
when 5
|
130
|
+
@tokenizer.calendar[:months][:'stand-alone'][:narrow][date.month]
|
131
|
+
else
|
132
|
+
# raise unknown date format
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def day(date, pattern, length)
|
137
|
+
case length
|
138
|
+
when 1
|
139
|
+
date.day.to_s
|
140
|
+
when 2
|
141
|
+
date.day.to_s.rjust(length, '0')
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def weekday(date, pattern, length)
|
146
|
+
key = WEEKDAY_KEYS[date.wday]
|
147
|
+
case length
|
148
|
+
when 1..3
|
149
|
+
@tokenizer.calendar[:days][:format][:abbreviated][key]
|
150
|
+
when 4
|
151
|
+
@tokenizer.calendar[:days][:format][:wide][key]
|
152
|
+
when 5
|
153
|
+
@tokenizer.calendar[:days][:'stand-alone'][:narrow][key]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def weekday_local(date, pattern, length)
|
158
|
+
# "Like E except adds a numeric value depending on the local starting day of the week"
|
159
|
+
raise 'not implemented (need to defer a country to lookup the local first day of week from weekdata)'
|
160
|
+
end
|
161
|
+
|
162
|
+
def weekday_local_stand_alone(date, pattern, length)
|
163
|
+
raise 'not implemented (need to defer a country to lookup the local first day of week from weekdata)'
|
164
|
+
end
|
165
|
+
|
166
|
+
def period(time, pattern, length)
|
167
|
+
@tokenizer.calendar[:periods][time.strftime('%p').downcase.to_sym]
|
168
|
+
end
|
169
|
+
|
170
|
+
def hour(time, pattern, length)
|
171
|
+
hour = time.hour
|
172
|
+
hour = case pattern[0, 1]
|
173
|
+
when 'h' # [1-12]
|
174
|
+
hour > 12 ? (hour - 12) : (hour == 0 ? 12 : hour)
|
175
|
+
when 'H' # [0-23]
|
176
|
+
hour
|
177
|
+
when 'K' # [0-11]
|
178
|
+
hour > 11 ? hour - 12 : hour
|
179
|
+
when 'k' # [1-24]
|
180
|
+
hour == 0 ? 24 : hour
|
181
|
+
end
|
182
|
+
length == 1 ? hour.to_s : hour.to_s.rjust(length, '0')
|
183
|
+
end
|
184
|
+
|
185
|
+
def minute(time, pattern, length)
|
186
|
+
length == 1 ? time.min.to_s : time.min.to_s.rjust(length, '0')
|
187
|
+
end
|
188
|
+
|
189
|
+
def second(time, pattern, length)
|
190
|
+
length == 1 ? time.sec.to_s : time.sec.to_s.rjust(length, '0')
|
191
|
+
end
|
192
|
+
|
193
|
+
def second_fraction(time, pattern, length)
|
194
|
+
raise 'can not use the S format with more than 6 digits' if length > 6
|
195
|
+
(time.usec.to_f / 10 ** (6 - length)).round.to_s.rjust(length, '0')
|
196
|
+
end
|
197
|
+
|
198
|
+
def timezone(time, pattern, length)
|
199
|
+
case length
|
200
|
+
when 1..3
|
201
|
+
time.zone
|
202
|
+
else
|
203
|
+
"UTC #{time.strftime("%z")}"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def timezone_generic_non_location(time, pattern, length)
|
208
|
+
raise 'not yet implemented (requires timezone translation data")'
|
209
|
+
end
|
210
|
+
|
211
|
+
def round_to(number, precision)
|
212
|
+
factor = 10 ** precision
|
213
|
+
(number * factor).round.to_f / factor
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
class CurrencyFormatter < NumberFormatter
|
4
|
+
DEFAULT_FORMAT_OPTIONS = { :precision => 2 }
|
5
|
+
DEFAULT_CURRENCY_SYMBOL = "$"
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@tokenizer = TwitterCldr::Tokenizers::NumberTokenizer.new(:locale => self.extract_locale(options), :type => :currency)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def format(number, options = {})
|
13
|
+
if options[:currency]
|
14
|
+
currency ||= TwitterCldr::Shared::Currencies.for_code(options[:currency])
|
15
|
+
currency ||= TwitterCldr::Shared::Currencies.for_country(options[:currency])
|
16
|
+
currency ||= { :symbol => options[:currency] }
|
17
|
+
else
|
18
|
+
currency = { :symbol => DEFAULT_CURRENCY_SYMBOL }
|
19
|
+
end
|
20
|
+
|
21
|
+
super(number, DEFAULT_FORMAT_OPTIONS.merge(options)).gsub('¤', currency[:symbol])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
class DecimalFormatter < NumberFormatter
|
4
|
+
def initialize(options = {})
|
5
|
+
@tokenizer = TwitterCldr::Tokenizers::NumberTokenizer.new(:locale => self.extract_locale(options), :type => :decimal)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def format(number, options = {})
|
10
|
+
super(Float(number), options)
|
11
|
+
rescue TypeError, ArgumentError
|
12
|
+
number
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def get_tokens(obj, options = {})
|
18
|
+
obj.abs == obj ? @tokenizer.tokens(:sign => :positive) : @tokenizer.tokens(:sign => :negative)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
module Numbers
|
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,29 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
module Numbers
|
4
|
+
class Fraction < Base
|
5
|
+
attr_reader :format, :decimal, :precision
|
6
|
+
|
7
|
+
def initialize(token, symbols = {})
|
8
|
+
@format = token ? token.value.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
|
+
precision = options[:precision] || self.precision
|
24
|
+
precision ? '0' * precision : @format
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
module Numbers
|
4
|
+
class Integer < Base
|
5
|
+
attr_reader :format, :separator, :groups
|
6
|
+
|
7
|
+
def initialize(token, symbols = {})
|
8
|
+
format = token.value.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,48 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
class NumberFormatter < Base
|
4
|
+
attr_reader :symbols
|
5
|
+
|
6
|
+
DEFAULT_SYMBOLS = { :group => ',', :decimal => '.', :plus_sign => '+', :minus_sign => '-' }
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@symbols = DEFAULT_SYMBOLS.merge(tokenizer.symbols)
|
10
|
+
end
|
11
|
+
|
12
|
+
def format(number, options = {})
|
13
|
+
opts = { :precision => precision_from(number) }.merge(options)
|
14
|
+
prefix, suffix, integer_format, fraction_format = *partition_tokens(self.get_tokens(number, opts))
|
15
|
+
int, fraction = parse_number(number, opts)
|
16
|
+
|
17
|
+
result = integer_format.apply(int, opts)
|
18
|
+
result << fraction_format.apply(fraction, opts) if fraction
|
19
|
+
"#{prefix.to_s}#{result}#{suffix.to_s}"
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def partition_tokens(tokens)
|
25
|
+
[tokens[0] || "",
|
26
|
+
tokens[2] || "",
|
27
|
+
Numbers::Integer.new(tokens[1], @tokenizer.symbols),
|
28
|
+
Numbers::Fraction.new(tokens[1], @tokenizer.symbols)]
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_number(number, options = {})
|
32
|
+
precision = options[:precision] || self.precision_from(number)
|
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
|
+
|
42
|
+
def precision_from(num)
|
43
|
+
parts = num.to_s.split(".")
|
44
|
+
parts.size == 2 ? parts[1].size : 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
class PercentFormatter < NumberFormatter
|
4
|
+
DEFAULT_PERCENT_SIGN = "%"
|
5
|
+
DEFAULT_FORMAT_OPTIONS = { :precision => 0 }
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@tokenizer = TwitterCldr::Tokenizers::NumberTokenizer.new(:locale => self.extract_locale(options), :type => :percent)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def format(number, options = {})
|
13
|
+
opts = DEFAULT_FORMAT_OPTIONS.merge(options)
|
14
|
+
super(number, opts).gsub('¤', @tokenizer.symbols[:percent_sign] || DEFAULT_PERCENT_SIGN)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Formatters
|
3
|
+
module Plurals
|
4
|
+
class Rules
|
5
|
+
class << self
|
6
|
+
def all
|
7
|
+
all_for(TwitterCldr::get_locale)
|
8
|
+
end
|
9
|
+
|
10
|
+
def all_for(locale)
|
11
|
+
locale = TwitterCldr.convert_locale(locale.to_sym)
|
12
|
+
get_resource(locale)[locale][:i18n][:plural][:keys]
|
13
|
+
rescue => e
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def rule_for(number, locale = TwitterCldr::get_locale)
|
18
|
+
locale = TwitterCldr.convert_locale(locale.to_sym)
|
19
|
+
get_resource(locale)[locale][:i18n][:plural][:rule].call(number)
|
20
|
+
rescue => e
|
21
|
+
:other
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def get_resource(locale)
|
27
|
+
locale = TwitterCldr.convert_locale(locale)
|
28
|
+
eval(TwitterCldr.resources.resource_for(locale, "plurals")[locale])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Shared
|
3
|
+
class Currencies
|
4
|
+
@@resource = TwitterCldr.resources.resource_for("shared", "currencies")[:shared][:currencies]
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def countries
|
8
|
+
@@resource.map { |country_name, data| country_name.to_s }
|
9
|
+
end
|
10
|
+
|
11
|
+
def currency_codes
|
12
|
+
@@resource.map { |country_name, data| data[:code] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def for_country(country_name)
|
16
|
+
@@resource[country_name.to_sym]
|
17
|
+
end
|
18
|
+
|
19
|
+
def for_code(currency_code)
|
20
|
+
final = nil
|
21
|
+
@@resource.each_pair do |country_name, data|
|
22
|
+
if data[:code] == currency_code
|
23
|
+
final = data.merge({ :country => country_name.to_s })
|
24
|
+
final.delete(:code)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
final
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Shared
|
3
|
+
class Languages
|
4
|
+
class << self
|
5
|
+
def all
|
6
|
+
all_for(TwitterCldr::get_locale)
|
7
|
+
end
|
8
|
+
|
9
|
+
def all_for(code)
|
10
|
+
get_resource(TwitterCldr.convert_locale(code.to_sym))[:languages]
|
11
|
+
rescue => e
|
12
|
+
{}
|
13
|
+
end
|
14
|
+
|
15
|
+
def from_code(code)
|
16
|
+
from_code_for_locale(code, TwitterCldr::get_locale)
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_code_for_locale(code, locale = TwitterCldr::get_locale)
|
20
|
+
get_resource(TwitterCldr.convert_locale(locale.to_sym))[:languages][TwitterCldr.convert_locale(code.to_sym)]
|
21
|
+
rescue => e
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def translate_language(language, source_locale = :en, dest_locale = TwitterCldr::get_locale)
|
26
|
+
source_locale = TwitterCldr.convert_locale(source_locale.to_sym)
|
27
|
+
lang_code = get_resource(source_locale)[:languages].select { |key, val| val.downcase == language.downcase }.flatten.first
|
28
|
+
|
29
|
+
if lang_code
|
30
|
+
dest_locale = TwitterCldr.convert_locale(dest_locale.to_sym)
|
31
|
+
get_resource(dest_locale)[:languages][lang_code.to_sym]
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
rescue => e
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def get_resource(locale)
|
42
|
+
locale = TwitterCldr.convert_locale(locale)
|
43
|
+
TwitterCldr.resources.resource_for(locale, "languages")[locale]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module TwitterCldr
|
2
|
+
module Shared
|
3
|
+
class Resources
|
4
|
+
def initialize
|
5
|
+
@resources_by_locale = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def resource_for(locale, resource)
|
9
|
+
locale = locale.to_sym
|
10
|
+
unless @resources_by_locale.include?(locale)
|
11
|
+
@resources_by_locale[locale] = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
unless @resources_by_locale[locale].include?(resource)
|
15
|
+
@resources_by_locale[locale][resource] = data_for(locale, resource)
|
16
|
+
end
|
17
|
+
|
18
|
+
@resources_by_locale[locale][resource]
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def data_for(locale, resource)
|
24
|
+
deep_symbolize_keys(YAML.load(File.read(TwitterCldr.get_resource_file(locale, resource))))
|
25
|
+
end
|
26
|
+
|
27
|
+
# adapted from: http://snippets.dzone.com/posts/show/11121 (first comment)
|
28
|
+
def deep_symbolize_keys(arg)
|
29
|
+
case arg
|
30
|
+
when Array then
|
31
|
+
arg.map { |elem| deep_symbolize_keys(elem) }
|
32
|
+
when Hash then
|
33
|
+
Hash[
|
34
|
+
arg.map do |key, value|
|
35
|
+
k = key.is_a?(String) ? key.to_sym : key
|
36
|
+
v = deep_symbolize_keys(value)
|
37
|
+
[k, v]
|
38
|
+
end]
|
39
|
+
else
|
40
|
+
arg
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# not yet implemented
|