Class: LcpRuby::I18nCheck::RegistryWalker

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/i18n_check/registry_walker.rb

Overview

Boot-time registry walker — the single pass that backs ‘lcp_ruby:i18n_check`.

Iterates every labeled element in ‘LcpRuby.loader.*_definitions` through the iteration façade on each Definition class (`each_section`, `each_field`, …). For each element:

1. Computes the canonical i18n key chain via `KeyDeriver`.
2. Checks `I18n.exists?(key, locale, fallback: false)` for every
   configured locale.
3. Emits an `Offense`:
     - `:literal_in_dsl` when the author overrode the humanized
       default AND the locale entry is missing
     - `:missing_translation` otherwise (or always for
       `missing_only` Tier-1 kinds without a DSL literal —
       workflow states, menu entries, transitions)
   Severity for missing-translation downgrades to `:info` when
   `I18n.fallbacks` covers every missing locale.

YAML-loaded definitions surface as ‘source: nil` from the façade. The walker falls back to `definition.source_path` (line nil) so they still emit with file context, just without per-row precision. Definitions with NO source at all (engine-internal definitions registered programmatically) are skipped — see Decision 4.

See ‘docs/design/i18n_consistency_check.md` for the full specification.

Constant Summary collapse

EXEMPT_RE =

Line-precise opt-out marker. The pattern (and the mandatory non-empty reason after the colon) mirrors ‘lcp_ruby:i18n_lint` and the `LcpRuby/NoHardcodedI18nString` cop. Recognized only when the offense carries both file and line — YAML-loaded definitions (line: nil) opt out via `config.exclude` globs instead.

/#\s*i18n-exempt:\s*(?<reason>\S.*)/.freeze

Instance Method Summary collapse

Constructor Details

#initialize(config: Configuration.new, key_deriver: KeyDeriver.new, loader: nil) ⇒ RegistryWalker

Returns a new instance of RegistryWalker.

Raises:

  • (ArgumentError)


42
43
44
45
46
47
48
# File 'lib/lcp_ruby/i18n_check/registry_walker.rb', line 42

def initialize(config: Configuration.new, key_deriver: KeyDeriver.new, loader: nil)
  @config      = config
  @key_deriver = key_deriver
  @loader      = loader || (defined?(LcpRuby) && LcpRuby.respond_to?(:loader) ? LcpRuby.loader : nil)
  raise ArgumentError, "RegistryWalker requires a loader" unless @loader
  @file_cache  = {}
end

Instance Method Details

#runObject



50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/lcp_ruby/i18n_check/registry_walker.rb', line 50

def run
  offenses = []
  @loader.presenter_definitions.each_value  { |p| offenses.concat(walk_presenter(p)) }
  @loader.model_definitions.each_value      { |m| offenses.concat(walk_model(m)) }
  @loader.workflow_definitions.each_value   { |w| offenses.concat(walk_workflow(w)) }
  @loader.view_group_definitions.each_value { |v| offenses.concat(walk_view_group(v)) }
  if @loader.respond_to?(:page_definitions)
    @loader.page_definitions.each_value { |p| offenses.concat(walk_page(p)) }
  end
  offenses.concat(walk_menu(@loader.menu_definition)) if @loader.respond_to?(:menu_defined?) && @loader.menu_defined?
  dedup_tier2(offenses.compact)
end