i18n-js 3.7.1 → 4.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +4 -0
  3. data/.github/FUNDING.yml +3 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  6. data/.github/PULL_REQUEST_TEMPLATE.md +38 -0
  7. data/.github/dependabot.yml +15 -0
  8. data/.github/workflows/ruby-tests.yml +61 -0
  9. data/.gitignore +11 -7
  10. data/.rubocop.yml +12 -0
  11. data/CHANGELOG.md +12 -495
  12. data/CODE_OF_CONDUCT.md +74 -0
  13. data/CONTRIBUTING.md +79 -0
  14. data/Gemfile +3 -0
  15. data/LICENSE.md +20 -0
  16. data/README.md +89 -903
  17. data/Rakefile +10 -20
  18. data/exe/i18n +5 -0
  19. data/i18n-js.gemspec +47 -29
  20. data/lib/guard/i18n-js.rb +78 -0
  21. data/lib/guard/i18n-js/templates/Guardfile +10 -0
  22. data/lib/guard/i18n-js/version.rb +13 -0
  23. data/lib/i18n-js.rb +66 -1
  24. data/lib/i18n-js/cli.rb +47 -0
  25. data/lib/i18n-js/cli/command.rb +43 -0
  26. data/lib/i18n-js/cli/export_command.rb +108 -0
  27. data/lib/i18n-js/cli/init_command.rb +50 -0
  28. data/lib/i18n-js/cli/ui.rb +30 -0
  29. data/lib/i18n-js/cli/version_command.rb +18 -0
  30. data/lib/i18n-js/listen.rb +80 -0
  31. data/lib/i18n-js/schema.rb +92 -0
  32. data/lib/i18n-js/version.rb +5 -0
  33. metadata +123 -190
  34. data/.editorconfig +0 -24
  35. data/.npmignore +0 -27
  36. data/.travis.yml +0 -39
  37. data/Appraisals +0 -44
  38. data/app/assets/javascripts/i18n.js +0 -1092
  39. data/app/assets/javascripts/i18n/filtered.js.erb +0 -23
  40. data/app/assets/javascripts/i18n/shims.js +0 -240
  41. data/app/assets/javascripts/i18n/translations.js +0 -3
  42. data/gemfiles/i18n_0_6.gemfile +0 -7
  43. data/gemfiles/i18n_0_7.gemfile +0 -7
  44. data/gemfiles/i18n_0_8.gemfile +0 -7
  45. data/gemfiles/i18n_0_9.gemfile +0 -7
  46. data/gemfiles/i18n_1_0.gemfile +0 -7
  47. data/gemfiles/i18n_1_1.gemfile +0 -7
  48. data/gemfiles/i18n_1_2.gemfile +0 -7
  49. data/gemfiles/i18n_1_3.gemfile +0 -7
  50. data/gemfiles/i18n_1_4.gemfile +0 -7
  51. data/gemfiles/i18n_1_5.gemfile +0 -7
  52. data/gemfiles/i18n_1_6.gemfile +0 -7
  53. data/gemfiles/i18n_1_7.gemfile +0 -7
  54. data/gemfiles/i18n_1_8.gemfile +0 -7
  55. data/lib/i18n/js.rb +0 -259
  56. data/lib/i18n/js/dependencies.rb +0 -63
  57. data/lib/i18n/js/engine.rb +0 -87
  58. data/lib/i18n/js/fallback_locales.rb +0 -70
  59. data/lib/i18n/js/formatters/base.rb +0 -25
  60. data/lib/i18n/js/formatters/js.rb +0 -32
  61. data/lib/i18n/js/formatters/json.rb +0 -13
  62. data/lib/i18n/js/middleware.rb +0 -82
  63. data/lib/i18n/js/private/hash_with_symbol_keys.rb +0 -36
  64. data/lib/i18n/js/segment.rb +0 -80
  65. data/lib/i18n/js/utils.rb +0 -78
  66. data/lib/i18n/js/version.rb +0 -7
  67. data/lib/rails/generators/i18n/js/config/config_generator.rb +0 -19
  68. data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +0 -27
  69. data/lib/tasks/export.rake +0 -8
  70. data/package.json +0 -25
  71. data/spec/fixtures/custom_path.yml +0 -5
  72. data/spec/fixtures/default.yml +0 -5
  73. data/spec/fixtures/erb.yml +0 -5
  74. data/spec/fixtures/except_condition.yml +0 -7
  75. data/spec/fixtures/js_export_dir_custom.yml +0 -7
  76. data/spec/fixtures/js_export_dir_none.yml +0 -6
  77. data/spec/fixtures/js_extend_parent.yml +0 -6
  78. data/spec/fixtures/js_extend_segment.yml +0 -6
  79. data/spec/fixtures/js_file_per_locale.yml +0 -7
  80. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +0 -4
  81. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +0 -6
  82. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +0 -4
  83. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +0 -4
  84. data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +0 -4
  85. data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +0 -4
  86. data/spec/fixtures/js_file_with_namespace_prefix_and_pretty_print.yml +0 -9
  87. data/spec/fixtures/js_sort_translation_keys_false.yml +0 -6
  88. data/spec/fixtures/js_sort_translation_keys_true.yml +0 -6
  89. data/spec/fixtures/json_only.yml +0 -18
  90. data/spec/fixtures/locales.yml +0 -133
  91. data/spec/fixtures/merge_plurals.yml +0 -6
  92. data/spec/fixtures/merge_plurals_with_no_overrides.yml +0 -4
  93. data/spec/fixtures/merge_plurals_with_partial_overrides.yml +0 -4
  94. data/spec/fixtures/millions.yml +0 -4
  95. data/spec/fixtures/multiple_conditions.yml +0 -7
  96. data/spec/fixtures/multiple_conditions_per_locale.yml +0 -7
  97. data/spec/fixtures/multiple_files.yml +0 -7
  98. data/spec/fixtures/no_config.yml +0 -2
  99. data/spec/fixtures/no_scope.yml +0 -4
  100. data/spec/fixtures/simple_scope.yml +0 -5
  101. data/spec/js/currency.spec.js +0 -62
  102. data/spec/js/current_locale.spec.js +0 -19
  103. data/spec/js/dates.spec.js +0 -276
  104. data/spec/js/defaults.spec.js +0 -31
  105. data/spec/js/extend.spec.js +0 -110
  106. data/spec/js/interpolation.spec.js +0 -124
  107. data/spec/js/jasmine/MIT.LICENSE +0 -20
  108. data/spec/js/jasmine/jasmine-html.js +0 -190
  109. data/spec/js/jasmine/jasmine.css +0 -166
  110. data/spec/js/jasmine/jasmine.js +0 -2476
  111. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  112. data/spec/js/locales.spec.js +0 -31
  113. data/spec/js/localization.spec.js +0 -78
  114. data/spec/js/numbers.spec.js +0 -170
  115. data/spec/js/placeholder.spec.js +0 -24
  116. data/spec/js/pluralization.spec.js +0 -219
  117. data/spec/js/prepare_options.spec.js +0 -41
  118. data/spec/js/require.js +0 -2083
  119. data/spec/js/specs.html +0 -49
  120. data/spec/js/specs_requirejs.html +0 -72
  121. data/spec/js/translate.spec.js +0 -299
  122. data/spec/js/translations.js +0 -172
  123. data/spec/js/utility_functions.spec.js +0 -20
  124. data/spec/ruby/i18n/js/fallback_locales_spec.rb +0 -84
  125. data/spec/ruby/i18n/js/segment_spec.rb +0 -219
  126. data/spec/ruby/i18n/js/utils_spec.rb +0 -106
  127. data/spec/ruby/i18n/js_spec.rb +0 -748
  128. data/spec/spec_helper.rb +0 -79
  129. data/yarn.lock +0 -131
