twitter_cldr_js 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/History.txt +3 -0
- data/LICENSE +177 -0
- data/NOTICE +120 -0
- data/README.md +161 -0
- data/Rakefile +66 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_af.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ar.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ca.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_cs.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_da.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_de.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_el.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_en.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_es.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_eu.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_fa.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_fi.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_fil.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_fr.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_he.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_hi.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_hu.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_id.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_it.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ja.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ko.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_msa.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_nl.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_no.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_pl.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_pt.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ru.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_sv.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_th.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_tr.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_uk.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_ur.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_zh-cn.js +887 -0
- data/lib/assets/javascripts/twitter_cldr/twitter_cldr_zh-tw.js +887 -0
- data/lib/twitter_cldr/js/compiler.rb +49 -0
- data/lib/twitter_cldr/js/mustache/bundle.coffee +15 -0
- data/lib/twitter_cldr/js/mustache/calendars/datetime.coffee +240 -0
- data/lib/twitter_cldr/js/mustache/calendars/timespan.coffee +52 -0
- data/lib/twitter_cldr/js/mustache/numbers/numbers.coffee +179 -0
- data/lib/twitter_cldr/js/mustache/plurals/rules.coffee +14 -0
- data/lib/twitter_cldr/js/mustache/shared/currencies.coffee +27 -0
- data/lib/twitter_cldr/js/renderers/base.rb +18 -0
- data/lib/twitter_cldr/js/renderers/bundle.rb +18 -0
- data/lib/twitter_cldr/js/renderers/calendars/datetime_renderer.rb +34 -0
- data/lib/twitter_cldr/js/renderers/calendars/timespan_renderer.rb +39 -0
- data/lib/twitter_cldr/js/renderers/numbers/numbers_renderer.rb +31 -0
- data/lib/twitter_cldr/js/renderers/plurals/rules/plural_rules_compiler.rb +93 -0
- data/lib/twitter_cldr/js/renderers/plurals/rules/plural_rules_renderer.rb +26 -0
- data/lib/twitter_cldr/js/renderers/shared/currencies_renderer.rb +23 -0
- data/lib/twitter_cldr/js/renderers.rb +31 -0
- data/lib/twitter_cldr/js/tasks/tasks.rake +14 -0
- data/lib/twitter_cldr/js/tasks/tasks.rb +88 -0
- data/lib/twitter_cldr/js/version.rb +10 -0
- data/lib/twitter_cldr/js.rb +31 -0
- data/spec/js/calendars/datetime.spec.js +418 -0
- data/spec/js/calendars/timespan.spec.js +91 -0
- data/spec/js/numbers/currency.spec.js +34 -0
- data/spec/js/numbers/decimal.spec.js +24 -0
- data/spec/js/numbers/helpers/fraction.spec.js +23 -0
- data/spec/js/numbers/helpers/integer.spec.js +100 -0
- data/spec/js/numbers/number.spec.js +70 -0
- data/spec/js/numbers/percent.spec.js +22 -0
- data/spec/js/plurals/plural_rules.spec.js +28 -0
- data/spec/ruby/renderers/plurals/plural_rules_compiler_spec.rb +56 -0
- data/spec/ruby/spec_helper.rb +11 -0
- data/twitter_cldr_js.gemspec +34 -0
- metadata +224 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Twitter, Inc
|
4
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
5
|
+
|
6
|
+
module TwitterCldr
|
7
|
+
module Js
|
8
|
+
class Compiler
|
9
|
+
attr_reader :locales
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@locales = options[:locales] || TwitterCldr.supported_locales
|
13
|
+
@features = options[:features] || renderers.keys
|
14
|
+
end
|
15
|
+
|
16
|
+
def compile_each(options = {})
|
17
|
+
options[:minify] = true unless options.include?(:minify)
|
18
|
+
|
19
|
+
@locales.each do |locale|
|
20
|
+
contents = ""
|
21
|
+
|
22
|
+
@features.each do |feature|
|
23
|
+
renderer_const = renderers[feature]
|
24
|
+
contents << renderer_const.new(:locale => locale).render if renderer_const
|
25
|
+
end
|
26
|
+
|
27
|
+
bundle = TwitterCldr::Js::Renderers::Bundle.new
|
28
|
+
bundle[:contents] = contents
|
29
|
+
result = CoffeeScript.compile(bundle.render, :bare => true)
|
30
|
+
result = Uglifier.compile(result) if options[:minify]
|
31
|
+
|
32
|
+
yield result, TwitterCldr.twitter_locale(locale)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def renderers
|
39
|
+
@renderers ||= {
|
40
|
+
:plural_rules => TwitterCldr::Js::Renderers::PluralRules::PluralRulesRenderer,
|
41
|
+
:timespan => TwitterCldr::Js::Renderers::Calendars::TimespanRenderer,
|
42
|
+
:datetime => TwitterCldr::Js::Renderers::Calendars::DateTimeRenderer,
|
43
|
+
:numbers => TwitterCldr::Js::Renderers::Numbers::NumbersRenderer,
|
44
|
+
:currencies => TwitterCldr::Js::Renderers::Shared::CurrenciesRenderer
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
###
|
2
|
+
// Copyright 2012 Twitter, Inc
|
3
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
4
|
+
|
5
|
+
// TwitterCLDR (JavaScript) v{{version}}
|
6
|
+
// Authors: Cameron Dutro [@camertron]
|
7
|
+
Kirill Lashuk [@KL_7]
|
8
|
+
portions by Sven Fuchs [@svenfuchs]
|
9
|
+
// Homepage: https://twitter.com
|
10
|
+
// Description: Provides date, time, number, and list formatting functionality for various Twitter-supported locales in Javascript.
|
11
|
+
###
|
12
|
+
|
13
|
+
TwitterCldr = {}
|
14
|
+
{{{contents}}}
|
15
|
+
exports[key] = obj for key, obj of TwitterCldr if exports? # for node
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# Copyright 2012 Twitter, Inc
|
2
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
3
|
+
|
4
|
+
TwitterCldr.DateTimeFormatter = class DateTimeFormatter
|
5
|
+
constructor: ->
|
6
|
+
@tokens = `{{{tokens}}}`
|
7
|
+
@calendar = `{{{calendar}}}`
|
8
|
+
@weekday_keys = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
|
9
|
+
@methods = # ignoring u, l, g, j, A
|
10
|
+
'G': 'era'
|
11
|
+
'y': 'year'
|
12
|
+
'Y': 'year_of_week_of_year'
|
13
|
+
'Q': 'quarter'
|
14
|
+
'q': 'quarter_stand_alone'
|
15
|
+
'M': 'month'
|
16
|
+
'L': 'month_stand_alone'
|
17
|
+
'w': 'week_of_year'
|
18
|
+
'W': 'week_of_month'
|
19
|
+
'd': 'day'
|
20
|
+
'D': 'day_of_month'
|
21
|
+
'F': 'day_of_week_in_month'
|
22
|
+
'E': 'weekday'
|
23
|
+
'e': 'weekday_local'
|
24
|
+
'c': 'weekday_local_stand_alone'
|
25
|
+
'a': 'period'
|
26
|
+
'h': 'hour'
|
27
|
+
'H': 'hour'
|
28
|
+
'K': 'hour'
|
29
|
+
'k': 'hour'
|
30
|
+
'm': 'minute'
|
31
|
+
's': 'second'
|
32
|
+
'S': 'second_fraction'
|
33
|
+
'z': 'timezone'
|
34
|
+
'Z': 'timezone'
|
35
|
+
'v': 'timezone_generic_non_location'
|
36
|
+
'V': 'timezone_metazone'
|
37
|
+
|
38
|
+
format: (obj, options) ->
|
39
|
+
format_token = (token) =>
|
40
|
+
result = ""
|
41
|
+
|
42
|
+
switch token.type
|
43
|
+
when "pattern"
|
44
|
+
return this.result_for_token(token, obj)
|
45
|
+
else
|
46
|
+
if token.value.length > 0 && token.value[0] == "'" && token.value[token.value.length - 1] == "'"
|
47
|
+
return token.value.substring(1, token.value.length - 1)
|
48
|
+
else
|
49
|
+
return token.value
|
50
|
+
|
51
|
+
tokens = this.get_tokens(obj, options)
|
52
|
+
return (format_token(token) for token in tokens).join("")
|
53
|
+
|
54
|
+
get_tokens: (obj, options) ->
|
55
|
+
return @tokens[options.format || "date_time"][options.type || "default"]
|
56
|
+
|
57
|
+
result_for_token: (token, date) ->
|
58
|
+
return this[@methods[token.value[0]]](date, token.value, token.value.length)
|
59
|
+
|
60
|
+
era: (date, pattern, length) ->
|
61
|
+
switch length
|
62
|
+
when 1, 2, 3
|
63
|
+
choices = @calendar["eras"]["abbr"]
|
64
|
+
else
|
65
|
+
choices = @calendar["eras"]["name"]
|
66
|
+
|
67
|
+
index = if (date.getFullYear() < 0) then 0 else 1
|
68
|
+
return choices[index]
|
69
|
+
|
70
|
+
year: (date, pattern, length) ->
|
71
|
+
year = date.getFullYear().toString()
|
72
|
+
|
73
|
+
if length == 2
|
74
|
+
if year.length != 1
|
75
|
+
year = year.slice(-2)
|
76
|
+
|
77
|
+
if length > 1
|
78
|
+
year = ("0000" + year).slice(-length)
|
79
|
+
|
80
|
+
return year
|
81
|
+
|
82
|
+
year_of_week_of_year: (date, pattern, length) ->
|
83
|
+
throw 'not implemented'
|
84
|
+
|
85
|
+
day_of_week_in_month: (date, pattern, length) -> # e.g. 2nd Wed in July
|
86
|
+
throw 'not implemented'
|
87
|
+
|
88
|
+
quarter: (date, pattern, length) ->
|
89
|
+
# the bitwise OR is used here to truncate the decimal produced by the / 3
|
90
|
+
quarter = ((date.getMonth() / 3) | 0) + 1
|
91
|
+
|
92
|
+
switch length
|
93
|
+
when 1
|
94
|
+
return quarter.toString()
|
95
|
+
when 2
|
96
|
+
return ("0000" + quarter.toString()).slice(-length)
|
97
|
+
when 3
|
98
|
+
return @calendar.quarters.format.abbreviated[quarter]
|
99
|
+
when 4
|
100
|
+
return @calendar.quarters.format.wide[quarter]
|
101
|
+
|
102
|
+
quarter_stand_alone: (date, pattern, length) ->
|
103
|
+
quarter = (date.getMonth() - 1) / 3 + 1
|
104
|
+
|
105
|
+
switch length
|
106
|
+
when 1
|
107
|
+
return quarter.toString()
|
108
|
+
when 2
|
109
|
+
return ("0000" + quarter.toString()).slice(-length)
|
110
|
+
when 3
|
111
|
+
throw 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
112
|
+
when 4
|
113
|
+
throw 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
114
|
+
when 5
|
115
|
+
return @calendar.quarters['stand-alone'].narrow[quarter]
|
116
|
+
|
117
|
+
month: (date, pattern, length) ->
|
118
|
+
month_str = (date.getMonth() + 1).toString()
|
119
|
+
|
120
|
+
switch length
|
121
|
+
when 1
|
122
|
+
return month_str
|
123
|
+
when 2
|
124
|
+
return ("0000" + month_str).slice(-length)
|
125
|
+
when 3
|
126
|
+
return @calendar.months.format.abbreviated[month_str]
|
127
|
+
when 4
|
128
|
+
return @calendar.months.format.wide[month_str]
|
129
|
+
when 5
|
130
|
+
throw 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
131
|
+
else
|
132
|
+
throw "Unknown date format"
|
133
|
+
|
134
|
+
month_stand_alone: (date, pattern, length) ->
|
135
|
+
switch length
|
136
|
+
when 1
|
137
|
+
return date.getMonth().toString()
|
138
|
+
when 2
|
139
|
+
return ("0000" + date.getMonth().toString()).slice(-length)
|
140
|
+
when 3
|
141
|
+
throw 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
142
|
+
when 4
|
143
|
+
throw 'not yet implemented (requires cldr\'s "multiple inheritance")'
|
144
|
+
when 5
|
145
|
+
return @calendar.months['stand-alone'].narrow[date.month]
|
146
|
+
else
|
147
|
+
throw "Unknown date format"
|
148
|
+
|
149
|
+
day: (date, pattern, length) ->
|
150
|
+
switch length
|
151
|
+
when 1
|
152
|
+
return date.getDate().toString()
|
153
|
+
when 2
|
154
|
+
return ("0000" + date.getDate().toString()).slice(-length)
|
155
|
+
|
156
|
+
weekday: (date, pattern, length) ->
|
157
|
+
key = @weekday_keys[date.getDay()]
|
158
|
+
|
159
|
+
switch length
|
160
|
+
when 1, 2, 3
|
161
|
+
return @calendar.days.format.abbreviated[key]
|
162
|
+
when 4
|
163
|
+
return @calendar.days.format.wide[key]
|
164
|
+
when 5
|
165
|
+
return @calendar.days['stand-alone'].narrow[key]
|
166
|
+
|
167
|
+
weekday_local: (date, pattern, length) ->
|
168
|
+
# "Like E except adds a numeric value depending on the local starting day of the week"
|
169
|
+
# CLDR does not contain data as to which day is the first day of the week, so we will assume Monday (Ruby default)
|
170
|
+
switch length
|
171
|
+
when 1, 2
|
172
|
+
day = date.getDay()
|
173
|
+
return (if day == 0 then "7" else day.toString())
|
174
|
+
else
|
175
|
+
return this.weekday(date, pattern, length)
|
176
|
+
|
177
|
+
weekday_local_stand_alone: (date, pattern, length) ->
|
178
|
+
switch length
|
179
|
+
when 1
|
180
|
+
return this.weekday_local(date, pattern, length)
|
181
|
+
else
|
182
|
+
return this.weekday(date, pattern, length)
|
183
|
+
|
184
|
+
period: (time, pattern, length) ->
|
185
|
+
if time.getHours() > 11
|
186
|
+
return @calendar.periods.format.wide["pm"]
|
187
|
+
else
|
188
|
+
return @calendar.periods.format.wide["am"]
|
189
|
+
|
190
|
+
hour: (time, pattern, length) ->
|
191
|
+
hour = time.getHours()
|
192
|
+
|
193
|
+
switch pattern[0]
|
194
|
+
when 'h'
|
195
|
+
if hour > 12
|
196
|
+
hour = hour - 12
|
197
|
+
else if hour == 0
|
198
|
+
hour = 12
|
199
|
+
when 'K'
|
200
|
+
if hour > 11
|
201
|
+
hour = hour - 12
|
202
|
+
when 'k'
|
203
|
+
if hour == 0
|
204
|
+
hour = 24
|
205
|
+
|
206
|
+
if length == 1
|
207
|
+
return hour.toString()
|
208
|
+
else
|
209
|
+
return ("000000" + hour.toString()).slice(-length)
|
210
|
+
|
211
|
+
minute: (time, pattern, length) ->
|
212
|
+
if length == 1
|
213
|
+
return time.getMinutes().toString()
|
214
|
+
else
|
215
|
+
return ("000000" + time.getMinutes().toString()).slice(-length)
|
216
|
+
|
217
|
+
second: (time, pattern, length) ->
|
218
|
+
if length == 1
|
219
|
+
return time.getSeconds().toString()
|
220
|
+
else
|
221
|
+
return ("000000" + time.getSeconds().toString()).slice(-length)
|
222
|
+
|
223
|
+
second_fraction: (time, pattern, length) ->
|
224
|
+
if length > 6
|
225
|
+
throw 'can not use the S format with more than 6 digits'
|
226
|
+
|
227
|
+
return ("000000" + Math.round(Math.pow(time.getMilliseconds() * 100.0, 6 - length)).toString()).slice(-length)
|
228
|
+
|
229
|
+
timezone: (time, pattern, length) ->
|
230
|
+
hours = ("00" + (time.getTimezoneOffset() / 60).toString()).slice(-2)
|
231
|
+
minutes = ("00" + (time.getTimezoneOffset() % 60).toString()).slice(-2)
|
232
|
+
|
233
|
+
switch length
|
234
|
+
when 1, 2, 3
|
235
|
+
return "-" + hours + ":" + minutes
|
236
|
+
else
|
237
|
+
return "UTC -" + hours + ":" + minutes
|
238
|
+
|
239
|
+
timezone_generic_non_location: (time, pattern, length) ->
|
240
|
+
throw 'not yet implemented (requires timezone translation data")'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright 2012 Twitter, Inc
|
2
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
3
|
+
|
4
|
+
TwitterCldr.TimespanFormatter = class TimespanFormatter
|
5
|
+
constructor: ->
|
6
|
+
@default_type = "default"
|
7
|
+
@tokens = `{{{tokens}}}`
|
8
|
+
@time_in_seconds = {
|
9
|
+
"second": 1,
|
10
|
+
"minute": 60,
|
11
|
+
"hour": 3600,
|
12
|
+
"day": 86400,
|
13
|
+
"week": 604800,
|
14
|
+
"month": 2629743.83,
|
15
|
+
"year": 31556926
|
16
|
+
}
|
17
|
+
|
18
|
+
format: (seconds, options = {}) ->
|
19
|
+
options["direction"] ||= (if seconds < 0 then "ago" else "until")
|
20
|
+
options["unit"] = this.calculate_unit(Math.abs(seconds)) if options["unit"] is null or options["unit"] is undefined
|
21
|
+
options["type"] ||= @default_type
|
22
|
+
options["number"] = this.calculate_time(Math.abs(seconds), options["unit"])
|
23
|
+
number = this.calculate_time(Math.abs(seconds), options["unit"])
|
24
|
+
options["rule"] = TwitterCldr.PluralRules.rule_for(number)
|
25
|
+
|
26
|
+
strings = (token.value for token in @tokens[options["direction"]][options["unit"]][options["type"]][options["rule"]])
|
27
|
+
strings.join("").replace(/\{[0-9]\}/, number.toString())
|
28
|
+
|
29
|
+
calculate_unit: (seconds) ->
|
30
|
+
if seconds < 30
|
31
|
+
"second"
|
32
|
+
else if seconds < 2670
|
33
|
+
"minute"
|
34
|
+
else if seconds < 86369
|
35
|
+
"hour"
|
36
|
+
else if seconds < 604800
|
37
|
+
"day"
|
38
|
+
else if seconds < 2591969
|
39
|
+
"week"
|
40
|
+
else if seconds < 31556926
|
41
|
+
"month"
|
42
|
+
else
|
43
|
+
"year"
|
44
|
+
|
45
|
+
# 0 <-> 29 secs # => seconds
|
46
|
+
# 30 secs <-> 44 mins, 29 secs # => minutes
|
47
|
+
# 44 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => hours
|
48
|
+
# 23 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => days
|
49
|
+
# 29 days, 23 hrs, 59 mins, 29 secs <-> 1 yr minus 1 sec # => months
|
50
|
+
# 1 yr <-> max time or date # => years
|
51
|
+
calculate_time: (seconds, unit) ->
|
52
|
+
Math.round(seconds / @time_in_seconds[unit])
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# Copyright 2012 Twitter, Inc
|
2
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
3
|
+
|
4
|
+
TwitterCldr.NumberFormatter = class NumberFormatter
|
5
|
+
constructor: ->
|
6
|
+
@all_tokens = `{{{tokens}}}`
|
7
|
+
@tokens = []
|
8
|
+
@symbols = `{{{symbols}}}`
|
9
|
+
|
10
|
+
@default_symbols =
|
11
|
+
'group': ','
|
12
|
+
'decimal': '.'
|
13
|
+
'plus_sign': '+'
|
14
|
+
'minus_sign': '-'
|
15
|
+
|
16
|
+
format: (number, options = {}) ->
|
17
|
+
opts = this.default_format_options_for(number)
|
18
|
+
|
19
|
+
for key, val of options
|
20
|
+
opts[key] = if options[key]? then options[key] else opts[key]
|
21
|
+
|
22
|
+
[prefix, suffix, integer_format, fraction_format] = this.partition_tokens(this.get_tokens(number, opts))
|
23
|
+
[int, fraction] = this.parse_number(number, opts)
|
24
|
+
result = integer_format.apply(parseFloat(int), opts)
|
25
|
+
result += fraction_format.apply(fraction, opts) if fraction
|
26
|
+
sign = if number < 0 && prefix != "-" then @symbols.minus_sign || @default_symbols.minus_sign else ""
|
27
|
+
"#{prefix}#{result}#{suffix}"
|
28
|
+
|
29
|
+
partition_tokens: (tokens) ->
|
30
|
+
[tokens[0] || "",
|
31
|
+
tokens[2] || "",
|
32
|
+
new IntegerHelper(tokens[1], @symbols),
|
33
|
+
new FractionHelper(tokens[1], @symbols)]
|
34
|
+
|
35
|
+
parse_number: (number, options = {}) ->
|
36
|
+
if options.precision?
|
37
|
+
precision = options.precision
|
38
|
+
else
|
39
|
+
precision = this.precision_from(number)
|
40
|
+
|
41
|
+
number = this.round_to(number, precision)
|
42
|
+
Math.abs(number).toFixed(precision).split(".")
|
43
|
+
|
44
|
+
precision_from: (num) ->
|
45
|
+
parts = num.toString().split(".")
|
46
|
+
if parts.length == 2 then parts[1].length else 0
|
47
|
+
|
48
|
+
round_to: (number, precision) ->
|
49
|
+
factor = Math.pow(10, precision)
|
50
|
+
Math.round(number * factor) / factor
|
51
|
+
|
52
|
+
get_tokens: ->
|
53
|
+
throw "get_tokens() not implemented - use a derived class like PercentFormatter."
|
54
|
+
|
55
|
+
TwitterCldr.PercentFormatter = class PercentFormatter extends NumberFormatter
|
56
|
+
constructor: (options = {}) ->
|
57
|
+
@default_percent_sign = "%"
|
58
|
+
super
|
59
|
+
|
60
|
+
format: (number, options = {}) ->
|
61
|
+
super(number, options).replace('¤', @symbols.percent_sign || @default_percent_sign)
|
62
|
+
|
63
|
+
default_format_options_for: (number) ->
|
64
|
+
precision: 0
|
65
|
+
|
66
|
+
get_tokens: (number, options) ->
|
67
|
+
if number < 0 then @all_tokens.percent.negative else @all_tokens.percent.positive
|
68
|
+
|
69
|
+
TwitterCldr.DecimalFormatter = class DecimalFormatter extends NumberFormatter
|
70
|
+
format: (number, options = {}) ->
|
71
|
+
try
|
72
|
+
super(number, options)
|
73
|
+
catch error
|
74
|
+
number
|
75
|
+
|
76
|
+
default_format_options_for: (number) ->
|
77
|
+
precision: this.precision_from(number)
|
78
|
+
|
79
|
+
get_tokens: (number, options = {}) ->
|
80
|
+
if number < 0 then @all_tokens.decimal.negative else @all_tokens.decimal.positive
|
81
|
+
|
82
|
+
TwitterCldr.CurrencyFormatter = class CurrencyFormatter extends NumberFormatter
|
83
|
+
constructor: (options = {}) ->
|
84
|
+
@default_currency_symbol = "$"
|
85
|
+
@default_precision = 2
|
86
|
+
super
|
87
|
+
|
88
|
+
format: (number, options = {}) ->
|
89
|
+
if options.currency
|
90
|
+
if TwitterCldr.Currencies?
|
91
|
+
currency = TwitterCldr.Currencies.for_code(options.currency)
|
92
|
+
currency ||= TwitterCldr.Currencies.for_country(options.currency)
|
93
|
+
currency ||= symbol: options.currency
|
94
|
+
else
|
95
|
+
currency = symbol: options.currency
|
96
|
+
else
|
97
|
+
currency = symbol: @default_currency_symbol
|
98
|
+
|
99
|
+
super(number, options).replace('¤', currency.symbol)
|
100
|
+
|
101
|
+
default_format_options_for: (number) ->
|
102
|
+
precision = this.precision_from(number)
|
103
|
+
if precision == 0 then precision = @default_precision
|
104
|
+
precision: precision
|
105
|
+
|
106
|
+
get_tokens: (number, options = {}) ->
|
107
|
+
if number < 0 then @all_tokens.currency.negative else @all_tokens.currency.positive
|
108
|
+
|
109
|
+
TwitterCldr.NumberFormatter.BaseHelper = class BaseHelper
|
110
|
+
interpolate: (string, value, orientation = "right") ->
|
111
|
+
value = value.toString()
|
112
|
+
length = value.length
|
113
|
+
start = if orientation == "left" then 0 else -length
|
114
|
+
string = (("#" for i in [0...length]).join("") + string).slice(-length) if string.length < length
|
115
|
+
|
116
|
+
if start < 0
|
117
|
+
string = string[0...(start + string.length)] + value
|
118
|
+
else
|
119
|
+
string = string[0...start] + value + string[(length)..-1]
|
120
|
+
|
121
|
+
string.replace(/#/g, "")
|
122
|
+
|
123
|
+
TwitterCldr.NumberFormatter.IntegerHelper = class IntegerHelper extends BaseHelper
|
124
|
+
constructor: (token, symbols = {}) ->
|
125
|
+
format = token.split('.')[0]
|
126
|
+
@format = this.prepare_format(format, symbols)
|
127
|
+
@groups = this.parse_groups(format)
|
128
|
+
@separator = symbols.group || ','
|
129
|
+
|
130
|
+
apply: (number, options = {}) ->
|
131
|
+
this.format_groups(this.interpolate(@format, parseInt(number)))
|
132
|
+
|
133
|
+
format_groups: (string) ->
|
134
|
+
return string if @groups.length == 0
|
135
|
+
tokens = []
|
136
|
+
|
137
|
+
cur_token = this.chop_group(string, @groups[0])
|
138
|
+
tokens.push(cur_token)
|
139
|
+
string = string[0...(string.length - cur_token.length)] if cur_token
|
140
|
+
|
141
|
+
while string.length > @groups[@groups.length - 1]
|
142
|
+
cur_token = this.chop_group(string, @groups[@groups.length - 1])
|
143
|
+
tokens.push(cur_token)
|
144
|
+
string = string[0...(string.length - cur_token.length)] if cur_token
|
145
|
+
|
146
|
+
tokens.push(string)
|
147
|
+
(token for token in tokens when token != null).reverse().join(@separator)
|
148
|
+
|
149
|
+
parse_groups: (format) ->
|
150
|
+
return [] unless index = format.lastIndexOf(',')
|
151
|
+
rest = format[0...index]
|
152
|
+
widths = [format.length - index - 1]
|
153
|
+
widths.push(rest.length - rest.lastIndexOf(',') - 1) if rest.lastIndexOf(',') > -1
|
154
|
+
widths = (width for width in widths when width != null) # compact
|
155
|
+
widths.reverse() # uniq
|
156
|
+
(widths[index] for index in [0...widths.length] when widths.indexOf(widths[index], index + 1) == -1).reverse()
|
157
|
+
|
158
|
+
chop_group: (string, size) ->
|
159
|
+
if string.length > size then string[-size..-1] else null
|
160
|
+
|
161
|
+
prepare_format: (format, symbols) ->
|
162
|
+
format.replace(",", "").replace("+", symbols.plus_sign).replace("-", symbols.minus_sign)
|
163
|
+
|
164
|
+
TwitterCldr.NumberFormatter.FractionHelper = class FractionHelper extends BaseHelper
|
165
|
+
constructor: (token, symbols = {}) ->
|
166
|
+
@format = if token then token.split('.').pop() else ""
|
167
|
+
@decimal = symbols.decimal || "."
|
168
|
+
@precision = @format.length
|
169
|
+
|
170
|
+
apply: (fraction, options = {}) ->
|
171
|
+
precision = if options.precision? then options.precision else @precision
|
172
|
+
if precision > 0
|
173
|
+
@decimal + this.interpolate(this.format_for(options), fraction, "left")
|
174
|
+
else
|
175
|
+
""
|
176
|
+
|
177
|
+
format_for: (options) ->
|
178
|
+
precision = if options.precision? then options.precision else @precision
|
179
|
+
if precision then ("0" for i in [0...precision]).join("") else @format
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Copyright 2012 Twitter, Inc
|
2
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
3
|
+
|
4
|
+
TwitterCldr.PluralRules = class PluralRules
|
5
|
+
@rules = `{{{rules}}}`
|
6
|
+
|
7
|
+
@all: ->
|
8
|
+
return @rules.keys
|
9
|
+
|
10
|
+
@rule_for: (number) ->
|
11
|
+
try
|
12
|
+
return @rules.rule(number)
|
13
|
+
catch error
|
14
|
+
return "other"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright 2012 Twitter, Inc
|
2
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
3
|
+
|
4
|
+
TwitterCldr.Currencies = class Currencies
|
5
|
+
@currencies = `{{{currencies}}}`
|
6
|
+
|
7
|
+
@countries: ->
|
8
|
+
@names ||= (country_name for country_name, data of @currencies)
|
9
|
+
|
10
|
+
@currency_codes: ->
|
11
|
+
@codes ||= (data.code for country_name, data of @currencies)
|
12
|
+
|
13
|
+
@for_country: (country_name) ->
|
14
|
+
@currencies[country_name]
|
15
|
+
|
16
|
+
@for_code: (currency_code) ->
|
17
|
+
final = null
|
18
|
+
for country_name, data of @currencies
|
19
|
+
if data.code == currency_code
|
20
|
+
final =
|
21
|
+
country: country_name
|
22
|
+
code: data.code
|
23
|
+
symbol: data.symbol
|
24
|
+
currency: data.currency
|
25
|
+
break
|
26
|
+
|
27
|
+
final
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Twitter, Inc
|
4
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
5
|
+
|
6
|
+
module TwitterCldr
|
7
|
+
module Js
|
8
|
+
module Renderers
|
9
|
+
class Base < Mustache
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@locale = options[:locale]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Twitter, Inc
|
4
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
5
|
+
|
6
|
+
module TwitterCldr
|
7
|
+
module Js
|
8
|
+
module Renderers
|
9
|
+
class Bundle < Mustache
|
10
|
+
self.template_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "mustache/bundle.coffee"))
|
11
|
+
|
12
|
+
def version
|
13
|
+
TwitterCldr::VERSION
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Twitter, Inc
|
4
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
5
|
+
|
6
|
+
module TwitterCldr
|
7
|
+
module Js
|
8
|
+
module Renderers
|
9
|
+
module Calendars
|
10
|
+
class DateTimeRenderer < TwitterCldr::Js::Renderers::Base
|
11
|
+
self.template_file = File.expand_path(File.join(File.dirname(__FILE__), "../..", "mustache/calendars/datetime.coffee"))
|
12
|
+
|
13
|
+
def tokens
|
14
|
+
tokens = {}
|
15
|
+
{ :date_time => TwitterCldr::Tokenizers::DateTimeTokenizer,
|
16
|
+
:time => TwitterCldr::Tokenizers::TimeTokenizer,
|
17
|
+
:date => TwitterCldr::Tokenizers::DateTokenizer }.each_pair do |name, const|
|
18
|
+
tokenizer = const.new(:locale => @locale)
|
19
|
+
tokens[name] = const::VALID_TYPES.inject({}) do |ret, type|
|
20
|
+
ret[type] = tokenizer.tokens(:type => type).map(&:to_hash)
|
21
|
+
ret
|
22
|
+
end
|
23
|
+
end
|
24
|
+
tokens.to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
def calendar
|
28
|
+
TwitterCldr::Tokenizers::DateTimeTokenizer.new(:locale => @locale).calendar.to_json
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Twitter, Inc
|
4
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
5
|
+
|
6
|
+
include TwitterCldr::Tokenizers
|
7
|
+
include TwitterCldr::Formatters
|
8
|
+
|
9
|
+
module TwitterCldr
|
10
|
+
module Js
|
11
|
+
module Renderers
|
12
|
+
module Calendars
|
13
|
+
|
14
|
+
class TimespanRenderer < TwitterCldr::Js::Renderers::Base
|
15
|
+
self.template_file = File.expand_path(File.join(File.dirname(__FILE__), "../..", "mustache/calendars/timespan.coffee"))
|
16
|
+
|
17
|
+
def tokens
|
18
|
+
tokenizer = TimespanTokenizer.new(:locale => @locale)
|
19
|
+
[:ago, :until, :none].inject({}) do |final, direction|
|
20
|
+
final[direction] = TimespanTokenizer::VALID_UNITS.inject({}) do |unit_hash, unit|
|
21
|
+
unit_hash[unit] = tokenizer.all_types_for(unit, direction).inject({}) do |type_hash, type|
|
22
|
+
type_hash[type] = Plurals::Rules.all_for(@locale).inject({}) do |rule_hash, rule|
|
23
|
+
rule_hash[rule] = tokenizer.tokens(:direction => direction, :unit => unit, :rule => rule, :type => type)
|
24
|
+
rule_hash
|
25
|
+
end
|
26
|
+
type_hash
|
27
|
+
end
|
28
|
+
unit_hash
|
29
|
+
end
|
30
|
+
final
|
31
|
+
end.to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|