mlanett-i18n-js 2.1.2
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/.gitignore +6 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +51 -0
- data/README.rdoc +320 -0
- data/Rakefile +13 -0
- data/config/i18n-js.yml +22 -0
- data/i18n-js.gemspec +27 -0
- data/lib/i18n-js.rb +192 -0
- data/lib/i18n-js/engine.rb +63 -0
- data/lib/i18n-js/middleware.rb +59 -0
- data/lib/i18n-js/railtie.rb +13 -0
- data/lib/i18n-js/rake.rb +16 -0
- data/lib/i18n-js/version.rb +10 -0
- data/spec/i18n_spec.js +820 -0
- data/spec/i18n_spec.rb +205 -0
- data/spec/resources/custom_path.yml +4 -0
- data/spec/resources/default.yml +4 -0
- data/spec/resources/js_file_per_locale.yml +3 -0
- data/spec/resources/locales.yml +76 -0
- data/spec/resources/multiple_conditions.yml +6 -0
- data/spec/resources/multiple_files.yml +6 -0
- data/spec/resources/no_config.yml +2 -0
- data/spec/resources/no_scope.yml +3 -0
- data/spec/resources/simple_scope.yml +4 -0
- data/spec/spec_helper.rb +19 -0
- data/vendor/assets/javascripts/i18n.js +706 -0
- data/vendor/assets/javascripts/i18n/translations.js.erb +9 -0
- metadata +197 -0
data/lib/i18n-js.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
require "FileUtils" unless defined?(FileUtils)
|
2
|
+
|
3
|
+
module SimplesIdeias
|
4
|
+
module I18n
|
5
|
+
extend self
|
6
|
+
|
7
|
+
require "i18n-js/railtie" if Rails.version >= "3.0"
|
8
|
+
require "i18n-js/engine" if Rails.version >= "3.1"
|
9
|
+
require "i18n-js/middleware"
|
10
|
+
|
11
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
12
|
+
MERGER = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2 }
|
13
|
+
|
14
|
+
# Under rails 3.1.1 and higher, perform a check to ensure that the
|
15
|
+
# full environment will be available during asset compilation.
|
16
|
+
# This is required to ensure I18n is loaded.
|
17
|
+
def assert_usable_configuration!
|
18
|
+
@usable_configuration ||= Rails.version >= "3.1.1" &&
|
19
|
+
Rails.configuration.assets.initialize_on_precompile ||
|
20
|
+
raise("Cannot precompile i18n-js translations unless environment is initialized. Please set config.assets.initialize_on_precompile to true.")
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_asset_pipeline?
|
24
|
+
Rails.configuration.respond_to?(:assets) && Rails.configuration.assets.enabled
|
25
|
+
end
|
26
|
+
|
27
|
+
def config_file
|
28
|
+
Rails.root.join("config/i18n-js.yml")
|
29
|
+
end
|
30
|
+
|
31
|
+
def export_dir
|
32
|
+
if has_asset_pipeline?
|
33
|
+
"app/assets/javascripts/i18n"
|
34
|
+
else
|
35
|
+
"public/javascripts"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def javascript_file
|
40
|
+
Rails.root.join(export_dir, "i18n.js")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Export translations to JavaScript, considering settings
|
44
|
+
# from configuration file
|
45
|
+
def export!
|
46
|
+
translation_segments.each do |filename, translations|
|
47
|
+
save(translations, filename)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def segments_per_locale(pattern,scope)
|
52
|
+
::I18n.available_locales.each_with_object({}) do |locale,segments|
|
53
|
+
result = scoped_translations("#{locale}.#{scope}")
|
54
|
+
unless result.empty?
|
55
|
+
segment_name = ::I18n.interpolate(pattern,{:locale => locale})
|
56
|
+
segments[segment_name] = result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def segment_for_scope(scope)
|
62
|
+
if scope == "*"
|
63
|
+
translations
|
64
|
+
else
|
65
|
+
scoped_translations(scope)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def configured_segments
|
70
|
+
config[:translations].each_with_object({}) do |options,segments|
|
71
|
+
options.reverse_merge!(:only => "*")
|
72
|
+
if options[:file] =~ ::I18n::INTERPOLATION_PATTERN
|
73
|
+
segments.merge!(segments_per_locale(options[:file],options[:only]))
|
74
|
+
else
|
75
|
+
result = segment_for_scope(options[:only])
|
76
|
+
segments[options[:file]] = result unless result.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def translation_segments
|
82
|
+
if config? && config[:translations]
|
83
|
+
configured_segments
|
84
|
+
else
|
85
|
+
{"#{export_dir}/translations.js" => translations}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Load configuration file for partial exporting and
|
90
|
+
# custom output directory
|
91
|
+
def config
|
92
|
+
if config?
|
93
|
+
(YAML.load_file(config_file) || {}).with_indifferent_access
|
94
|
+
else
|
95
|
+
{}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check if configuration file exist
|
100
|
+
def config?
|
101
|
+
File.file? config_file
|
102
|
+
end
|
103
|
+
|
104
|
+
# Copy configuration and JavaScript library files to
|
105
|
+
# <tt>config/i18n-js.yml</tt> and <tt>public/javascripts/i18n.js</tt>.
|
106
|
+
def setup!
|
107
|
+
FileUtils.cp(File.dirname(__FILE__) + "/../vendor/assets/javascripts/i18n.js", javascript_file) unless Rails.version >= "3.1"
|
108
|
+
FileUtils.cp(File.dirname(__FILE__) + "/../config/i18n-js.yml", config_file) unless config?
|
109
|
+
end
|
110
|
+
|
111
|
+
# Retrieve an updated JavaScript library from Github.
|
112
|
+
def update!
|
113
|
+
require "open-uri"
|
114
|
+
contents = open("https://raw.github.com/fnando/i18n-js/master/vendor/assets/javascripts/i18n.js").read
|
115
|
+
File.open(javascript_file, "w+") {|f| f << contents}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Convert translations to JSON string and save file.
|
119
|
+
def save(translations, file)
|
120
|
+
file = Rails.root.join(file)
|
121
|
+
FileUtils.mkdir_p File.dirname(file)
|
122
|
+
|
123
|
+
File.open(file, "w+") do |f|
|
124
|
+
f << %(var I18n = I18n || {};\n)
|
125
|
+
f << %(I18n.translations = $.extend\(true, I18n.translations, )
|
126
|
+
f << deep_sort(translations).to_json
|
127
|
+
f << %(\);)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def scoped_translations(scopes) # :nodoc:
|
132
|
+
result = {}
|
133
|
+
|
134
|
+
[scopes].flatten.each do |scope|
|
135
|
+
deep_merge! result, filter(translations, scope)
|
136
|
+
end
|
137
|
+
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
# Filter translations according to the specified scope.
|
142
|
+
def filter(translations, scopes)
|
143
|
+
scopes = scopes.split(".") if scopes.is_a?(String)
|
144
|
+
scopes = scopes.clone
|
145
|
+
scope = scopes.shift
|
146
|
+
|
147
|
+
if scope == "*"
|
148
|
+
results = {}
|
149
|
+
translations.each do |scope, translations|
|
150
|
+
tmp = scopes.empty? ? translations : filter(translations, scopes)
|
151
|
+
results[scope.to_sym] = tmp unless tmp.nil?
|
152
|
+
end
|
153
|
+
return results
|
154
|
+
elsif translations.has_key?(scope.to_sym)
|
155
|
+
return {scope.to_sym => scopes.empty? ? translations[scope.to_sym] : filter(translations[scope.to_sym], scopes)}
|
156
|
+
end
|
157
|
+
nil
|
158
|
+
end
|
159
|
+
|
160
|
+
# Initialize and return translations
|
161
|
+
def translations
|
162
|
+
::I18n.backend.instance_eval do
|
163
|
+
init_translations unless initialized?
|
164
|
+
translations
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def deep_merge(target, hash) # :nodoc:
|
169
|
+
target.merge(hash, &MERGER)
|
170
|
+
end
|
171
|
+
|
172
|
+
def deep_merge!(target, hash) # :nodoc:
|
173
|
+
target.merge!(hash, &MERGER)
|
174
|
+
end
|
175
|
+
|
176
|
+
def deep_sort(hash)
|
177
|
+
case hash
|
178
|
+
when Hash
|
179
|
+
result = Hash[hash.sort { |a, b| a[0].to_s <=> b[0].to_s } ]
|
180
|
+
result.each do |key, value|
|
181
|
+
result[key] = deep_sort(value)
|
182
|
+
end
|
183
|
+
result
|
184
|
+
when Array
|
185
|
+
hash.map { |value| deep_sort(value) }
|
186
|
+
else
|
187
|
+
hash
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module SimplesIdeias
|
2
|
+
module I18n
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
I18N_TRANSLATIONS_ASSET = "i18n/translations"
|
5
|
+
|
6
|
+
initializer "i18n-js.asset_dependencies", :after => "sprockets.environment",
|
7
|
+
:before => "i18n-js.initialize" do
|
8
|
+
next unless SimplesIdeias::I18n.has_asset_pipeline?
|
9
|
+
|
10
|
+
config = I18n.config_file
|
11
|
+
cache_file = I18n::Engine.load_path_hash_cache
|
12
|
+
|
13
|
+
Rails.application.assets.register_preprocessor "application/javascript", :"i18n-js_dependencies" do |context, data|
|
14
|
+
if context.logical_path == I18N_TRANSLATIONS_ASSET
|
15
|
+
context.depend_on(config) if I18n.config?
|
16
|
+
# also set up dependencies on every locale file
|
17
|
+
::I18n.load_path.each {|path| context.depend_on(path)}
|
18
|
+
|
19
|
+
# Set up a dependency on the contents of the load path
|
20
|
+
# itself. In some situations it is possible to get here
|
21
|
+
# before the path hash cache file has been written; in
|
22
|
+
# this situation, write it now.
|
23
|
+
I18n::Engine.write_hash! unless File.exists?(cache_file)
|
24
|
+
context.depend_on(cache_file)
|
25
|
+
end
|
26
|
+
|
27
|
+
data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# rewrite path cache hash at startup and before each request in development
|
32
|
+
config.to_prepare do
|
33
|
+
next unless SimplesIdeias::I18n.has_asset_pipeline?
|
34
|
+
SimplesIdeias::I18n::Engine.write_hash_if_changed unless Rails.env.production?
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_path_hash_cache
|
38
|
+
@load_path_hash_cache ||= Rails.root.join("tmp/i18n-js.cache")
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.write_hash_if_changed
|
42
|
+
load_path_hash = ::I18n.load_path.hash
|
43
|
+
|
44
|
+
if load_path_hash != cached_load_path_hash
|
45
|
+
self.cached_load_path_hash = load_path_hash
|
46
|
+
write_hash!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.write_hash!
|
51
|
+
FileUtils.mkdir_p Rails.root.join("tmp")
|
52
|
+
|
53
|
+
File.open(load_path_hash_cache, "w+") do |f|
|
54
|
+
f.write(cached_load_path_hash)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
attr_accessor :cached_load_path_hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SimplesIdeias
|
2
|
+
module I18n
|
3
|
+
class Middleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
@cache = nil
|
10
|
+
verify_locale_files!
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def cache_path
|
16
|
+
@cache_path ||= Rails.root.join("tmp/cache/i18n-js.yml")
|
17
|
+
end
|
18
|
+
|
19
|
+
def cache
|
20
|
+
@cache ||= begin
|
21
|
+
if cache_path.exist?
|
22
|
+
YAML.load_file(cache_path) || {}
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check if translations should be regenerated.
|
30
|
+
# ONLY REGENERATE when these conditions are met:
|
31
|
+
#
|
32
|
+
# # Cache file doesn't exist
|
33
|
+
# # Translations and cache size are different (files were removed/added)
|
34
|
+
# # Translation file has been updated
|
35
|
+
#
|
36
|
+
def verify_locale_files!
|
37
|
+
valid_cache = []
|
38
|
+
new_cache = {}
|
39
|
+
|
40
|
+
valid_cache.push cache_path.exist?
|
41
|
+
valid_cache.push ::I18n.load_path.uniq.size == cache.size
|
42
|
+
|
43
|
+
::I18n.load_path.each do |path|
|
44
|
+
changed_at = File.mtime(path).to_i
|
45
|
+
valid_cache.push changed_at == cache[path]
|
46
|
+
new_cache[path] = changed_at
|
47
|
+
end
|
48
|
+
|
49
|
+
unless valid_cache.all?
|
50
|
+
File.open(cache_path, "w+") do |file|
|
51
|
+
file << new_cache.to_yaml
|
52
|
+
end
|
53
|
+
|
54
|
+
SimplesIdeias::I18n.export!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SimplesIdeias
|
2
|
+
module I18n
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
rake_tasks do
|
5
|
+
require "i18n-js/rake"
|
6
|
+
end
|
7
|
+
|
8
|
+
initializer "i18n-js.initialize" do |app|
|
9
|
+
app.config.middleware.use(Middleware) if (Rails.env.development? || Rails.env.test?) && !SimplesIdeias::I18n.has_asset_pipeline?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/i18n-js/rake.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
namespace "i18n:js" do
|
2
|
+
desc "Copy i18n.js and configuration file"
|
3
|
+
task :setup => :environment do
|
4
|
+
SimplesIdeias::I18n.setup!
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Export the messages files"
|
8
|
+
task :export => :environment do
|
9
|
+
SimplesIdeias::I18n.export!
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Update the JavaScript library"
|
13
|
+
task :update => :environment do
|
14
|
+
SimplesIdeias::I18n.update!
|
15
|
+
end
|
16
|
+
end
|
data/spec/i18n_spec.js
ADDED
@@ -0,0 +1,820 @@
|
|
1
|
+
load("vendor/assets/javascripts/i18n.js");
|
2
|
+
|
3
|
+
describe("I18n.js", function(){
|
4
|
+
before(function() {
|
5
|
+
I18n.defaultLocale = "en";
|
6
|
+
I18n.defaultSeparator = ".";
|
7
|
+
I18n.locale = null;
|
8
|
+
|
9
|
+
I18n.translations = {
|
10
|
+
en: {
|
11
|
+
hello: "Hello World!",
|
12
|
+
greetings: {
|
13
|
+
stranger: "Hello stranger!",
|
14
|
+
name: "Hello {{name}}!"
|
15
|
+
},
|
16
|
+
profile: {
|
17
|
+
details: "{{name}} is {{age}}-years old"
|
18
|
+
},
|
19
|
+
inbox: {
|
20
|
+
one: "You have {{count}} message",
|
21
|
+
other: "You have {{count}} messages",
|
22
|
+
zero: "You have no messages"
|
23
|
+
},
|
24
|
+
unread: {
|
25
|
+
one: "You have 1 new message ({{unread}} unread)",
|
26
|
+
other: "You have {{count}} new messages ({{unread}} unread)",
|
27
|
+
zero: "You have no new messages ({{unread}} unread)"
|
28
|
+
},
|
29
|
+
number: {
|
30
|
+
human: {
|
31
|
+
storage_units: {
|
32
|
+
units: {
|
33
|
+
"byte": {
|
34
|
+
one: "Byte",
|
35
|
+
other: "Bytes"
|
36
|
+
},
|
37
|
+
|
38
|
+
"kb": "KB",
|
39
|
+
"mb": "MB",
|
40
|
+
"gb": "GB",
|
41
|
+
"tb": "TB"
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
|
48
|
+
"pt-BR": {
|
49
|
+
hello: "Olá Mundo!",
|
50
|
+
date: {
|
51
|
+
formats: {
|
52
|
+
"default": "%d/%m/%Y",
|
53
|
+
"short": "%d de %B",
|
54
|
+
"long": "%d de %B de %Y"
|
55
|
+
},
|
56
|
+
day_names: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
|
57
|
+
abbr_day_names: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
|
58
|
+
month_names: [null, "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
|
59
|
+
abbr_month_names: [null, "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
|
60
|
+
},
|
61
|
+
number: {
|
62
|
+
percentage: {
|
63
|
+
format: {
|
64
|
+
delimiter: "",
|
65
|
+
separator: ",",
|
66
|
+
precision: 2
|
67
|
+
}
|
68
|
+
}
|
69
|
+
},
|
70
|
+
time: {
|
71
|
+
formats: {
|
72
|
+
"default": "%A, %d de %B de %Y, %H:%M h",
|
73
|
+
"short": "%d/%m, %H:%M h",
|
74
|
+
"long": "%A, %d de %B de %Y, %H:%M h"
|
75
|
+
},
|
76
|
+
am: "AM",
|
77
|
+
pm: "PM"
|
78
|
+
}
|
79
|
+
},
|
80
|
+
|
81
|
+
"en-US": {
|
82
|
+
date: {
|
83
|
+
formats: {
|
84
|
+
"default": "%d/%m/%Y",
|
85
|
+
"short": "%d de %B",
|
86
|
+
"long": "%d de %B de %Y"
|
87
|
+
},
|
88
|
+
day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
89
|
+
abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
90
|
+
month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
91
|
+
abbr_month_names: [null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"],
|
92
|
+
meridian: ["am", "pm"]
|
93
|
+
}
|
94
|
+
},
|
95
|
+
|
96
|
+
"de": {
|
97
|
+
hello: "Hallo Welt!"
|
98
|
+
},
|
99
|
+
|
100
|
+
"nb": {
|
101
|
+
hello: "Hei Verden!"
|
102
|
+
}
|
103
|
+
};
|
104
|
+
});
|
105
|
+
|
106
|
+
specify("with default options", function(){
|
107
|
+
expect(I18n.defaultLocale).toBeEqualTo("en");
|
108
|
+
expect(I18n.locale).toBeEqualTo(null);
|
109
|
+
expect(I18n.currentLocale()).toBeEqualTo("en");
|
110
|
+
});
|
111
|
+
|
112
|
+
specify("with custom locale", function(){
|
113
|
+
I18n.locale = "pt-BR";
|
114
|
+
expect(I18n.currentLocale()).toBeEqualTo("pt-BR");
|
115
|
+
});
|
116
|
+
|
117
|
+
specify("aliases", function(){
|
118
|
+
expect(I18n.t).toBe(I18n.translate);
|
119
|
+
expect(I18n.l).toBe(I18n.localize);
|
120
|
+
expect(I18n.p).toBe(I18n.pluralize);
|
121
|
+
});
|
122
|
+
|
123
|
+
specify("translation with single scope", function(){
|
124
|
+
expect(I18n.t("hello")).toBeEqualTo("Hello World!");
|
125
|
+
});
|
126
|
+
|
127
|
+
specify("translation as object", function(){
|
128
|
+
expect(I18n.t("greetings")).toBeInstanceOf(Object);
|
129
|
+
});
|
130
|
+
|
131
|
+
specify("translation with invalid scope shall not block", function(){
|
132
|
+
actual = I18n.t("invalid.scope.shall.not.block");
|
133
|
+
expected = '[missing "en.invalid.scope.shall.not.block" translation]';
|
134
|
+
expect(actual).toBeEqualTo(expected);
|
135
|
+
});
|
136
|
+
|
137
|
+
specify("translation for single scope on a custom locale", function(){
|
138
|
+
I18n.locale = "pt-BR";
|
139
|
+
expect(I18n.t("hello")).toBeEqualTo("Olá Mundo!");
|
140
|
+
});
|
141
|
+
|
142
|
+
specify("translation for multiple scopes", function(){
|
143
|
+
expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
|
144
|
+
});
|
145
|
+
|
146
|
+
specify("translation with default locale option", function(){
|
147
|
+
expect(I18n.t("hello", {locale: "en"})).toBeEqualTo("Hello World!");
|
148
|
+
expect(I18n.t("hello", {locale: "pt-BR"})).toBeEqualTo("Olá Mundo!");
|
149
|
+
});
|
150
|
+
|
151
|
+
specify("translation should fall if locale is missing", function(){
|
152
|
+
I18n.locale = "pt-BR";
|
153
|
+
expect(I18n.t("greetings.stranger")).toBeEqualTo("[missing \"pt-BR.greetings.stranger\" translation]");
|
154
|
+
});
|
155
|
+
|
156
|
+
specify("translation should handle fallback if I18n.fallbacks == true", function(){
|
157
|
+
I18n.locale = "pt-BR";
|
158
|
+
I18n.fallbacks = true;
|
159
|
+
expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
|
160
|
+
});
|
161
|
+
|
162
|
+
specify("translation should handle fallback from unknown locale", function(){
|
163
|
+
I18n.locale = "fr";
|
164
|
+
I18n.fallbacks = true;
|
165
|
+
expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
|
166
|
+
});
|
167
|
+
|
168
|
+
specify("translation should handle fallback to less specific locale", function(){
|
169
|
+
I18n.locale = "de-DE";
|
170
|
+
I18n.fallbacks = true;
|
171
|
+
expect(I18n.t("hello")).toBeEqualTo("Hallo Welt!");
|
172
|
+
});
|
173
|
+
|
174
|
+
specify("translation should handle fallback via custom rules", function(){
|
175
|
+
I18n.locale = "no";
|
176
|
+
I18n.fallbacks = true;
|
177
|
+
I18n.fallbackRules.no = [ "nb" ];
|
178
|
+
expect(I18n.t("hello")).toBeEqualTo("Hei Verden!");
|
179
|
+
});
|
180
|
+
|
181
|
+
specify("single interpolation", function(){
|
182
|
+
actual = I18n.t("greetings.name", {name: "John Doe"});
|
183
|
+
expect(actual).toBeEqualTo("Hello John Doe!");
|
184
|
+
});
|
185
|
+
|
186
|
+
specify("multiple interpolation", function(){
|
187
|
+
actual = I18n.t("profile.details", {name: "John Doe", age: 27});
|
188
|
+
expect(actual).toBeEqualTo("John Doe is 27-years old");
|
189
|
+
});
|
190
|
+
|
191
|
+
specify("translation with count option", function(){
|
192
|
+
expect(I18n.t("inbox", {count: 0})).toBeEqualTo("You have no messages");
|
193
|
+
expect(I18n.t("inbox", {count: 1})).toBeEqualTo("You have 1 message");
|
194
|
+
expect(I18n.t("inbox", {count: 5})).toBeEqualTo("You have 5 messages");
|
195
|
+
});
|
196
|
+
|
197
|
+
specify("translation with count option and multiple placeholders", function(){
|
198
|
+
actual = I18n.t("unread", {unread: 5, count: 1});
|
199
|
+
expect(actual).toBeEqualTo("You have 1 new message (5 unread)");
|
200
|
+
|
201
|
+
actual = I18n.t("unread", {unread: 2, count: 10});
|
202
|
+
expect(actual).toBeEqualTo("You have 10 new messages (2 unread)");
|
203
|
+
|
204
|
+
actual = I18n.t("unread", {unread: 5, count: 0});
|
205
|
+
expect(actual).toBeEqualTo("You have no new messages (5 unread)");
|
206
|
+
});
|
207
|
+
|
208
|
+
specify("missing translation with count option", function(){
|
209
|
+
actual = I18n.t("invalid", {count: 1});
|
210
|
+
expect(actual).toBeEqualTo('[missing "en.invalid" translation]');
|
211
|
+
|
212
|
+
I18n.translations.en.inbox.one = null;
|
213
|
+
actual = I18n.t("inbox", {count: 1});
|
214
|
+
expect(actual).toBeEqualTo('[missing "en.inbox.one" translation]');
|
215
|
+
});
|
216
|
+
|
217
|
+
specify("pluralization", function(){
|
218
|
+
expect(I18n.p(0, "inbox")).toBeEqualTo("You have no messages");
|
219
|
+
expect(I18n.p(1, "inbox")).toBeEqualTo("You have 1 message");
|
220
|
+
expect(I18n.p(5, "inbox")).toBeEqualTo("You have 5 messages");
|
221
|
+
});
|
222
|
+
|
223
|
+
specify("pluralize should return 'other' scope", function(){
|
224
|
+
I18n.translations["en"]["inbox"]["zero"] = null;
|
225
|
+
expect(I18n.p(0, "inbox")).toBeEqualTo("You have 0 messages");
|
226
|
+
});
|
227
|
+
|
228
|
+
specify("pluralize should return 'zero' scope", function(){
|
229
|
+
I18n.translations["en"]["inbox"]["zero"] = "No messages (zero)";
|
230
|
+
I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
|
231
|
+
|
232
|
+
expect(I18n.p(0, "inbox")).toBeEqualTo("No messages (zero)");
|
233
|
+
});
|
234
|
+
|
235
|
+
specify("pluralize should return 'none' scope", function(){
|
236
|
+
I18n.translations["en"]["inbox"]["zero"] = null;
|
237
|
+
I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
|
238
|
+
|
239
|
+
expect(I18n.p(0, "inbox")).toBeEqualTo("No messages (none)");
|
240
|
+
});
|
241
|
+
|
242
|
+
specify("pluralize with negative values", function(){
|
243
|
+
expect(I18n.p(-1, "inbox")).toBeEqualTo("You have -1 message");
|
244
|
+
expect(I18n.p(-5, "inbox")).toBeEqualTo("You have -5 messages");
|
245
|
+
});
|
246
|
+
|
247
|
+
specify("pluralize with missing scope", function(){
|
248
|
+
expect(I18n.p(-1, "missing")).toBeEqualTo('[missing "en.missing" translation]');
|
249
|
+
});
|
250
|
+
|
251
|
+
specify("pluralize with multiple placeholders", function(){
|
252
|
+
actual = I18n.p(1, "unread", {unread: 5});
|
253
|
+
expect(actual).toBeEqualTo("You have 1 new message (5 unread)");
|
254
|
+
|
255
|
+
actual = I18n.p(10, "unread", {unread: 2});
|
256
|
+
expect(actual).toBeEqualTo("You have 10 new messages (2 unread)");
|
257
|
+
|
258
|
+
actual = I18n.p(0, "unread", {unread: 5});
|
259
|
+
expect(actual).toBeEqualTo("You have no new messages (5 unread)");
|
260
|
+
});
|
261
|
+
|
262
|
+
specify("pluralize should allow empty strings", function(){
|
263
|
+
I18n.translations["en"]["inbox"]["zero"] = "";
|
264
|
+
|
265
|
+
expect(I18n.p(0, "inbox")).toBeEqualTo("");
|
266
|
+
});
|
267
|
+
|
268
|
+
specify("numbers with default settings", function(){
|
269
|
+
expect(I18n.toNumber(1)).toBeEqualTo("1.000");
|
270
|
+
expect(I18n.toNumber(12)).toBeEqualTo("12.000");
|
271
|
+
expect(I18n.toNumber(123)).toBeEqualTo("123.000");
|
272
|
+
expect(I18n.toNumber(1234)).toBeEqualTo("1,234.000");
|
273
|
+
expect(I18n.toNumber(12345)).toBeEqualTo("12,345.000");
|
274
|
+
expect(I18n.toNumber(123456)).toBeEqualTo("123,456.000");
|
275
|
+
expect(I18n.toNumber(1234567)).toBeEqualTo("1,234,567.000");
|
276
|
+
expect(I18n.toNumber(12345678)).toBeEqualTo("12,345,678.000");
|
277
|
+
expect(I18n.toNumber(123456789)).toBeEqualTo("123,456,789.000");
|
278
|
+
});
|
279
|
+
|
280
|
+
specify("negative numbers with default settings", function(){
|
281
|
+
expect(I18n.toNumber(-1)).toBeEqualTo("-1.000");
|
282
|
+
expect(I18n.toNumber(-12)).toBeEqualTo("-12.000");
|
283
|
+
expect(I18n.toNumber(-123)).toBeEqualTo("-123.000");
|
284
|
+
expect(I18n.toNumber(-1234)).toBeEqualTo("-1,234.000");
|
285
|
+
expect(I18n.toNumber(-12345)).toBeEqualTo("-12,345.000");
|
286
|
+
expect(I18n.toNumber(-123456)).toBeEqualTo("-123,456.000");
|
287
|
+
expect(I18n.toNumber(-1234567)).toBeEqualTo("-1,234,567.000");
|
288
|
+
expect(I18n.toNumber(-12345678)).toBeEqualTo("-12,345,678.000");
|
289
|
+
expect(I18n.toNumber(-123456789)).toBeEqualTo("-123,456,789.000");
|
290
|
+
});
|
291
|
+
|
292
|
+
specify("numbers with partial translation and default options", function(){
|
293
|
+
I18n.translations.en.number = {
|
294
|
+
format: {
|
295
|
+
precision: 2
|
296
|
+
}
|
297
|
+
};
|
298
|
+
|
299
|
+
expect(I18n.toNumber(1234)).toBeEqualTo("1,234.00");
|
300
|
+
});
|
301
|
+
|
302
|
+
specify("numbers with full translation and default options", function(){
|
303
|
+
I18n.translations.en.number = {
|
304
|
+
format: {
|
305
|
+
delimiter: ".",
|
306
|
+
separator: ",",
|
307
|
+
precision: 2
|
308
|
+
}
|
309
|
+
};
|
310
|
+
|
311
|
+
expect(I18n.toNumber(1234)).toBeEqualTo("1.234,00");
|
312
|
+
});
|
313
|
+
|
314
|
+
specify("numbers with some custom options that should be merged with default options", function(){
|
315
|
+
expect(I18n.toNumber(1234, {precision: 0})).toBeEqualTo("1,234");
|
316
|
+
expect(I18n.toNumber(1234, {separator: '-'})).toBeEqualTo("1,234-000");
|
317
|
+
expect(I18n.toNumber(1234, {delimiter: '-'})).toBeEqualTo("1-234.000");
|
318
|
+
});
|
319
|
+
|
320
|
+
specify("numbers considering options", function(){
|
321
|
+
options = {
|
322
|
+
precision: 2,
|
323
|
+
separator: ",",
|
324
|
+
delimiter: "."
|
325
|
+
};
|
326
|
+
|
327
|
+
expect(I18n.toNumber(1, options)).toBeEqualTo("1,00");
|
328
|
+
expect(I18n.toNumber(12, options)).toBeEqualTo("12,00");
|
329
|
+
expect(I18n.toNumber(123, options)).toBeEqualTo("123,00");
|
330
|
+
expect(I18n.toNumber(1234, options)).toBeEqualTo("1.234,00");
|
331
|
+
expect(I18n.toNumber(123456, options)).toBeEqualTo("123.456,00");
|
332
|
+
expect(I18n.toNumber(1234567, options)).toBeEqualTo("1.234.567,00");
|
333
|
+
expect(I18n.toNumber(12345678, options)).toBeEqualTo("12.345.678,00");
|
334
|
+
});
|
335
|
+
|
336
|
+
specify("numbers with different precisions", function(){
|
337
|
+
options = {separator: ".", delimiter: ","};
|
338
|
+
|
339
|
+
options["precision"] = 2;
|
340
|
+
expect(I18n.toNumber(1.98, options)).toBeEqualTo("1.98");
|
341
|
+
|
342
|
+
options["precision"] = 3;
|
343
|
+
expect(I18n.toNumber(1.98, options)).toBeEqualTo("1.980");
|
344
|
+
|
345
|
+
options["precision"] = 2;
|
346
|
+
expect(I18n.toNumber(1.987, options)).toBeEqualTo("1.99");
|
347
|
+
|
348
|
+
options["precision"] = 1;
|
349
|
+
expect(I18n.toNumber(1.98, options)).toBeEqualTo("2.0");
|
350
|
+
|
351
|
+
options["precision"] = 0;
|
352
|
+
expect(I18n.toNumber(1.98, options)).toBeEqualTo("2");
|
353
|
+
});
|
354
|
+
|
355
|
+
specify("currency with default settings", function(){
|
356
|
+
expect(I18n.toCurrency(100.99)).toBeEqualTo("$100.99");
|
357
|
+
expect(I18n.toCurrency(1000.99)).toBeEqualTo("$1,000.99");
|
358
|
+
});
|
359
|
+
|
360
|
+
specify("currency with custom settings", function(){
|
361
|
+
I18n.translations.en.number = {
|
362
|
+
currency: {
|
363
|
+
format: {
|
364
|
+
format: "%n %u",
|
365
|
+
unit: "USD",
|
366
|
+
delimiter: ".",
|
367
|
+
separator: ",",
|
368
|
+
precision: 2
|
369
|
+
}
|
370
|
+
}
|
371
|
+
};
|
372
|
+
|
373
|
+
expect(I18n.toCurrency(12)).toBeEqualTo("12,00 USD");
|
374
|
+
expect(I18n.toCurrency(123)).toBeEqualTo("123,00 USD");
|
375
|
+
expect(I18n.toCurrency(1234.56)).toBeEqualTo("1.234,56 USD");
|
376
|
+
});
|
377
|
+
|
378
|
+
specify("currency with custom settings and partial overriding", function(){
|
379
|
+
I18n.translations.en.number = {
|
380
|
+
currency: {
|
381
|
+
format: {
|
382
|
+
format: "%n %u",
|
383
|
+
unit: "USD",
|
384
|
+
delimiter: ".",
|
385
|
+
separator: ",",
|
386
|
+
precision: 2
|
387
|
+
}
|
388
|
+
}
|
389
|
+
};
|
390
|
+
|
391
|
+
expect(I18n.toCurrency(12, {precision: 0})).toBeEqualTo("12 USD");
|
392
|
+
expect(I18n.toCurrency(123, {unit: "bucks"})).toBeEqualTo("123,00 bucks");
|
393
|
+
});
|
394
|
+
|
395
|
+
specify("currency with some custom options that should be merged with default options", function(){
|
396
|
+
expect(I18n.toCurrency(1234, {precision: 0})).toBeEqualTo("$1,234");
|
397
|
+
expect(I18n.toCurrency(1234, {unit: "º"})).toBeEqualTo("º1,234.00");
|
398
|
+
expect(I18n.toCurrency(1234, {separator: "-"})).toBeEqualTo("$1,234-00");
|
399
|
+
expect(I18n.toCurrency(1234, {delimiter: "-"})).toBeEqualTo("$1-234.00");
|
400
|
+
expect(I18n.toCurrency(1234, {format: "%u %n"})).toBeEqualTo("$ 1,234.00");
|
401
|
+
});
|
402
|
+
|
403
|
+
specify("localize numbers", function(){
|
404
|
+
expect(I18n.l("number", 1234567)).toBeEqualTo("1,234,567.000");
|
405
|
+
});
|
406
|
+
|
407
|
+
specify("localize currency", function(){
|
408
|
+
expect(I18n.l("currency", 1234567)).toBeEqualTo("$1,234,567.00");
|
409
|
+
});
|
410
|
+
|
411
|
+
specify("parse date", function(){
|
412
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
413
|
+
actual = I18n.parseDate("2009-01-24");
|
414
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
415
|
+
|
416
|
+
expected = new Date(2009, 0, 24, 0, 15, 0);
|
417
|
+
actual = I18n.parseDate("2009-01-24 00:15:00");
|
418
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
419
|
+
|
420
|
+
expected = new Date(2009, 0, 24, 0, 0, 15);
|
421
|
+
actual = I18n.parseDate("2009-01-24 00:00:15");
|
422
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
423
|
+
|
424
|
+
expected = new Date(2009, 0, 24, 15, 33, 44);
|
425
|
+
actual = I18n.parseDate("2009-01-24 15:33:44");
|
426
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
427
|
+
|
428
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
429
|
+
actual = I18n.parseDate(expected.getTime());
|
430
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
431
|
+
|
432
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
433
|
+
actual = I18n.parseDate("01/24/2009");
|
434
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
435
|
+
|
436
|
+
expected = new Date(2009, 0, 24, 14, 33, 55);
|
437
|
+
actual = I18n.parseDate(expected).toString();
|
438
|
+
expect(actual).toBeEqualTo(expected.toString());
|
439
|
+
|
440
|
+
expected = new Date(2009, 0, 24, 15, 33, 44);
|
441
|
+
actual = I18n.parseDate("2009-01-24T15:33:44");
|
442
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
443
|
+
|
444
|
+
expected = new Date(Date.UTC(2011, 6, 20, 12, 51, 55));
|
445
|
+
actual = I18n.parseDate("2011-07-20T12:51:55+0000");
|
446
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
447
|
+
|
448
|
+
expected = new Date(Date.UTC(2011, 6, 20, 13, 03, 39));
|
449
|
+
actual = I18n.parseDate("Wed Jul 20 13:03:39 +0000 2011");
|
450
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
451
|
+
|
452
|
+
expected = new Date(Date.UTC(2009, 0, 24, 15, 33, 44));
|
453
|
+
actual = I18n.parseDate("2009-01-24T15:33:44Z");
|
454
|
+
expect(actual.toString()).toBeEqualTo(expected.toString());
|
455
|
+
});
|
456
|
+
|
457
|
+
specify("date formatting", function(){
|
458
|
+
I18n.locale = "pt-BR";
|
459
|
+
|
460
|
+
// 2009-04-26 19:35:44 (Sunday)
|
461
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
462
|
+
|
463
|
+
// short week day
|
464
|
+
expect(I18n.strftime(date, "%a")).toBeEqualTo("Dom");
|
465
|
+
|
466
|
+
// full week day
|
467
|
+
expect(I18n.strftime(date, "%A")).toBeEqualTo("Domingo");
|
468
|
+
|
469
|
+
// short month
|
470
|
+
expect(I18n.strftime(date, "%b")).toBeEqualTo("Abr");
|
471
|
+
|
472
|
+
// full month
|
473
|
+
expect(I18n.strftime(date, "%B")).toBeEqualTo("Abril");
|
474
|
+
|
475
|
+
// day
|
476
|
+
expect(I18n.strftime(date, "%d")).toBeEqualTo("26");
|
477
|
+
|
478
|
+
// 24-hour
|
479
|
+
expect(I18n.strftime(date, "%H")).toBeEqualTo("19");
|
480
|
+
|
481
|
+
// 12-hour
|
482
|
+
expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
|
483
|
+
|
484
|
+
// month
|
485
|
+
expect(I18n.strftime(date, "%m")).toBeEqualTo("04");
|
486
|
+
|
487
|
+
// minutes
|
488
|
+
expect(I18n.strftime(date, "%M")).toBeEqualTo("35");
|
489
|
+
|
490
|
+
// meridian
|
491
|
+
expect(I18n.strftime(date, "%p")).toBeEqualTo("PM");
|
492
|
+
|
493
|
+
// seconds
|
494
|
+
expect(I18n.strftime(date, "%S")).toBeEqualTo("44");
|
495
|
+
|
496
|
+
// week day
|
497
|
+
expect(I18n.strftime(date, "%w")).toBeEqualTo("0");
|
498
|
+
|
499
|
+
// short year
|
500
|
+
expect(I18n.strftime(date, "%y")).toBeEqualTo("09");
|
501
|
+
|
502
|
+
// full year
|
503
|
+
expect(I18n.strftime(date, "%Y")).toBeEqualTo("2009");
|
504
|
+
});
|
505
|
+
|
506
|
+
specify("date formatting without padding", function(){
|
507
|
+
I18n.locale = "pt-BR";
|
508
|
+
|
509
|
+
// 2009-04-26 19:35:44 (Sunday)
|
510
|
+
var date = new Date(2009, 3, 9, 7, 8, 9);
|
511
|
+
|
512
|
+
// 24-hour without padding
|
513
|
+
expect(I18n.strftime(date, "%-H")).toBeEqualTo("7");
|
514
|
+
|
515
|
+
// 12-hour without padding
|
516
|
+
expect(I18n.strftime(date, "%-I")).toBeEqualTo("7");
|
517
|
+
|
518
|
+
// minutes without padding
|
519
|
+
expect(I18n.strftime(date, "%-M")).toBeEqualTo("8");
|
520
|
+
|
521
|
+
// seconds without padding
|
522
|
+
expect(I18n.strftime(date, "%-S")).toBeEqualTo("9");
|
523
|
+
|
524
|
+
// short year without padding
|
525
|
+
expect(I18n.strftime(date, "%-y")).toBeEqualTo("9");
|
526
|
+
|
527
|
+
// month without padding
|
528
|
+
expect(I18n.strftime(date, "%-m")).toBeEqualTo("4");
|
529
|
+
|
530
|
+
// day without padding
|
531
|
+
expect(I18n.strftime(date, "%-d")).toBeEqualTo("9");
|
532
|
+
expect(I18n.strftime(date, "%e")).toBeEqualTo("9");
|
533
|
+
});
|
534
|
+
|
535
|
+
specify("date formatting with padding", function(){
|
536
|
+
I18n.locale = "pt-BR";
|
537
|
+
|
538
|
+
// 2009-04-26 19:35:44 (Sunday)
|
539
|
+
var date = new Date(2009, 3, 9, 7, 8, 9);
|
540
|
+
|
541
|
+
// 24-hour
|
542
|
+
expect(I18n.strftime(date, "%H")).toBeEqualTo("07");
|
543
|
+
|
544
|
+
// 12-hour
|
545
|
+
expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
|
546
|
+
|
547
|
+
// minutes
|
548
|
+
expect(I18n.strftime(date, "%M")).toBeEqualTo("08");
|
549
|
+
|
550
|
+
// seconds
|
551
|
+
expect(I18n.strftime(date, "%S")).toBeEqualTo("09");
|
552
|
+
|
553
|
+
// short year
|
554
|
+
expect(I18n.strftime(date, "%y")).toBeEqualTo("09");
|
555
|
+
|
556
|
+
// month
|
557
|
+
expect(I18n.strftime(date, "%m")).toBeEqualTo("04");
|
558
|
+
|
559
|
+
// day
|
560
|
+
expect(I18n.strftime(date, "%d")).toBeEqualTo("09");
|
561
|
+
});
|
562
|
+
|
563
|
+
specify("date formatting with negative time zone", function(){
|
564
|
+
I18n.locale = "pt-BR";
|
565
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
566
|
+
stub(date, "getTimezoneOffset()", 345);
|
567
|
+
|
568
|
+
expect(I18n.strftime(date, "%z")).toMatch(/^(\+|-)[\d]{4}$/);
|
569
|
+
expect(I18n.strftime(date, "%z")).toBeEqualTo("-0545");
|
570
|
+
});
|
571
|
+
|
572
|
+
specify("date formatting with positive time zone", function(){
|
573
|
+
I18n.locale = "pt-BR";
|
574
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
575
|
+
stub(date, "getTimezoneOffset()", -345);
|
576
|
+
|
577
|
+
expect(I18n.strftime(date, "%z")).toMatch(/^(\+|-)[\d]{4}$/);
|
578
|
+
expect(I18n.strftime(date, "%z")).toBeEqualTo("+0545");
|
579
|
+
});
|
580
|
+
|
581
|
+
specify("date formatting with custom meridian", function(){
|
582
|
+
I18n.locale = "en-US";
|
583
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
584
|
+
expect(I18n.strftime(date, "%p")).toBeEqualTo("pm");
|
585
|
+
});
|
586
|
+
|
587
|
+
specify("date formatting meridian boundaries", function(){
|
588
|
+
I18n.locale = "en-US";
|
589
|
+
var date = new Date(2009, 3, 26, 0, 35, 44);
|
590
|
+
expect(I18n.strftime(date, "%p")).toBeEqualTo("am");
|
591
|
+
|
592
|
+
date = new Date(2009, 3, 26, 12, 35, 44);
|
593
|
+
expect(I18n.strftime(date, "%p")).toBeEqualTo("pm");
|
594
|
+
});
|
595
|
+
|
596
|
+
specify("date formatting hour12 values", function(){
|
597
|
+
I18n.locale = "pt-BR";
|
598
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
599
|
+
expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
|
600
|
+
|
601
|
+
date = new Date(2009, 3, 26, 12, 35, 44);
|
602
|
+
expect(I18n.strftime(date, "%I")).toBeEqualTo("12");
|
603
|
+
|
604
|
+
date = new Date(2009, 3, 26, 0, 35, 44);
|
605
|
+
expect(I18n.strftime(date, "%I")).toBeEqualTo("12");
|
606
|
+
});
|
607
|
+
|
608
|
+
specify("localize date strings", function(){
|
609
|
+
I18n.locale = "pt-BR";
|
610
|
+
|
611
|
+
expect(I18n.l("date.formats.default", "2009-11-29")).toBeEqualTo("29/11/2009");
|
612
|
+
expect(I18n.l("date.formats.short", "2009-01-07")).toBeEqualTo("07 de Janeiro");
|
613
|
+
expect(I18n.l("date.formats.long", "2009-01-07")).toBeEqualTo("07 de Janeiro de 2009");
|
614
|
+
});
|
615
|
+
|
616
|
+
specify("localize time strings", function(){
|
617
|
+
I18n.locale = "pt-BR";
|
618
|
+
|
619
|
+
expect(I18n.l("time.formats.default", "2009-11-29 15:07:59")).toBeEqualTo("Domingo, 29 de Novembro de 2009, 15:07 h");
|
620
|
+
expect(I18n.l("time.formats.short", "2009-01-07 09:12:35")).toBeEqualTo("07/01, 09:12 h");
|
621
|
+
expect(I18n.l("time.formats.long", "2009-11-29 15:07:59")).toBeEqualTo("Domingo, 29 de Novembro de 2009, 15:07 h");
|
622
|
+
});
|
623
|
+
|
624
|
+
specify("localize percentage", function(){
|
625
|
+
I18n.locale = "pt-BR";
|
626
|
+
expect(I18n.l("percentage", 123.45)).toBeEqualTo("123,45%");
|
627
|
+
});
|
628
|
+
|
629
|
+
specify("default value for simple translation", function(){
|
630
|
+
actual = I18n.t("warning", {defaultValue: "Warning!"});
|
631
|
+
expect(actual).toBeEqualTo("Warning!");
|
632
|
+
});
|
633
|
+
|
634
|
+
specify("default value for unknown locale", function(){
|
635
|
+
I18n.locale = "fr";
|
636
|
+
actual = I18n.t("warning", {defaultValue: "Warning!"});
|
637
|
+
expect(actual).toBeEqualTo("Warning!");
|
638
|
+
});
|
639
|
+
|
640
|
+
specify("default value with interpolation", function(){
|
641
|
+
actual = I18n.t(
|
642
|
+
"alert",
|
643
|
+
{defaultValue: "Attention! {{message}}", message: "You're out of quota!"}
|
644
|
+
);
|
645
|
+
|
646
|
+
expect(actual).toBeEqualTo("Attention! You're out of quota!");
|
647
|
+
});
|
648
|
+
|
649
|
+
specify("default value should not be used when scope exist", function(){
|
650
|
+
actual = I18n.t("hello", {defaultValue: "What's up?"});
|
651
|
+
expect(actual).toBeEqualTo("Hello World!");
|
652
|
+
});
|
653
|
+
|
654
|
+
specify("default value for pluralize", function(){
|
655
|
+
options = {defaultValue: {
|
656
|
+
none: "No things here!",
|
657
|
+
one: "There is {{count}} thing here!",
|
658
|
+
other: "There are {{count}} things here!"
|
659
|
+
}};
|
660
|
+
|
661
|
+
expect(I18n.p(0, "things", options)).toBeEqualTo("No things here!");
|
662
|
+
expect(I18n.p(1, "things", options)).toBeEqualTo("There is 1 thing here!");
|
663
|
+
expect(I18n.p(5, "things", options)).toBeEqualTo("There are 5 things here!");
|
664
|
+
});
|
665
|
+
|
666
|
+
specify("default value for pluralize should not be used when scope exist", function(){
|
667
|
+
options = {defaultValue: {
|
668
|
+
none: "No things here!",
|
669
|
+
one: "There is {{count}} thing here!",
|
670
|
+
other: "There are {{count}} things here!"
|
671
|
+
}};
|
672
|
+
|
673
|
+
expect(I18n.pluralize(0, "inbox", options)).toBeEqualTo("You have no messages");
|
674
|
+
expect(I18n.pluralize(1, "inbox", options)).toBeEqualTo("You have 1 message");
|
675
|
+
expect(I18n.pluralize(5, "inbox", options)).toBeEqualTo("You have 5 messages");
|
676
|
+
});
|
677
|
+
|
678
|
+
specify("prepare options", function(){
|
679
|
+
options = I18n.prepareOptions(
|
680
|
+
{name: "Mary Doe"},
|
681
|
+
{name: "John Doe", role: "user"}
|
682
|
+
);
|
683
|
+
|
684
|
+
expect(options["name"]).toBeEqualTo("Mary Doe");
|
685
|
+
expect(options["role"]).toBeEqualTo("user");
|
686
|
+
});
|
687
|
+
|
688
|
+
specify("prepare options with multiple options", function(){
|
689
|
+
options = I18n.prepareOptions(
|
690
|
+
{name: "Mary Doe"},
|
691
|
+
{name: "John Doe", role: "user"},
|
692
|
+
{age: 33},
|
693
|
+
{email: "mary@doe.com", url: "http://marydoe.com"},
|
694
|
+
{role: "admin", email: "john@doe.com"}
|
695
|
+
);
|
696
|
+
|
697
|
+
expect(options["name"]).toBeEqualTo("Mary Doe");
|
698
|
+
expect(options["role"]).toBeEqualTo("user");
|
699
|
+
expect(options["age"]).toBeEqualTo(33);
|
700
|
+
expect(options["email"]).toBeEqualTo("mary@doe.com");
|
701
|
+
expect(options["url"]).toBeEqualTo("http://marydoe.com");
|
702
|
+
});
|
703
|
+
|
704
|
+
specify("prepare options should return an empty hash when values are null", function(){
|
705
|
+
expect({}).toBeEqualTo(I18n.prepareOptions(null, null));
|
706
|
+
});
|
707
|
+
|
708
|
+
specify("percentage with defaults", function(){
|
709
|
+
expect(I18n.toPercentage(1234)).toBeEqualTo("1234.000%");
|
710
|
+
});
|
711
|
+
|
712
|
+
specify("percentage with custom options", function(){
|
713
|
+
actual = I18n.toPercentage(1234, {delimiter: "_", precision: 0});
|
714
|
+
expect(actual).toBeEqualTo("1_234%");
|
715
|
+
});
|
716
|
+
|
717
|
+
specify("percentage with translation", function(){
|
718
|
+
I18n.translations.en.number = {
|
719
|
+
percentage: {
|
720
|
+
format: {
|
721
|
+
precision: 2,
|
722
|
+
delimiter: ".",
|
723
|
+
separator: ","
|
724
|
+
}
|
725
|
+
}
|
726
|
+
};
|
727
|
+
|
728
|
+
expect(I18n.toPercentage(1234)).toBeEqualTo("1.234,00%");
|
729
|
+
});
|
730
|
+
|
731
|
+
specify("percentage with translation and custom options", function(){
|
732
|
+
I18n.translations.en.number = {
|
733
|
+
percentage: {
|
734
|
+
format: {
|
735
|
+
precision: 2,
|
736
|
+
delimiter: ".",
|
737
|
+
separator: ","
|
738
|
+
}
|
739
|
+
}
|
740
|
+
};
|
741
|
+
|
742
|
+
actual = I18n.toPercentage(1234, {precision: 4, delimiter: "-", separator: "+"});
|
743
|
+
expect(actual).toBeEqualTo("1-234+0000%");
|
744
|
+
});
|
745
|
+
|
746
|
+
specify("scope option as string", function(){
|
747
|
+
actual = I18n.t("stranger", {scope: "greetings"});
|
748
|
+
expect(actual).toBeEqualTo("Hello stranger!");
|
749
|
+
});
|
750
|
+
|
751
|
+
specify("scope as array", function(){
|
752
|
+
actual = I18n.t(["greetings", "stranger"]);
|
753
|
+
expect(actual).toBeEqualTo("Hello stranger!");
|
754
|
+
});
|
755
|
+
|
756
|
+
specify("new placeholder syntax", function(){
|
757
|
+
I18n.translations["en"]["new_syntax"] = "Hi %{name}!";
|
758
|
+
actual = I18n.t("new_syntax", {name: "John"});
|
759
|
+
expect(actual).toBeEqualTo("Hi John!");
|
760
|
+
});
|
761
|
+
|
762
|
+
specify("return translation for custom scope separator", function(){
|
763
|
+
I18n.defaultSeparator = "•";
|
764
|
+
actual = I18n.t("greetings•stranger");
|
765
|
+
expect(actual).toBeEqualTo("Hello stranger!");
|
766
|
+
});
|
767
|
+
|
768
|
+
specify("return number as human size", function(){
|
769
|
+
kb = 1024;
|
770
|
+
|
771
|
+
expect(I18n.toHumanSize(1)).toBeEqualTo("1Byte");
|
772
|
+
expect(I18n.toHumanSize(100)).toBeEqualTo("100Bytes");
|
773
|
+
|
774
|
+
expect(I18n.toHumanSize(kb)).toBeEqualTo("1KB");
|
775
|
+
expect(I18n.toHumanSize(kb * 1.5)).toBeEqualTo("1.5KB");
|
776
|
+
|
777
|
+
expect(I18n.toHumanSize(kb * kb)).toBeEqualTo("1MB");
|
778
|
+
expect(I18n.toHumanSize(kb * kb * 1.5)).toBeEqualTo("1.5MB");
|
779
|
+
|
780
|
+
expect(I18n.toHumanSize(kb * kb * kb)).toBeEqualTo("1GB");
|
781
|
+
expect(I18n.toHumanSize(kb * kb * kb * 1.5)).toBeEqualTo("1.5GB");
|
782
|
+
|
783
|
+
expect(I18n.toHumanSize(kb * kb * kb * kb)).toBeEqualTo("1TB");
|
784
|
+
expect(I18n.toHumanSize(kb * kb * kb * kb * 1.5)).toBeEqualTo("1.5TB");
|
785
|
+
|
786
|
+
expect(I18n.toHumanSize(kb * kb * kb * kb * kb)).toBeEqualTo("1024TB");
|
787
|
+
});
|
788
|
+
|
789
|
+
specify("return number as human size using custom options", function(){
|
790
|
+
expect(I18n.toHumanSize(1024 * 1.6, {precision: 0})).toBeEqualTo("2KB");
|
791
|
+
});
|
792
|
+
|
793
|
+
specify("return number without insignificant zeros", function(){
|
794
|
+
options = {precision: 4, strip_insignificant_zeros: true};
|
795
|
+
|
796
|
+
expect(I18n.toNumber(65, options)).toBeEqualTo("65");
|
797
|
+
expect(I18n.toNumber(1.2, options)).toBeEqualTo("1.2");
|
798
|
+
expect(I18n.toCurrency(1.2, options)).toBeEqualTo("$1.2");
|
799
|
+
expect(I18n.toHumanSize(1.2, options)).toBeEqualTo("1.2Bytes");
|
800
|
+
});
|
801
|
+
|
802
|
+
specify("return plural key for given count for english locale", function(){
|
803
|
+
expect(I18n.pluralizationRules.en(0)).toBeEqualTo(["zero", "none", "other"]);
|
804
|
+
expect(I18n.pluralizationRules.en(1)).toBeEqualTo("one");
|
805
|
+
expect(I18n.pluralizationRules.en(2)).toBeEqualTo("other");
|
806
|
+
});
|
807
|
+
|
808
|
+
specify("return given pluralizer", function(){
|
809
|
+
I18n.pluralizationRules.en = "en";
|
810
|
+
expect(I18n.pluralizer("ru")).toBeEqualTo("en");
|
811
|
+
I18n.pluralizationRules.ru = "ru";
|
812
|
+
expect(I18n.pluralizer("ru")).toBeEqualTo("ru");
|
813
|
+
});
|
814
|
+
|
815
|
+
specify("find and translate valid node", function(){
|
816
|
+
expect(I18n.findAndTranslateValidNode(["one", "other"], {one: "one", other: "other"})).toBeEqualTo("one");
|
817
|
+
expect(I18n.findAndTranslateValidNode(["one", "other"], {other: "other"})).toBeEqualTo("other");
|
818
|
+
expect(I18n.findAndTranslateValidNode(["one"], {})).toBeEqualTo(null);
|
819
|
+
});
|
820
|
+
});
|