@@ -1,63 +0,0 @@
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? || rails6?) &&
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? || rails6? || 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 rails6?
47
- rails? && Rails.version.to_i == 6
48
- end
49
-
50
- def safe_gem_check(*args)
51
- if Gem::Specification.respond_to?(:find_by_name)
52
- Gem::Specification.find_by_name(*args)
53
- elsif Gem.respond_to?(:available?)
54
- Gem.available?(*args)
55
- end
56
- rescue Gem::LoadError
57
- false
58
- end
59
-
60
- end
61
- end
62
- end
63
- end
@@ -1,87 +0,0 @@
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
@@ -1,70 +0,0 @@
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::JS.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,25 +0,0 @@
1
- module I18n
2
- module JS
3
- module Formatters
4
- class Base
5
- def initialize(js_extend: false, namespace: nil, pretty_print: false, prefix: nil, suffix: nil)
6
- @js_extend = js_extend
7
- @namespace = namespace
8
- @pretty_print = pretty_print
9
- @prefix = prefix
10
- @suffix = suffix
11
- end
12
-
13
- protected
14
-
15
- def format_json(translations)
16
- if @pretty_print
17
- ::JSON.pretty_generate(translations)
18
- else
19
- translations.to_json
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,32 +0,0 @@
1
- require "i18n/js/formatters/base"
2
-
3
- module I18n
4
- module JS
5
- module Formatters
6
- class JS < Base
7
- def format(translations)
8
- contents = header
9
- translations.each do |locale, translations_for_locale|
10
- contents << line(locale, format_json(translations_for_locale))
11
- end
12
- contents << (@suffix || '')
13
- end
14
-
15
- protected
16
-
17
- def header
18
- text = @prefix || ''
19
- text + %(#{@namespace}.translations || (#{@namespace}.translations = {});\n)
20
- end
21
-
22
- def line(locale, translations)
23
- if @js_extend
24
- %(#{@namespace}.translations["#{locale}"] = I18n.extend((#{@namespace}.translations["#{locale}"] || {}), #{translations});\n)
25
- else
26
- %(#{@namespace}.translations["#{locale}"] = #{translations};\n)
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,13 +0,0 @@
1
- require "i18n/js/formatters/base"
2
-
3
- module I18n
4
- module JS
5
- module Formatters
6
- class JSON < Base
7
- def format(translations)
8
- format_json(translations)
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,82 +0,0 @@
1
- require "fileutils"
2
-
3
- module I18n
4
- module JS
5
- class Middleware
6
- def initialize(app)
7
- @app = app
8
- clear_cache
9
- end
10
-
11
- def call(env)
12
- @cache = nil
13
- verify_locale_files!
14
- @app.call(env)
15
- end
16
-
17
- private
18
- def 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")
24
- end
25
-
26
- def cache
27
- @cache ||= begin
28
- if cache_path.exist?
29
- YAML.load_file(cache_path) || {}
30
- else
31
- {}
32
- end
33
- end
34
- end
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
-
54
- # Check if translations should be regenerated.
55
- # ONLY REGENERATE when these conditions are met:
56
- #
57
- # # Cache file doesn't exist
58
- # # Translations and cache size are different (files were removed/added)
59
- # # Translation file has been updated
60
- #
61
- def verify_locale_files!
62
- valid_cache = []
63
- new_cache = {}
64
-
65
- valid_cache.push cache_path.exist?
66
- valid_cache.push ::I18n.load_path.uniq.size == cache.size
67
-
68
- ::I18n.load_path.each do |path|
69
- changed_at = File.mtime(path).to_i
70
- valid_cache.push changed_at == cache[path]
71
- new_cache[path] = changed_at
72
- end
73
-
74
- return if valid_cache.all?
75
-
76
- save_cache(new_cache)
77
-
78
- ::I18n::JS.export
79
- end
80
- end
81
- end
82
- end
@@ -1,36 +0,0 @@
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
@@ -1,80 +0,0 @@
1
- require "i18n/js/private/hash_with_symbol_keys"
2
- require "i18n/js/formatters/js"
3
- require "i18n/js/formatters/json"
4
-
5
- module I18n
6
- module JS
7
-
8
- # Class which enscapulates a translations hash and outputs a single JSON translation file
9
- class Segment
10
- OPTIONS = [:namespace, :pretty_print, :js_extend, :prefix, :suffix, :sort_translation_keys, :json_only].freeze
11
- LOCALE_INTERPOLATOR = /%\{locale\}/
12
-
13
- attr_reader *([:file, :translations] | OPTIONS)
14
-
15
- def initialize(file, translations, options = {})
16
- @file = file
17
- # `#slice` will be used
18
- # But when activesupport is absent,
19
- # the core extension from `i18n` gem will be used instead
20
- # And it's causing errors (at least in test)
21
- #
22
- # So the input is wrapped by our class for better `#slice`
23
- @translations = Private::HashWithSymbolKeys.new(translations)
24
- @namespace = options[:namespace] || 'I18n'
25
- @pretty_print = !!options[:pretty_print]
26
- @js_extend = options.key?(:js_extend) ? !!options[:js_extend] : true
27
- @prefix = options.key?(:prefix) ? options[:prefix] : nil
28
- @suffix = options.key?(:suffix) ? options[:suffix] : nil
29
- @sort_translation_keys = options.key?(:sort_translation_keys) ? !!options[:sort_translation_keys] : true
30
- @json_only = options.key?(:json_only) ? !!options[:json_only] : false
31
- end
32
-
33
- # Saves JSON file containing translations
34
- def save!
35
- if @file =~ LOCALE_INTERPOLATOR
36
- I18n.available_locales.each do |locale|
37
- write_file(file_for_locale(locale), @translations.slice(locale))
38
- end
39
- else
40
- write_file
41
- end
42
- end
43
-
44
- protected
45
-
46
- def file_for_locale(locale)
47
- @file.gsub(LOCALE_INTERPOLATOR, locale.to_s)
48
- end
49
-
50
- def write_file(_file = @file, _translations = @translations)
51
- FileUtils.mkdir_p File.dirname(_file)
52
- _translations = Utils.deep_key_sort(_translations) if @sort_translation_keys
53
- contents = formatter.format(_translations)
54
-
55
- return if File.exist?(_file) && File.read(_file) == contents
56
-
57
- File.open(_file, "w+") do |f|
58
- f << contents
59
- end
60
- end
61
-
62
- def formatter
63
- if @json_only
64
- Formatters::JSON.new(**formatter_options)
65
- else
66
- Formatters::JS.new(**formatter_options)
67
- end
68
- end
69
-
70
- def formatter_options
71
- { js_extend: @js_extend,
72
- namespace: @namespace,
73
- pretty_print: @pretty_print,
74
- prefix: @prefix,
75
- suffix: @suffix
76
- }
77
- end
78
- end
79
- end
80
- end