module I18n::Processes::UsedKeys

Constants

ALWAYS_EXCLUDE
SEARCH_DEFAULTS

Public Instance Methods

caching_file_finder_provider() click to toggle source
# File lib/i18n/processes/used_keys.rb, line 116
def caching_file_finder_provider
  @caching_file_finder_provider ||= Scanners::Files::CachingFileFinderProvider.new(exclude: ALWAYS_EXCLUDE)
end
caching_file_reader() click to toggle source
# File lib/i18n/processes/used_keys.rb, line 120
def caching_file_reader
  @caching_file_reader ||= Scanners::Files::CachingFileReader.new
end
merge_scanner_configs(a, b) click to toggle source
# File lib/i18n/processes/used_keys.rb, line 104
def merge_scanner_configs(a, b)
  a.deep_merge(b).tap do |c|
    %i[scanners paths relative_roots].each do |key|
      c[key] = a[key] if b[key].blank?
    end
    %i[exclude].each do |key|
      merged = Array(a[key]) + Array(b[key])
      c[key] = merged unless merged.empty?
    end
  end
end
scanner(strict: nil) click to toggle source
# File lib/i18n/processes/used_keys.rb, line 64
def scanner(strict: nil)
  (@scanner ||= {})[strict?(strict)] ||= begin
    shared_options = search_config.dup
    shared_options.delete(:scanners)
    shared_options[:strict] = strict unless strict.nil?
    log_verbose 'Scanners: '
    Scanners::ScannerMultiplexer.new(
      scanners: search_config[:scanners].map do |(class_name, args)|
        if args && args[:strict]
          fail CommandError, 'the strict option is global and cannot be applied on the scanner level'
        end
        ActiveSupport::Inflector.constantize(class_name).new(
          config:               merge_scanner_configs(shared_options, args || {}),
          file_finder_provider: caching_file_finder_provider,
          file_reader:          caching_file_reader
        )
      end.tap { |scanners| log_verbose { scanners.map { |s| "  #{s.class.name} #{s.config.inspect}" } * "\n" } }
    )
  end
end
search_config() click to toggle source
# File lib/i18n/processes/used_keys.rb, line 85
def search_config
  @search_config ||= begin
    conf = (config[:search] || {}).deep_symbolize_keys
    if conf[:scanner]
      warn_deprecated 'search.scanner is now search.scanners, an array of [ScannerClass, options]'
      conf[:scanners] = [[conf.delete(:scanner)]]
    end
    if conf[:ignore_lines]
      warn_deprecated 'search.ignore_lines is no longer a global setting: pass it directly to the pattern scanner.'
      conf.delete(:ignore_lines)
    end
    if conf[:include]
      warn_deprecated 'search.include is now search.only'
      conf[:only] = conf.delete(:include)
    end
    merge_scanner_configs(SEARCH_DEFAULTS, conf).freeze
  end
end
used_in_expr?(key) click to toggle source

@return [Boolean] whether the key is potentially used in a code expression such as `t(“category.#{category_key}”)`

# File lib/i18n/processes/used_keys.rb, line 125
def used_in_expr?(key)
  !!(key =~ expr_key_re) # rubocop:disable Style/DoubleNegation
end
used_in_source_tree(key_filter: nil, strict: nil) click to toggle source
# File lib/i18n/processes/used_keys.rb, line 50
def used_in_source_tree(key_filter: nil, strict: nil)
  keys = ((@keys_used_in_source_tree ||= {})[strict?(strict)] ||=
            scanner(strict: strict).keys.freeze)
  if key_filter
    key_filter_re = compile_key_pattern(key_filter)
    keys          = keys.select { |k| k.key =~ key_filter_re }
  end
  Data::Tree::Node.new(
    key:      'used',
    data:     { key_filter: key_filter },
    children: Data::Tree::Siblings.from_key_occurrences(keys)
  ).to_siblings
end
used_tree(key_filter: nil, strict: nil, include_raw_references: false) click to toggle source

Find all keys in the source and return a forest with the keys in absolute form and their occurrences.

@param key_filter [String] only return keys matching this pattern. @param strict [Boolean] if true, dynamic keys are excluded (e.g. `t(“category.#{ category.key }”)`) @param include_raw_references [Boolean] if true, includes reference usages as they appear in the source @return [Data::Tree::Siblings]

# File lib/i18n/processes/used_keys.rb, line 36
def used_tree(key_filter: nil, strict: nil, include_raw_references: false)
  src_tree = used_in_source_tree(key_filter: key_filter, strict: strict)
  raw_refs, resolved_refs, used_refs = process_references(src_tree['used'].children)
  raw_refs.leaves { |node| node.data[:ref_type] = :reference_usage }
  resolved_refs.leaves { |node| node.data[:ref_type] = :reference_usage_resolved }
  used_refs.leaves { |node| node.data[:ref_type] = :reference_usage_key }
  src_tree.tap do |result|
    tree = result['used'].children
    tree.subtract_by_key!(raw_refs)
    tree.merge!(raw_refs) if include_raw_references
    tree.merge!(used_refs).merge!(resolved_refs)
  end
end

Private Instance Methods

expr_key_re(replacement: ':') click to toggle source

keys in the source that end with a ., e.g. t(“category.#{ cat.i18n_key }”) or t(“category.” + category.key) @param [String] replacement for interpolated values.

# File lib/i18n/processes/used_keys.rb, line 139
def expr_key_re(replacement: ':')
  @expr_key_re ||= begin
    # disallow patterns with no keys
    ignore_pattern_re = /\A[\.#{replacement}]*\z/
    patterns          = used_in_source_tree(strict: false).key_names.select do |k|
      k.end_with?('.') || k =~ /\#{/
    end.map do |k|
      pattern = "#{replace_key_exp(k, replacement)}#{replacement if k.end_with?('.')}"
      next if pattern =~ ignore_pattern_re
      pattern
    end.compact
    compile_key_pattern "{#{patterns * ','}}"
  end
end
replace_key_exp(key, replacement) click to toggle source

Replace interpolations in dynamic keys such as “category.#{category.i18n_key}”. @param key [String] @param replacement [String] @return [String]

# File lib/i18n/processes/used_keys.rb, line 158
def replace_key_exp(key, replacement)
  scanner = StringScanner.new(key)
  braces  = []
  result  = []
  while (match_until = scanner.scan_until(/(?:#?\{|})/))
    if scanner.matched == '#{'
      braces << scanner.matched
      result << match_until[0..-3] if braces.length == 1
    elsif scanner.matched == '}'
      prev_brace = braces.pop
      result << replacement if braces.empty? && prev_brace == '#{'
    else
      braces << '{'
    end
  end
  result << key[scanner.pos..-1] unless scanner.eos?
  result.join
end
strict?(strict) click to toggle source

@param strict [Boolean, nil] @return [Boolean]

# File lib/i18n/processes/used_keys.rb, line 133
def strict?(strict)
  strict.nil? ? search_config[:strict] : strict
end