i18n-js 2.1.2 → 3.0.11
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.
- checksums.yaml +7 -0
- data/.editorconfig +24 -0
- data/.gitignore +5 -4
- data/.npmignore +27 -0
- data/.travis.yml +37 -0
- data/Appraisals +20 -0
- data/CHANGELOG.md +354 -0
- data/Gemfile +1 -1
- data/README.md +872 -0
- data/Rakefile +19 -7
- data/app/assets/javascripts/i18n/filtered.js.erb +23 -0
- data/app/assets/javascripts/i18n/shims.js +208 -0
- data/app/assets/javascripts/i18n/translations.js +3 -0
- data/app/assets/javascripts/i18n.js +1077 -0
- data/gemfiles/i18n_0_6.gemfile +7 -0
- data/gemfiles/i18n_0_7.gemfile +7 -0
- data/gemfiles/i18n_0_8.gemfile +7 -0
- data/gemfiles/i18n_0_9.gemfile +7 -0
- data/gemfiles/i18n_1_0.gemfile +7 -0
- data/i18n-js.gemspec +11 -9
- data/lib/i18n/js/dependencies.rb +59 -0
- data/lib/i18n/js/engine.rb +87 -0
- data/lib/i18n/js/fallback_locales.rb +70 -0
- data/lib/{i18n-js → i18n/js}/middleware.rb +32 -9
- data/lib/i18n/js/private/hash_with_symbol_keys.rb +36 -0
- data/lib/i18n/js/segment.rb +88 -0
- data/lib/i18n/js/utils.rb +52 -0
- data/lib/i18n/js/version.rb +7 -0
- data/lib/i18n/js.rb +242 -0
- data/lib/i18n-js.rb +1 -177
- data/lib/rails/generators/i18n/js/config/config_generator.rb +19 -0
- data/{config → lib/rails/generators/i18n/js/config/templates}/i18n-js.yml +11 -6
- data/lib/tasks/export.rake +8 -0
- data/package.json +25 -0
- data/spec/fixtures/custom_path.yml +5 -0
- data/spec/fixtures/default.yml +5 -0
- data/spec/fixtures/erb.yml +5 -0
- data/spec/fixtures/except_condition.yml +7 -0
- data/spec/fixtures/js_export_dir_custom.yml +7 -0
- data/spec/fixtures/js_export_dir_none.yml +6 -0
- data/spec/fixtures/js_extend_parent.yml +6 -0
- data/spec/fixtures/js_extend_segment.yml +6 -0
- data/spec/fixtures/js_file_per_locale.yml +7 -0
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +4 -0
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +6 -0
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +4 -0
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +4 -0
- data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +4 -0
- data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +4 -0
- data/spec/fixtures/js_file_with_namespace_and_pretty_print.yml +7 -0
- data/spec/fixtures/js_sort_translation_keys_false.yml +6 -0
- data/spec/fixtures/js_sort_translation_keys_true.yml +6 -0
- data/spec/{resources → fixtures}/locales.yml +13 -1
- data/spec/fixtures/multiple_conditions.yml +7 -0
- data/spec/fixtures/multiple_conditions_per_locale.yml +7 -0
- data/spec/fixtures/multiple_files.yml +7 -0
- data/spec/{resources → fixtures}/no_config.yml +0 -0
- data/spec/fixtures/no_scope.yml +4 -0
- data/spec/fixtures/simple_scope.yml +5 -0
- data/spec/js/currency.spec.js +62 -0
- data/spec/js/current_locale.spec.js +19 -0
- data/spec/js/dates.spec.js +265 -0
- data/spec/js/defaults.spec.js +31 -0
- data/spec/js/extend.spec.js +110 -0
- data/spec/js/interpolation.spec.js +124 -0
- data/spec/js/jasmine/MIT.LICENSE +20 -0
- data/spec/js/jasmine/jasmine-html.js +190 -0
- data/spec/js/jasmine/jasmine.css +166 -0
- data/spec/js/jasmine/jasmine.js +2476 -0
- data/spec/js/jasmine/jasmine_favicon.png +0 -0
- data/spec/js/locales.spec.js +31 -0
- data/spec/js/localization.spec.js +48 -0
- data/spec/js/numbers.spec.js +170 -0
- data/spec/js/placeholder.spec.js +24 -0
- data/spec/js/pluralization.spec.js +211 -0
- data/spec/js/prepare_options.spec.js +41 -0
- data/spec/js/require.js +2083 -0
- data/spec/js/specs.html +49 -0
- data/spec/js/specs_requirejs.html +72 -0
- data/spec/js/translate.spec.js +277 -0
- data/spec/js/translations.js +164 -0
- data/spec/js/utility_functions.spec.js +20 -0
- data/spec/ruby/i18n/js/fallback_locales_spec.rb +84 -0
- data/spec/ruby/i18n/js/segment_spec.rb +157 -0
- data/spec/ruby/i18n/js/utils_spec.rb +106 -0
- data/spec/ruby/i18n/js_spec.rb +627 -0
- data/spec/spec_helper.rb +55 -14
- data/yarn.lock +131 -0
- metadata +188 -96
- data/.rspec +0 -1
- data/Gemfile.lock +0 -51
- data/README.rdoc +0 -305
- data/lib/i18n-js/engine.rb +0 -62
- data/lib/i18n-js/railtie.rb +0 -13
- data/lib/i18n-js/rake.rb +0 -16
- data/lib/i18n-js/version.rb +0 -10
- data/spec/i18n_spec.js +0 -768
- data/spec/i18n_spec.rb +0 -205
- data/spec/resources/custom_path.yml +0 -4
- data/spec/resources/default.yml +0 -4
- data/spec/resources/js_file_per_locale.yml +0 -3
- data/spec/resources/multiple_conditions.yml +0 -6
- data/spec/resources/multiple_files.yml +0 -6
- data/spec/resources/no_scope.yml +0 -3
- data/spec/resources/simple_scope.yml +0 -4
- data/vendor/assets/javascripts/i18n/translations.js.erb +0 -7
- data/vendor/assets/javascripts/i18n.js +0 -450
data/i18n-js.gemspec
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "i18n
|
3
|
+
require "i18n/js/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "i18n-js"
|
7
|
-
s.version =
|
7
|
+
s.version = I18n::JS::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Nando Vieira"]
|
10
10
|
s.email = ["fnando.vieira@gmail.com"]
|
11
11
|
s.homepage = "http://rubygems.org/gems/i18n-js"
|
12
12
|
s.summary = "It's a small library to provide the Rails I18n translations on the Javascript."
|
13
13
|
s.description = s.summary
|
14
|
+
s.license = "MIT"
|
14
15
|
|
15
16
|
s.files = `git ls-files`.split("\n")
|
16
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
19
|
s.require_paths = ["lib"]
|
19
20
|
|
20
|
-
s.add_dependency "i18n"
|
21
|
-
|
22
|
-
s.add_development_dependency "
|
23
|
-
s.add_development_dependency "rspec", "~>
|
24
|
-
s.add_development_dependency "
|
25
|
-
s.add_development_dependency "
|
26
|
-
|
21
|
+
s.add_dependency "i18n", ">= 0.6.6", "< 2"
|
22
|
+
|
23
|
+
s.add_development_dependency "appraisal", "~> 2.0"
|
24
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
s.add_development_dependency "rake", "~> 12.0"
|
26
|
+
s.add_development_dependency "gem-release", ">= 0.7"
|
27
|
+
|
28
|
+
s.required_ruby_version = ">= 2.1.0"
|
27
29
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module I18n
|
2
|
+
module JS
|
3
|
+
# When using `safe_gem_check` to check for a pre-release version of gem,
|
4
|
+
# we need to specify pre-release version suffix in version constraint
|
5
|
+
module Dependencies
|
6
|
+
class << self
|
7
|
+
def rails?
|
8
|
+
defined?(Rails) && Rails.respond_to?(:version)
|
9
|
+
end
|
10
|
+
|
11
|
+
def sprockets_rails_v2_plus?
|
12
|
+
safe_gem_check("sprockets-rails", ">= 2")
|
13
|
+
end
|
14
|
+
|
15
|
+
# This cannot be called at class definition time
|
16
|
+
# Since not all libraries are loaded
|
17
|
+
#
|
18
|
+
# Call this in an initializer
|
19
|
+
def using_asset_pipeline?
|
20
|
+
assets_pipeline_available =
|
21
|
+
(rails3? || rails4? || rails5?) &&
|
22
|
+
Rails.respond_to?(:application) &&
|
23
|
+
Rails.application.config.respond_to?(:assets)
|
24
|
+
rails3_assets_enabled =
|
25
|
+
rails3? &&
|
26
|
+
assets_pipeline_available &&
|
27
|
+
Rails.application.config.assets.enabled != false
|
28
|
+
|
29
|
+
assets_pipeline_available && (rails4? || rails5? || rails3_assets_enabled)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def rails3?
|
35
|
+
rails? && Rails.version.to_i == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
def rails4?
|
39
|
+
rails? && Rails.version.to_i == 4
|
40
|
+
end
|
41
|
+
|
42
|
+
def rails5?
|
43
|
+
rails? && Rails.version.to_i == 5
|
44
|
+
end
|
45
|
+
|
46
|
+
def safe_gem_check(*args)
|
47
|
+
if Gem::Specification.respond_to?(:find_by_name)
|
48
|
+
Gem::Specification.find_by_name(*args)
|
49
|
+
elsif Gem.respond_to?(:available?)
|
50
|
+
Gem.available?(*args)
|
51
|
+
end
|
52
|
+
rescue Gem::LoadError
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "i18n/js"
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module JS
|
5
|
+
# @api private
|
6
|
+
# The class cannot be private
|
7
|
+
class SprocketsExtension
|
8
|
+
# Actual definition is placed below
|
9
|
+
end
|
10
|
+
|
11
|
+
class Engine < ::Rails::Engine
|
12
|
+
# See https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md#supporting-all-versions-of-sprockets-in-processors
|
13
|
+
# for reference of supporting multiple versions
|
14
|
+
|
15
|
+
# `sprockets.environment` was used for 1.x of `sprockets-rails`
|
16
|
+
# https://github.com/rails/sprockets-rails/issues/227
|
17
|
+
#
|
18
|
+
# References for current values:
|
19
|
+
#
|
20
|
+
# Here is where sprockets are attached with Rails. There is no 'sprockets.environment' mentioned.
|
21
|
+
# https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/railtie.rb
|
22
|
+
#
|
23
|
+
# Finisher hook is the place which should be used as border.
|
24
|
+
# http://guides.rubyonrails.org/configuring.html#initializers
|
25
|
+
#
|
26
|
+
# For detail see Pull Request:
|
27
|
+
# https://github.com/fnando/i18n-js/pull/371
|
28
|
+
initializer "i18n-js.register_preprocessor", after: :engines_blank_point, before: :finisher_hook do
|
29
|
+
# This must be called inside initializer block
|
30
|
+
# For details see comments for `using_asset_pipeline?`
|
31
|
+
next unless JS::Dependencies.using_asset_pipeline?
|
32
|
+
|
33
|
+
# From README of 2.x & 3.x of `sprockets-rails`
|
34
|
+
# It seems the `configure` block is preferred way to call `register_preprocessor`
|
35
|
+
# Not sure if this will break older versions of rails
|
36
|
+
#
|
37
|
+
# https://github.com/rails/sprockets-rails/blob/v2.3.3/README.md
|
38
|
+
# https://github.com/rails/sprockets-rails/blob/v3.0.0/README.md
|
39
|
+
if JS::Dependencies.sprockets_rails_v2_plus?
|
40
|
+
Rails.application.config.assets.configure do |config|
|
41
|
+
config.register_preprocessor("application/javascript", ::I18n::JS::SprocketsExtension)
|
42
|
+
end
|
43
|
+
elsif Rails.application.assets.respond_to?(:register_preprocessor)
|
44
|
+
Rails.application.assets.register_preprocessor("application/javascript", ::I18n::JS::SprocketsExtension)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
class SprocketsExtension
|
51
|
+
def initialize(filename, &block)
|
52
|
+
@filename = filename
|
53
|
+
@source = block.call
|
54
|
+
end
|
55
|
+
|
56
|
+
def render(context, empty_hash_wtf)
|
57
|
+
self.class.run(@filename, @source, context)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.run(filename, source, context)
|
61
|
+
if context.logical_path == "i18n/filtered"
|
62
|
+
::I18n.load_path.each { |path| context.depend_on(File.expand_path(path)) }
|
63
|
+
|
64
|
+
# Absolute path is required or
|
65
|
+
# Sprockets assumes it's a logical path
|
66
|
+
#
|
67
|
+
# Calling `depend on` with an absent file
|
68
|
+
# will invoke `resolve` and will throw an error in the end
|
69
|
+
if I18n::JS.config_file_exists?
|
70
|
+
context.depend_on(File.expand_path(I18n::JS.config_file_path))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
source
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.call(input)
|
78
|
+
filename = input[:filename]
|
79
|
+
source = input[:data]
|
80
|
+
context = input[:environment].context_class.new(input)
|
81
|
+
|
82
|
+
result = run(filename, source, context)
|
83
|
+
context.metadata.merge(data: result)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module I18n
|
2
|
+
module JS
|
3
|
+
class FallbackLocales
|
4
|
+
attr_reader :fallbacks, :locale
|
5
|
+
|
6
|
+
def initialize(fallbacks, locale)
|
7
|
+
@fallbacks = fallbacks
|
8
|
+
@locale = locale
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
locales.each { |locale| yield(locale) }
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<String, Symbol>]
|
16
|
+
# An Array of locales to use as fallbacks for given locale.
|
17
|
+
def locales
|
18
|
+
locales = case fallbacks
|
19
|
+
when true
|
20
|
+
default_fallbacks
|
21
|
+
when :default_locale
|
22
|
+
[::I18n.default_locale]
|
23
|
+
when Symbol, String
|
24
|
+
[fallbacks.to_sym]
|
25
|
+
when Array
|
26
|
+
ensure_valid_fallbacks_as_array!
|
27
|
+
fallbacks
|
28
|
+
when Hash
|
29
|
+
Array(fallbacks[locale] || default_fallbacks)
|
30
|
+
else
|
31
|
+
fail ArgumentError, "fallbacks must be: true, :default_locale an Array or a Hash - given: #{fallbacks}"
|
32
|
+
end
|
33
|
+
|
34
|
+
locales.map! { |locale| locale.to_sym }
|
35
|
+
locales
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# @return [Array<String, Symbol>] An Array of locales.
|
41
|
+
def default_fallbacks
|
42
|
+
if using_i18n_fallbacks_module?
|
43
|
+
I18n.fallbacks[locale]
|
44
|
+
else
|
45
|
+
[::I18n.default_locale]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return
|
50
|
+
# true if we can safely use I18n.fallbacks, false otherwise.
|
51
|
+
#
|
52
|
+
# @note
|
53
|
+
# We should implement this as `I18n.respond_to?(:fallbacks)`, but
|
54
|
+
# once I18n::Backend::Fallbacks is included, I18n will _always_
|
55
|
+
# respond to :fallbacks. Even if we switch the backend to one
|
56
|
+
# without fallbacks!
|
57
|
+
#
|
58
|
+
# Maybe this should be fixed within I18n.
|
59
|
+
def using_i18n_fallbacks_module?
|
60
|
+
I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
|
61
|
+
end
|
62
|
+
|
63
|
+
def ensure_valid_fallbacks_as_array!
|
64
|
+
return if fallbacks.all? { |e| e.is_a?(String) || e.is_a?(Symbol) }
|
65
|
+
|
66
|
+
fail ArgumentError, "If fallbacks is passed as Array, it must ony include Strings or Symbols. Given: #{fallbacks}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module JS
|
3
5
|
class Middleware
|
4
6
|
def initialize(app)
|
5
7
|
@app = app
|
8
|
+
clear_cache
|
6
9
|
end
|
7
10
|
|
8
11
|
def call(env)
|
@@ -13,7 +16,11 @@ module SimplesIdeias
|
|
13
16
|
|
14
17
|
private
|
15
18
|
def cache_path
|
16
|
-
@cache_path ||=
|
19
|
+
@cache_path ||= cache_dir.join("i18n-js.yml")
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_dir
|
23
|
+
@cache_dir ||= Rails.root.join("tmp/cache")
|
17
24
|
end
|
18
25
|
|
19
26
|
def cache
|
@@ -26,6 +33,24 @@ module SimplesIdeias
|
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
36
|
+
def clear_cache
|
37
|
+
# `File.delete` will raise error when "multiple worker"
|
38
|
+
# Are running at the same time, like in a parallel test
|
39
|
+
#
|
40
|
+
# `FileUtils.rm_f` is tested manually
|
41
|
+
#
|
42
|
+
# See https://github.com/fnando/i18n-js/issues/436
|
43
|
+
FileUtils.rm_f(cache_path) if File.exist?(cache_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def save_cache(new_cache)
|
47
|
+
# path could be a symbolic link
|
48
|
+
FileUtils.mkdir_p(cache_dir) unless File.exists?(cache_dir)
|
49
|
+
File.open(cache_path, "w+") do |file|
|
50
|
+
file << new_cache.to_yaml
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
29
54
|
# Check if translations should be regenerated.
|
30
55
|
# ONLY REGENERATE when these conditions are met:
|
31
56
|
#
|
@@ -46,13 +71,11 @@ module SimplesIdeias
|
|
46
71
|
new_cache[path] = changed_at
|
47
72
|
end
|
48
73
|
|
49
|
-
|
50
|
-
File.open(cache_path, "w+") do |file|
|
51
|
-
file << new_cache.to_yaml
|
52
|
-
end
|
74
|
+
return if valid_cache.all?
|
53
75
|
|
54
|
-
|
55
|
-
|
76
|
+
save_cache(new_cache)
|
77
|
+
|
78
|
+
::I18n::JS.export
|
56
79
|
end
|
57
80
|
end
|
58
81
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module I18n
|
2
|
+
module JS
|
3
|
+
# @api private
|
4
|
+
module Private
|
5
|
+
# Hash with string keys converted to symbol keys
|
6
|
+
# Used for handling values read on YAML
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class HashWithSymbolKeys < ::Hash
|
10
|
+
# An instance can only be created by passing in another hash
|
11
|
+
def initialize(hash)
|
12
|
+
raise TypeError unless hash.is_a?(::Hash)
|
13
|
+
|
14
|
+
hash.each_key do |key|
|
15
|
+
# Objects like `Integer` does not have `to_sym`
|
16
|
+
new_key = key.respond_to?(:to_sym) ? key.to_sym : key
|
17
|
+
self[new_key] = hash[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
self.default = hash.default if hash.default
|
21
|
+
self.default_proc = hash.default_proc if hash.default_proc
|
22
|
+
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
# From AS Core extension
|
27
|
+
def slice(*keys)
|
28
|
+
hash = keys.each_with_object(Hash.new) do |k, hash|
|
29
|
+
hash[k] = self[k] if has_key?(k)
|
30
|
+
end
|
31
|
+
self.class.new(hash)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "i18n/js/private/hash_with_symbol_keys"
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module JS
|
5
|
+
|
6
|
+
# Class which enscapulates a translations hash and outputs a single JSON translation file
|
7
|
+
class Segment
|
8
|
+
OPTIONS = [:namespace, :pretty_print, :js_extend, :sort_translation_keys].freeze
|
9
|
+
LOCALE_INTERPOLATOR = /%\{locale\}/
|
10
|
+
|
11
|
+
attr_reader *([:file, :translations] | OPTIONS)
|
12
|
+
|
13
|
+
def initialize(file, translations, options = {})
|
14
|
+
@file = file
|
15
|
+
# `#slice` will be used
|
16
|
+
# But when activesupport is absent,
|
17
|
+
# the core extension from `i18n` gem will be used instead
|
18
|
+
# And it's causing errors (at least in test)
|
19
|
+
#
|
20
|
+
# So the input is wrapped by our class for better `#slice`
|
21
|
+
@translations = Private::HashWithSymbolKeys.new(translations)
|
22
|
+
@namespace = options[:namespace] || 'I18n'
|
23
|
+
@pretty_print = !!options[:pretty_print]
|
24
|
+
@js_extend = options.key?(:js_extend) ? !!options[:js_extend] : true
|
25
|
+
@sort_translation_keys = options.key?(:sort_translation_keys) ? !!options[:sort_translation_keys] : true
|
26
|
+
end
|
27
|
+
|
28
|
+
# Saves JSON file containing translations
|
29
|
+
def save!
|
30
|
+
if @file =~ LOCALE_INTERPOLATOR
|
31
|
+
I18n.available_locales.each do |locale|
|
32
|
+
write_file(file_for_locale(locale), @translations.slice(locale))
|
33
|
+
end
|
34
|
+
else
|
35
|
+
write_file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def write_file(_file = @file, _translations = @translations)
|
42
|
+
FileUtils.mkdir_p File.dirname(_file)
|
43
|
+
contents = js_header
|
44
|
+
_translations.each do |locale, translations_for_locale|
|
45
|
+
contents << js_translations(locale, translations_for_locale)
|
46
|
+
end
|
47
|
+
|
48
|
+
return if File.exist?(_file) && File.read(_file) == contents
|
49
|
+
|
50
|
+
File.open(_file, "w+") do |f|
|
51
|
+
f << contents
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def js_header
|
56
|
+
%(#{@namespace}.translations || (#{@namespace}.translations = {});\n)
|
57
|
+
end
|
58
|
+
|
59
|
+
def js_translations(locale, translations)
|
60
|
+
translations = Utils.deep_key_sort(translations) if @sort_translation_keys
|
61
|
+
translations = print_json(translations)
|
62
|
+
js_translations_line(locale, translations)
|
63
|
+
end
|
64
|
+
|
65
|
+
def js_translations_line(locale, translations)
|
66
|
+
if @js_extend
|
67
|
+
%(#{@namespace}.translations["#{locale}"] = I18n.extend((#{@namespace}.translations["#{locale}"] || {}), #{translations});\n)
|
68
|
+
else
|
69
|
+
%(#{@namespace}.translations["#{locale}"] = #{translations};\n)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Outputs pretty or ugly JSON depending on :pretty_print option
|
74
|
+
def print_json(translations)
|
75
|
+
if @pretty_print
|
76
|
+
JSON.pretty_generate(translations)
|
77
|
+
else
|
78
|
+
translations.to_json
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# interpolates filename
|
83
|
+
def file_for_locale(locale)
|
84
|
+
@file.gsub(LOCALE_INTERPOLATOR, locale.to_s)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module I18n
|
2
|
+
module JS
|
3
|
+
module Utils
|
4
|
+
# deep_merge by Stefan Rusterholz, see <http://www.ruby-forum.com/topic/142809>.
|
5
|
+
# The last result is modified to treat `nil` as missing key
|
6
|
+
MERGER = proc do |_key, v1, v2|
|
7
|
+
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : (v2.nil? ? v1 : v2)
|
8
|
+
end
|
9
|
+
|
10
|
+
HASH_NIL_VALUE_CLEANER_PROC = proc do |k, v|
|
11
|
+
v.kind_of?(Hash) ? (v.delete_if(&HASH_NIL_VALUE_CLEANER_PROC); false) : v.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.strip_keys_with_nil_values(hash)
|
15
|
+
hash.dup.delete_if(&HASH_NIL_VALUE_CLEANER_PROC)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.deep_merge(target_hash, hash) # :nodoc:
|
19
|
+
target_hash.merge(hash, &MERGER)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.deep_merge!(target_hash, hash) # :nodoc:
|
23
|
+
target_hash.merge!(hash, &MERGER)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.deep_reject(hash, scopes = [], &block)
|
27
|
+
hash.each_with_object({}) do |(k, v), memo|
|
28
|
+
unless block.call(k, v, scopes + [k.to_s])
|
29
|
+
memo[k] = v.kind_of?(Hash) ? deep_reject(v, scopes + [k.to_s], &block) : v
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.scopes_match?(scopes1, scopes2)
|
35
|
+
if scopes1.length == scopes2.length
|
36
|
+
[scopes1, scopes2].transpose.all? do |scope1, scope2|
|
37
|
+
scope1.to_s == '*' || scope2.to_s == '*' || scope1.to_s == scope2.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.deep_key_sort(hash)
|
43
|
+
# Avoid things like `true` or `1` from YAML which causes error
|
44
|
+
hash.keys.sort {|a, b| a.to_s <=> b.to_s}.
|
45
|
+
each_with_object({}) do |key, seed|
|
46
|
+
value = hash[key]
|
47
|
+
seed[key] = value.is_a?(Hash) ? deep_key_sort(value) : value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|