refinerycms-i18n 0.9.8
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/REFINERY_README +8 -0
- data/Rakefile +34 -0
- data/config/locales/ar.yml +121 -0
- data/config/locales/bg.yml +198 -0
- data/config/locales/bn-IN.yml +180 -0
- data/config/locales/bs.yml +114 -0
- data/config/locales/ca-ES.yml +155 -0
- data/config/locales/cz.rb +166 -0
- data/config/locales/da.yml +167 -0
- data/config/locales/de-AT.yml +140 -0
- data/config/locales/de.yml +141 -0
- data/config/locales/dsb.yml +182 -0
- data/config/locales/el.yml +191 -0
- data/config/locales/es-AR.yml +168 -0
- data/config/locales/es-CO.yml +146 -0
- data/config/locales/es-MX.yml +118 -0
- data/config/locales/es.yml +186 -0
- data/config/locales/et.yml +109 -0
- data/config/locales/fa.yml +119 -0
- data/config/locales/fi.yml +154 -0
- data/config/locales/fr-CH.yml +123 -0
- data/config/locales/fr.yml +140 -0
- data/config/locales/fun/en-AU.rb +105 -0
- data/config/locales/fun/gibberish.rb +109 -0
- data/config/locales/fur.yml +141 -0
- data/config/locales/gl-ES.yml +193 -0
- data/config/locales/he.yml +103 -0
- data/config/locales/hr.yml +116 -0
- data/config/locales/hsb.yml +190 -0
- data/config/locales/hu.yml +144 -0
- data/config/locales/id.yml +122 -0
- data/config/locales/is.yml +142 -0
- data/config/locales/it.yml +146 -0
- data/config/locales/ja.yml +139 -0
- data/config/locales/ko.yml +153 -0
- data/config/locales/lo.yml +201 -0
- data/config/locales/lt.yml +141 -0
- data/config/locales/lv.yml +132 -0
- data/config/locales/mk.yml +115 -0
- data/config/locales/nb.yml +103 -0
- data/config/locales/nl.yml +173 -0
- data/config/locales/nn.yml +96 -0
- data/config/locales/pl.yml +127 -0
- data/config/locales/pt-BR.yml +148 -0
- data/config/locales/pt-PT.yml +220 -0
- data/config/locales/rm.yml +134 -0
- data/config/locales/ro.yml +152 -0
- data/config/locales/ru.yml +210 -0
- data/config/locales/sk.yml +139 -0
- data/config/locales/sl.yml +190 -0
- data/config/locales/sr-Latn.yml +116 -0
- data/config/locales/sr.yml +116 -0
- data/config/locales/sv-SE.yml +191 -0
- data/config/locales/sw.yml +123 -0
- data/config/locales/th.rb +222 -0
- data/config/locales/tr.yml +139 -0
- data/config/locales/uk.yml +237 -0
- data/config/locales/vi.yml +198 -0
- data/config/locales/zh-CN.yml +131 -0
- data/config/locales/zh-TW.yml +132 -0
- data/i18n-js-readme.rdoc +249 -0
- data/lib/gemspec.rb +29 -0
- data/lib/refinery/i18n-filter.rb +38 -0
- data/lib/refinery/i18n-js.rb +132 -0
- data/lib/refinery/i18n-js.yml +4 -0
- data/lib/refinery/i18n.js +339 -0
- data/lib/refinery/i18n.rb +146 -0
- data/lib/refinery/translate.rb +13 -0
- data/lib/tasks/i18n-js_tasks.rake +16 -0
- data/lib/tasks/translate.rake +200 -0
- data/lib/translate/file.rb +35 -0
- data/lib/translate/keys.rb +123 -0
- data/lib/translate/log.rb +39 -0
- data/lib/translate/storage.rb +20 -0
- data/test/i18n-test.html +50 -0
- data/test/i18n-test.js +661 -0
- data/test/i18n_js_test.rb +168 -0
- data/test/jsunittest/jsunittest.js +1017 -0
- data/test/jsunittest/unittest.css +54 -0
- data/test/resources/custom_path.yml +4 -0
- data/test/resources/default.yml +4 -0
- data/test/resources/locales.yml +76 -0
- data/test/resources/multiple_files.yml +6 -0
- data/test/resources/no_scope.yml +3 -0
- data/test/resources/simple_scope.yml +4 -0
- data/test/test_helper.rb +24 -0
- data/translate-readme.md +61 -0
- metadata +154 -0
data/lib/gemspec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
version = '0.9.8'
|
3
|
+
raise "Could not get version so gemspec can not be built" if version.nil?
|
4
|
+
files = Dir.glob("**/*").flatten.reject do |file|
|
5
|
+
file =~ /\.gem(spec)?$/
|
6
|
+
end
|
7
|
+
|
8
|
+
gemspec = <<EOF
|
9
|
+
Gem::Specification.new do |s|
|
10
|
+
s.name = %q{refinerycms-i18n}
|
11
|
+
s.version = %q{#{version}}
|
12
|
+
s.description = %q{i18n logic extracted from RefineryCMS, for Refinery CMS.}
|
13
|
+
s.date = %q{#{Time.now.strftime('%Y-%m-%d')}}
|
14
|
+
s.summary = %q{i18n logic for Refinery CMS.}
|
15
|
+
s.email = %q{info@refinerycms.com}
|
16
|
+
s.homepage = %q{http://refinerycms.com}
|
17
|
+
s.authors = %w(Resolve\\ Digital)
|
18
|
+
s.require_paths = %w(lib)
|
19
|
+
|
20
|
+
s.files = [
|
21
|
+
'#{files.join("',\n '")}'
|
22
|
+
]
|
23
|
+
#{"s.test_files = [
|
24
|
+
'#{Dir.glob("test/**/*.rb").join("',\n '")}'
|
25
|
+
]" if File.directory?("test")}
|
26
|
+
end
|
27
|
+
EOF
|
28
|
+
|
29
|
+
File.open(File.expand_path("../../refinerycms-i18n.gemspec", __FILE__), 'w').puts(gemspec)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RoutingFilter
|
2
|
+
class RefineryLocales < Filter
|
3
|
+
|
4
|
+
def around_recognize(path, env, &block)
|
5
|
+
if ::Refinery::I18n.enabled?
|
6
|
+
if path =~ %r{^/(#{::Refinery::I18n.locales.keys.join('|')})/?}
|
7
|
+
path.sub! %r(^/(([a-zA-Z\-_])*)(?=/|$)) do
|
8
|
+
::I18n.locale = $1
|
9
|
+
''
|
10
|
+
end
|
11
|
+
path.sub!(%r{^$}) { '/' }
|
12
|
+
else
|
13
|
+
::I18n.locale = ::Refinery::I18n.default_frontend_locale
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
yield.tap do |params|
|
18
|
+
params[:locale] = ::I18n.locale if ::Refinery::I18n.enabled?
|
19
|
+
params
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def around_generate(params, &block)
|
24
|
+
locale = params.delete(:locale) || ::I18n.locale
|
25
|
+
|
26
|
+
yield.tap do |result|
|
27
|
+
if ::Refinery::I18n.enabled? and
|
28
|
+
locale != ::Refinery::I18n.default_frontend_locale and
|
29
|
+
result !~ %r{^/(refinery|wymiframe)}
|
30
|
+
result.sub!(%r(^(http.?://[^/]*)?(.*))) { "#{$1}/#{locale}#{$2}" }
|
31
|
+
end
|
32
|
+
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module SimplesIdeias
|
2
|
+
module I18n
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
6
|
+
MERGER = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2 }
|
7
|
+
|
8
|
+
# Set configuration file path
|
9
|
+
CONFIG_FILE = Rails.root.join("config/i18n-js.yml")
|
10
|
+
|
11
|
+
# Set i18n.js output path
|
12
|
+
JAVASCRIPT_FILE = Rails.root.join("public/javascripts/i18n.js")
|
13
|
+
|
14
|
+
# Export translations to JavaScript, considering settings
|
15
|
+
# from configuration file
|
16
|
+
def export!
|
17
|
+
if config?
|
18
|
+
for options in config[:translations]
|
19
|
+
options.reverse_merge!(:only => "*")
|
20
|
+
|
21
|
+
if options[:only] == "*"
|
22
|
+
save translations, options[:file]
|
23
|
+
else
|
24
|
+
result = scoped_translations(options[:only])
|
25
|
+
save result, options[:file] unless result.empty?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
save translations, "public/javascripts/translations.js"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Load configuration file for partial exporting and
|
34
|
+
# custom output directory
|
35
|
+
def config
|
36
|
+
HashWithIndifferentAccess.new YAML.load_file(CONFIG_FILE)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check if configuration file exist
|
40
|
+
def config?
|
41
|
+
File.file? CONFIG_FILE
|
42
|
+
end
|
43
|
+
|
44
|
+
# Copy configuration and JavaScript library files to
|
45
|
+
# <tt>SimplesIdeias::I18n::CONFIG_FILE</tt> and <tt>public/i18n.js</tt>.
|
46
|
+
def setup!
|
47
|
+
FileUtils.cp File.dirname(__FILE__) + "/i18n.js", JAVASCRIPT_FILE
|
48
|
+
FileUtils.cp(File.dirname(__FILE__) + "/i18n-js.yml", CONFIG_FILE) unless config?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Retrieve an updated JavaScript library from Github.
|
52
|
+
def update!
|
53
|
+
require "open-uri"
|
54
|
+
contents = open("http://github.com/fnando/i18n-js/raw/master/lib/i18n.js").read
|
55
|
+
File.open(JAVASCRIPT_FILE, "w+") {|f| f << contents}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Convert translations to JSON string and save file.
|
59
|
+
def save(translations, file)
|
60
|
+
file = Rails.root.join(file)
|
61
|
+
FileUtils.mkdir_p File.dirname(file)
|
62
|
+
|
63
|
+
File.open(file, "w+") do |f|
|
64
|
+
f << %(var I18n = I18n || {};\n)
|
65
|
+
f << %(I18n.translations = );
|
66
|
+
f << sorted_hash(translations).to_json
|
67
|
+
f << %(;)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def scoped_translations(scopes) # :nodoc:
|
72
|
+
result = {}
|
73
|
+
|
74
|
+
[scopes].flatten.each do |scope|
|
75
|
+
deep_merge! result, filter(translations, scope)
|
76
|
+
end
|
77
|
+
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
# Filter translations according to the specified scope.
|
82
|
+
def filter(translations, scopes)
|
83
|
+
scopes = scopes.split(".") if scopes.is_a?(String)
|
84
|
+
scopes = scopes.clone
|
85
|
+
scope = scopes.shift
|
86
|
+
|
87
|
+
if scope == "*"
|
88
|
+
results = {}
|
89
|
+
translations.each do |scope, translations|
|
90
|
+
tmp = scopes.empty? ? translations : filter(translations, scopes)
|
91
|
+
results[scope.to_sym] = tmp unless tmp.nil?
|
92
|
+
end
|
93
|
+
return results
|
94
|
+
elsif translations.has_key?(scope.to_sym)
|
95
|
+
return {scope.to_sym => scopes.empty? ? translations[scope.to_sym] : filter(translations[scope.to_sym], scopes)}
|
96
|
+
end
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Initialize and return translations
|
101
|
+
def translations
|
102
|
+
::I18n.backend.instance_eval do
|
103
|
+
init_translations unless initialized?
|
104
|
+
translations
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def deep_merge(target, hash) # :nodoc:
|
109
|
+
target.merge(hash, &MERGER)
|
110
|
+
end
|
111
|
+
|
112
|
+
def deep_merge!(target, hash) # :nodoc:
|
113
|
+
target.merge!(hash, &MERGER)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Taken from http://seb.box.re/2010/1/15/deep-hash-ordering-with-ruby-1-8/
|
117
|
+
def sorted_hash(object, deep = false) # :nodoc:
|
118
|
+
if object.is_a?(Hash)
|
119
|
+
res = returning(ActiveSupport::OrderedHash.new) do |map|
|
120
|
+
object.each {|k, v| map[k] = deep ? sorted_hash(v, deep) : v }
|
121
|
+
end
|
122
|
+
return res.class[res.sort {|a, b| a[0].to_s <=> b[0].to_s } ]
|
123
|
+
elsif deep && object.is_a?(Array)
|
124
|
+
array = Array.new
|
125
|
+
object.each_with_index {|v, i| array[i] = sorted_hash(v, deep) }
|
126
|
+
return array
|
127
|
+
else
|
128
|
+
return object
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
// Instantiate the object
|
2
|
+
var I18n = I18n || {};
|
3
|
+
|
4
|
+
// Set default locale to english
|
5
|
+
I18n.defaultLocale = "en";
|
6
|
+
|
7
|
+
// Set current locale to null
|
8
|
+
I18n.locale = null;
|
9
|
+
|
10
|
+
I18n.lookup = function(scope, options) {
|
11
|
+
var translations = this.prepareOptions(I18n.translations);
|
12
|
+
var messages = translations[I18n.currentLocale()];
|
13
|
+
options = this.prepareOptions(options);
|
14
|
+
|
15
|
+
if (!messages) {
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
|
19
|
+
if (typeof(scope) == "object") {
|
20
|
+
scope = scope.join(".");
|
21
|
+
}
|
22
|
+
|
23
|
+
if (options.scope) {
|
24
|
+
scope = options.scope.toString() + "." + scope;
|
25
|
+
}
|
26
|
+
|
27
|
+
scope = scope.split(".");
|
28
|
+
|
29
|
+
while (scope.length > 0) {
|
30
|
+
var currentScope = scope.shift();
|
31
|
+
messages = messages[currentScope];
|
32
|
+
|
33
|
+
if (!messages) {
|
34
|
+
break;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
if (!messages && options.defaultValue != null && options.defaultValue != undefined) {
|
39
|
+
messages = options.defaultValue;
|
40
|
+
}
|
41
|
+
|
42
|
+
return messages;
|
43
|
+
};
|
44
|
+
|
45
|
+
// Merge serveral hash options, checking if value is set before
|
46
|
+
// overwriting any value. The precedence is from left to right.
|
47
|
+
//
|
48
|
+
// I18n.prepareOptions({name: "John Doe"}, {name: "Mary Doe", role: "user"});
|
49
|
+
// #=> {name: "John Doe", role: "user"}
|
50
|
+
//
|
51
|
+
I18n.prepareOptions = function() {
|
52
|
+
var options = {};
|
53
|
+
var opts;
|
54
|
+
var count = arguments.length;
|
55
|
+
|
56
|
+
for (var i = 0; i < count; i++) {
|
57
|
+
opts = arguments[i];
|
58
|
+
|
59
|
+
if (!opts) {
|
60
|
+
continue;
|
61
|
+
}
|
62
|
+
|
63
|
+
for (var key in opts) {
|
64
|
+
if (options[key] == undefined || options[key] == null) {
|
65
|
+
options[key] = opts[key];
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
return options;
|
71
|
+
};
|
72
|
+
|
73
|
+
I18n.interpolate = function(message, options) {
|
74
|
+
options = this.prepareOptions(options);
|
75
|
+
var regex = /\{\{(.*?)\}\}/gm;
|
76
|
+
|
77
|
+
var matches = message.match(regex);
|
78
|
+
|
79
|
+
if (!matches) {
|
80
|
+
return message;
|
81
|
+
}
|
82
|
+
|
83
|
+
var placeholder, value, name;
|
84
|
+
|
85
|
+
for (var i = 0; placeholder = matches[i]; i++) {
|
86
|
+
name = placeholder.replace(/\{\{(.*?)\}\}/gm, "$1");
|
87
|
+
|
88
|
+
value = options[name];
|
89
|
+
|
90
|
+
if (options[name] == null || options[name] == undefined) {
|
91
|
+
value = "[missing " + placeholder + " value]";
|
92
|
+
}
|
93
|
+
|
94
|
+
regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}"));
|
95
|
+
|
96
|
+
message = message.replace(regex, value);
|
97
|
+
}
|
98
|
+
|
99
|
+
return message;
|
100
|
+
};
|
101
|
+
|
102
|
+
I18n.translate = function(scope, options) {
|
103
|
+
options = this.prepareOptions(options);
|
104
|
+
var translation = this.lookup(scope, options);
|
105
|
+
|
106
|
+
try {
|
107
|
+
if (typeof(translation) == "object") {
|
108
|
+
if (typeof(options.count) == "number") {
|
109
|
+
return this.pluralize(options.count, scope, options);
|
110
|
+
} else {
|
111
|
+
return translation;
|
112
|
+
}
|
113
|
+
} else {
|
114
|
+
return this.interpolate(translation, options);
|
115
|
+
}
|
116
|
+
} catch(err) {
|
117
|
+
return this.missingTranslation(scope);
|
118
|
+
}
|
119
|
+
};
|
120
|
+
|
121
|
+
I18n.localize = function(scope, value) {
|
122
|
+
switch (scope) {
|
123
|
+
case "currency":
|
124
|
+
return this.toCurrency(value);
|
125
|
+
case "number":
|
126
|
+
scope = this.lookup("number.format");
|
127
|
+
return this.toNumber(value, scope);
|
128
|
+
case "percentage":
|
129
|
+
return this.toPercentage(value);
|
130
|
+
default:
|
131
|
+
if (scope.match(/^(date|time)/)) {
|
132
|
+
return this.toTime(scope, value);
|
133
|
+
} else {
|
134
|
+
return value.toString();
|
135
|
+
}
|
136
|
+
}
|
137
|
+
};
|
138
|
+
|
139
|
+
I18n.parseDate = function(d) {
|
140
|
+
var matches, date;
|
141
|
+
|
142
|
+
if (matches = d.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ |T](\d{2}):(\d{2}):(\d{2}))?(Z)?/)) {
|
143
|
+
// date/time strings: yyyy-mm-dd hh:mm:ss or yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ
|
144
|
+
for (var i = 1; i <= 6; i++) {
|
145
|
+
matches[i] = parseInt(matches[i], 10) || 0;
|
146
|
+
}
|
147
|
+
|
148
|
+
// month starts on 0
|
149
|
+
matches[2] -= 1;
|
150
|
+
|
151
|
+
if (matches[7]) {
|
152
|
+
date = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]));
|
153
|
+
} else {
|
154
|
+
date = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]);
|
155
|
+
}
|
156
|
+
} else if (typeof(d) == "number") {
|
157
|
+
// UNIX timestamp
|
158
|
+
date = new Date();
|
159
|
+
date.setTime(d);
|
160
|
+
} else {
|
161
|
+
// an arbitrary javascript string
|
162
|
+
date = new Date();
|
163
|
+
date.setTime(Date.parse(d));
|
164
|
+
}
|
165
|
+
|
166
|
+
return date;
|
167
|
+
};
|
168
|
+
|
169
|
+
I18n.toTime = function(scope, d) {
|
170
|
+
var date = this.parseDate(d);
|
171
|
+
var format = this.lookup(scope);
|
172
|
+
|
173
|
+
if (date.toString().match(/invalid/i)) {
|
174
|
+
return date.toString();
|
175
|
+
}
|
176
|
+
|
177
|
+
if (!format) {
|
178
|
+
return date.toString();
|
179
|
+
}
|
180
|
+
|
181
|
+
return this.strftime(date, format);
|
182
|
+
};
|
183
|
+
|
184
|
+
I18n.strftime = function(date, format) {
|
185
|
+
var options = this.lookup("date");
|
186
|
+
|
187
|
+
if (!options) {
|
188
|
+
return date.toString();
|
189
|
+
}
|
190
|
+
|
191
|
+
var weekDay = date.getDay();
|
192
|
+
var day = date.getDate();
|
193
|
+
var year = date.getFullYear();
|
194
|
+
var month = date.getMonth() + 1;
|
195
|
+
var hour = date.getHours();
|
196
|
+
var hour12 = hour;
|
197
|
+
var meridian = hour > 12? "PM" : "AM";
|
198
|
+
var secs = date.getSeconds();
|
199
|
+
var mins = date.getMinutes();
|
200
|
+
var offset = date.getTimezoneOffset();
|
201
|
+
var absOffsetHours = Math.floor(Math.abs(offset / 60));
|
202
|
+
var absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60);
|
203
|
+
var timezoneoffset = (offset > 0 ? "-" : "+") + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes);
|
204
|
+
|
205
|
+
if (hour12 > 12) {
|
206
|
+
hour12 = hour12 - 12;
|
207
|
+
}
|
208
|
+
|
209
|
+
var padding = function(n) {
|
210
|
+
var s = "0" + n.toString();
|
211
|
+
return s.substr(s.length - 2);
|
212
|
+
}
|
213
|
+
|
214
|
+
var f = format;
|
215
|
+
f = f.replace("%a", options["abbr_day_names"][weekDay]);
|
216
|
+
f = f.replace("%A", options["day_names"][weekDay]);
|
217
|
+
f = f.replace("%b", options["abbr_month_names"][month]);
|
218
|
+
f = f.replace("%B", options["month_names"][month]);
|
219
|
+
f = f.replace("%d", padding(day));
|
220
|
+
f = f.replace("%-d", day);
|
221
|
+
f = f.replace("%H", padding(hour));
|
222
|
+
f = f.replace("%-H", hour);
|
223
|
+
f = f.replace("%I", padding(hour12));
|
224
|
+
f = f.replace("%-I", hour12);
|
225
|
+
f = f.replace("%m", padding(month));
|
226
|
+
f = f.replace("%-m", month);
|
227
|
+
f = f.replace("%M", padding(mins));
|
228
|
+
f = f.replace("%-M", mins);
|
229
|
+
f = f.replace("%p", meridian);
|
230
|
+
f = f.replace("%S", padding(secs));
|
231
|
+
f = f.replace("%-S", secs);
|
232
|
+
f = f.replace("%w", weekDay);
|
233
|
+
f = f.replace("%y", padding(year));
|
234
|
+
f = f.replace("%-y", padding(year).replace(/^0+/, ""));
|
235
|
+
f = f.replace("%Y", year);
|
236
|
+
f = f.replace("%z", timezoneoffset);
|
237
|
+
|
238
|
+
return f;
|
239
|
+
};
|
240
|
+
|
241
|
+
I18n.toNumber = function(number, options) {
|
242
|
+
options = this.prepareOptions(
|
243
|
+
options,
|
244
|
+
this.lookup("number.format"),
|
245
|
+
{precision: 3, separator: ".", delimiter: ","}
|
246
|
+
);
|
247
|
+
|
248
|
+
var string = number.toFixed(options["precision"]).toString();
|
249
|
+
var parts = string.split(".");
|
250
|
+
|
251
|
+
number = parts[0];
|
252
|
+
var precision = parts[1];
|
253
|
+
|
254
|
+
var n = [];
|
255
|
+
|
256
|
+
while (number.length > 0) {
|
257
|
+
n.unshift(number.substr(Math.max(0, number.length - 3), 3));
|
258
|
+
number = number.substr(0, number.length -3);
|
259
|
+
}
|
260
|
+
|
261
|
+
var formattedNumber = n.join(options["delimiter"]);
|
262
|
+
|
263
|
+
if (options["precision"] > 0) {
|
264
|
+
formattedNumber += options["separator"] + parts[1];
|
265
|
+
}
|
266
|
+
|
267
|
+
return formattedNumber;
|
268
|
+
};
|
269
|
+
|
270
|
+
I18n.toCurrency = function(number, options) {
|
271
|
+
options = this.prepareOptions(
|
272
|
+
options,
|
273
|
+
this.lookup("number.currency.format"),
|
274
|
+
this.lookup("number.format"),
|
275
|
+
{unit: "$", precision: 2, format: "%u%n", delimiter: ",", separator: "."}
|
276
|
+
);
|
277
|
+
|
278
|
+
number = this.toNumber(number, options);
|
279
|
+
number = options["format"]
|
280
|
+
.replace("%u", options["unit"])
|
281
|
+
.replace("%n", number);
|
282
|
+
|
283
|
+
return number;
|
284
|
+
};
|
285
|
+
|
286
|
+
I18n.toPercentage = function(number, options) {
|
287
|
+
options = this.prepareOptions(
|
288
|
+
options,
|
289
|
+
this.lookup("number.percentage.format"),
|
290
|
+
this.lookup("number.format"),
|
291
|
+
{precision: 3, separator: ".", delimiter: ""}
|
292
|
+
);
|
293
|
+
|
294
|
+
number = this.toNumber(number, options);
|
295
|
+
return number + "%";
|
296
|
+
};
|
297
|
+
|
298
|
+
I18n.pluralize = function(count, scope, options) {
|
299
|
+
var translation = this.lookup(scope, options);
|
300
|
+
|
301
|
+
var message;
|
302
|
+
options = this.prepareOptions(options);
|
303
|
+
options["count"] = count.toString();
|
304
|
+
|
305
|
+
switch(Math.abs(count)) {
|
306
|
+
case 0:
|
307
|
+
message = translation["zero"] || translation["none"] || translation["other"] || this.missingTranslation(scope, "zero");
|
308
|
+
break;
|
309
|
+
case 1:
|
310
|
+
message = translation["one"] || this.missingTranslation(scope, "one");;
|
311
|
+
break;
|
312
|
+
default:
|
313
|
+
message = translation["other"] || this.missingTranslation(scope, "other");;
|
314
|
+
}
|
315
|
+
|
316
|
+
return this.interpolate(message, options);
|
317
|
+
};
|
318
|
+
|
319
|
+
I18n.missingTranslation = function() {
|
320
|
+
var message = '[missing "' + this.currentLocale();
|
321
|
+
var count = arguments.length;
|
322
|
+
|
323
|
+
for (var i = 0; i < count; i++) {
|
324
|
+
message += "." + arguments[i];
|
325
|
+
}
|
326
|
+
|
327
|
+
message += '" translation]';
|
328
|
+
|
329
|
+
return message;
|
330
|
+
};
|
331
|
+
|
332
|
+
I18n.currentLocale = function() {
|
333
|
+
return (I18n.locale || I18n.defaultLocale);
|
334
|
+
};
|
335
|
+
|
336
|
+
// shortcuts
|
337
|
+
I18n.t = I18n.translate;
|
338
|
+
I18n.l = I18n.localize;
|
339
|
+
I18n.p = I18n.pluralize;
|