twitter_cldr 1.0.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.
- 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
|