Module: I18n::JS

Defined in:
lib/i18n/js.rb,
lib/i18n/js/utils.rb,
lib/i18n/js/engine.rb,
lib/i18n/js/segment.rb,
lib/i18n/js/version.rb,
lib/i18n/js/middleware.rb,
lib/i18n/js/dependencies.rb,
lib/i18n/js/formatters/js.rb,
lib/i18n/js/formatters/base.rb,
lib/i18n/js/formatters/json.rb,
lib/i18n/js/fallback_locales.rb,
lib/i18n/js/private/config_store.rb,
lib/i18n/js/private/hash_with_symbol_keys.rb

Defined Under Namespace

Modules: Dependencies, Formatters, Private, Utils Classes: Engine, FallbackLocales, Middleware, Segment, SprocketsExtension

Constant Summary collapse

DEFAULT_CONFIG_PATH =
"config/i18n-js.yml"
DEFAULT_EXPORT_DIR_PATH =
"public/javascripts"
VERSION =
"3.9.0"

Class Method Summary collapse

Class Method Details

.backendObject

Allow using a different backend than the one globally configured



35
36
37
# File 'lib/i18n/js.rb', line 35

def self.backend
  @backend ||= I18n.backend
end

.backend=(alternative_backend) ⇒ Object



39
40
41
# File 'lib/i18n/js.rb', line 39

def self.backend=(alternative_backend)
  @backend = alternative_backend
end

.configObject

Load configuration file for partial exporting and custom output directory



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/i18n/js.rb', line 113

def self.config
  Private::ConfigStore.instance.fetch do
    if config_file_exists?
      erb_result_from_yaml_file = ERB.new(File.read(config_file_path)).result
      Private::HashWithSymbolKeys.new(
        (::YAML.load(erb_result_from_yaml_file) || {})
      )
    else
      Private::HashWithSymbolKeys.new({})
    end.freeze
  end
end

.config_file_exists?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if configuration file exist

Returns:

  • (Boolean)


128
129
130
# File 'lib/i18n/js.rb', line 128

def self.config_file_exists?
  File.file? config_file_path
end

.config_file_pathObject

The configuration file. This defaults to the ‘config/i18n-js.yml` file.



24
25
26
# File 'lib/i18n/js.rb', line 24

def self.config_file_path
  @config_file_path ||= DEFAULT_CONFIG_PATH
end

.config_file_path=(new_path) ⇒ Object



28
29
30
31
32
# File 'lib/i18n/js.rb', line 28

def self.config_file_path=(new_path)
  @config_file_path = new_path
  # new config file path = need to re-read config from new file
  Private::ConfigStore.instance.flush_cache
end

.configured_segmentsObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/i18n/js.rb', line 59

def self.configured_segments
  config[:translations].inject([]) do |segments, options_hash|
    options_hash_with_symbol_keys = Private::HashWithSymbolKeys.new(options_hash)
    file = options_hash_with_symbol_keys[:file]
    only = options_hash_with_symbol_keys[:only] || '*'
    exceptions = [options_hash_with_symbol_keys[:except] || []].flatten

    result = segment_for_scope(only, exceptions)

    merge_with_fallbacks!(result) if fallbacks

    unless result.empty?
      segments << Segment.new(
        file,
        result,
        extract_segment_options(options_hash_with_symbol_keys),
      )
    end

    segments
  end
end

.exclude(translations, exceptions) ⇒ Object

Exclude keys from translations listed in the ‘except:` section in the config file



146
147
148
149
150
151
152
153
154
155
# File 'lib/i18n/js.rb', line 146

def self.exclude(translations, exceptions)
  return translations if exceptions.empty?

  exceptions.inject(translations) do |memo, exception|
    exception_scopes = exception.to_s.split(".")
    Utils.deep_reject(memo) do |key, value, scopes|
      Utils.scopes_match?(scopes, exception_scopes)
    end
  end
end

.exportObject

Export translations to JavaScript, considering settings from configuration file



45
46
47
48
49
# File 'lib/i18n/js.rb', line 45

def self.export
  export_i18n_js

  translation_segments.each(&:save!)
end

.extract_segment_options(options) ⇒ Object



236
237
238
239
240
241
242
243
# File 'lib/i18n/js.rb', line 236

def self.extract_segment_options(options)
  segment_options = Private::HashWithSymbolKeys.new({
    js_extend: js_extend,
    sort_translation_keys: sort_translation_keys?,
    json_only: json_only
  }).freeze
  segment_options.merge(options.slice(*Segment::OPTIONS))
end

.fallbacksObject



202
203
204
205
206
207
# File 'lib/i18n/js.rb', line 202

def self.fallbacks
  config.fetch(:fallbacks) do
    # default value
    true
  end
end

.filter(translations, scopes) ⇒ Object

Filter translations according to the specified scope.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/i18n/js.rb', line 158

def self.filter(translations, scopes)
  scopes = scopes.split(".") if scopes.is_a?(String)
  scopes = scopes.clone
  scope = scopes.shift

  if scope == "*"
    results = {}
    translations.each do |scope, translations|
      tmp = scopes.empty? ? translations : filter(translations, scopes)
      results[scope.to_sym] = tmp unless tmp.nil?
    end
    return results
  elsif translations.respond_to?(:key?) && translations.key?(scope.to_sym)
    return {scope.to_sym => scopes.empty? ? translations[scope.to_sym] : filter(translations[scope.to_sym], scopes)}
  end
  nil
end

.filtered_translationsObject



93
94
95
96
97
98
99
100
101
# File 'lib/i18n/js.rb', line 93

def self.filtered_translations
  translations = {}.tap do |result|
    translation_segments.each do |segment|
      Utils.deep_merge!(result, segment.translations)
    end
  end
  return Utils.deep_key_sort(translations) if I18n::JS.sort_translation_keys?
  translations
end

.js_available_localesArray<Symbol>

Get all available locales.

Returns:

  • (Array<Symbol>)

    the locales.



219
220
221
222
223
224
# File 'lib/i18n/js.rb', line 219

def self.js_available_locales
  config.fetch(:js_available_locales) do
    # default value
    I18n.available_locales
  end.map(&:to_sym)
end

.js_extendObject



209
210
211
212
213
214
# File 'lib/i18n/js.rb', line 209

def self.js_extend
  config.fetch(:js_extend) do
    # default value
    true
  end
end

.json_onlyObject



195
196
197
198
199
200
# File 'lib/i18n/js.rb', line 195

def self.json_only
  config.fetch(:json_only) do
    # default value
    false
  end
end

.merge_with_fallbacks!(result) ⇒ Object

deep_merge! given result with result for fallback locale



83
84
85
86
87
88
89
90
91
# File 'lib/i18n/js.rb', line 83

def self.merge_with_fallbacks!(result)
  js_available_locales.each do |locale|
    fallback_locales = FallbackLocales.new(fallbacks, locale)
    fallback_locales.each do |fallback_locale|
      # `result[fallback_locale]` could be missing
      result[locale] = Utils.deep_merge(result[fallback_locale] || {}, result[locale] || {})
    end
  end
end

.scoped_translations(scopes, exceptions = []) ⇒ Object

:nodoc:



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/i18n/js.rb', line 132

def self.scoped_translations(scopes, exceptions = []) # :nodoc:
  result = {}

  [scopes].flatten.each do |scope|
    translations_without_exceptions = exclude(translations, exceptions)
    filtered_translations = filter(translations_without_exceptions, scope) || {}

    Utils.deep_merge!(result, filtered_translations)
  end

  result
end

.segment_for_scope(scope, exceptions) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/i18n/js.rb', line 51

def self.segment_for_scope(scope, exceptions)
  if scope == "*"
    exclude(translations, exceptions)
  else
    scoped_translations(scope, exceptions)
  end
end

.sort_translation_keys=(value) ⇒ Object



232
233
234
# File 'lib/i18n/js.rb', line 232

def self.sort_translation_keys=(value)
  @sort_translation_keys = !!value
end

.sort_translation_keys?Boolean

Returns:

  • (Boolean)


226
227
228
229
230
# File 'lib/i18n/js.rb', line 226

def self.sort_translation_keys?
  @sort_translation_keys ||= (config[:sort_translation_keys]) if config.key?(:sort_translation_keys)
  @sort_translation_keys = true if @sort_translation_keys.nil?
  @sort_translation_keys
end

.translation_segmentsObject



103
104
105
106
107
108
109
# File 'lib/i18n/js.rb', line 103

def self.translation_segments
  if config_file_exists? && config[:translations]
    configured_segments
  else
    [Segment.new("#{DEFAULT_EXPORT_DIR_PATH}/translations.js", translations)]
  end
end

.translationsObject

Initialize and return translations



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/i18n/js.rb', line 177

def self.translations
  self.backend.instance_eval do
    init_translations unless initialized?
    # When activesupport is absent,
    # the core extension (`#slice`) from `i18n` gem will be used instead
    # And it's causing errors (at least in test)
    #
    # So the input is wrapped by our class for better `#slice`
    Private::HashWithSymbolKeys.new(translations).
      slice(*::I18n::JS.js_available_locales).
      to_h
  end
end

.use_fallbacks?Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/i18n/js.rb', line 191

def self.use_fallbacks?
  fallbacks != false
end