mirror of
https://github.com/we-promise/sure.git
synced 2026-09-05 23:01:26 +00:00
* fix(i18n): remove duplicate locale keys that shadow translations
YAML resolves duplicate keys by silently letting the last occurrence
win, so every duplicated key hides an earlier definition without any
warning:
- transactions/{en,de,nl,hu}.yml defined `transactions.merge_duplicate`
twice: once as a stale flat string ("Yes, merge them") and once as the
`success`/`failure` mapping the controllers actually read. The flat
string is dead either way (the show view reads
`transactions.show.merge_duplicate`), so drop it.
- securities/fr.yml listed five providers (tiingo, eodhd, alpha_vantage,
mfapi, binance_public) twice inside `securities.providers`.
- settings/api_keys/hu.yml defined
`settings.api_keys.created.usage_instructions_title` twice.
- imports/en.yml defined `imports.create.ndjson_uploaded` twice.
All removals keep the currently-winning value, so rendered output is
unchanged; this only makes the files match what I18n already loads.
Add a non-skipped I18nTest case that walks the raw Psych AST of every
file under config/locales and fails on same-level duplicate keys, so
shadowed translations can't sneak back in.
Fixes #1506
Fixes #1502
* Fix linter / merge errors
* fix(i18n): repair duplicate locale regression test
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: rsnetworkinginc <rsnetworkinginc@users.noreply.github.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: sure-admin <sure-admin@splashblot.com>
118 lines
4.3 KiB
Ruby
118 lines
4.3 KiB
Ruby
require "i18n/tasks"
|
|
require "pathname"
|
|
require "yaml"
|
|
|
|
# We're currently skipping some i18n tests to speed up development. Eventually, we'll make a dedicated
|
|
# project for getting i18n working. More details on that here:
|
|
# https://github.com/maybe-finance/maybe/issues/1225
|
|
class I18nTest < ActiveSupport::TestCase
|
|
GERMAN_COVERAGE_GLOBS = [
|
|
"config/locales/breadcrumbs/*.yml",
|
|
"config/locales/doorkeeper.*.yml",
|
|
"config/locales/mailers/**/*.yml",
|
|
"config/locales/models/**/*.yml",
|
|
"config/locales/views/**/*.yml"
|
|
]
|
|
|
|
def setup
|
|
@i18n = I18n::Tasks::BaseTask.new
|
|
end
|
|
|
|
def test_german_locale_files_parse
|
|
german_locale_paths.each do |path|
|
|
assert_nothing_raised do
|
|
YAML.load_file(path, aliases: true)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_no_missing_keys
|
|
skip "Skipping missing keys test"
|
|
missing_keys = @i18n.missing_keys(locales: [ :en ])
|
|
assert_empty missing_keys,
|
|
"Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
|
|
end
|
|
|
|
def test_no_unused_keys
|
|
skip "Skipping unused keys test"
|
|
unused_keys = @i18n.unused_keys(locales: [ :en ])
|
|
assert_empty unused_keys,
|
|
"#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
|
|
end
|
|
|
|
def test_files_are_normalized
|
|
skip "Skipping file normalization test"
|
|
non_normalized = @i18n.non_normalized_paths(locales: [ :en ])
|
|
error_message = "The following files need to be normalized:\n" \
|
|
"#{non_normalized.map { |path| " #{path}" }.join("\n")}\n" \
|
|
"Please run `i18n-tasks normalize' to fix"
|
|
assert_empty non_normalized, error_message
|
|
end
|
|
|
|
def test_no_inconsistent_interpolations
|
|
skip "Skipping inconsistent interpolations test"
|
|
inconsistent_interpolations = @i18n.inconsistent_interpolations(locales: [ :en ])
|
|
error_message = "#{inconsistent_interpolations.leaves.count} i18n keys have inconsistent interpolations.\n" \
|
|
"Please run `i18n-tasks check-consistent-interpolations' to show them"
|
|
assert_empty inconsistent_interpolations, error_message
|
|
end
|
|
|
|
# YAML silently resolves duplicate keys by letting the last occurrence win,
|
|
# so a duplicated key shadows the earlier definition without any warning
|
|
# (see #1506 / #1502, where a stale `transactions.merge_duplicate` string
|
|
# shadowed — or was shadowed by — the `merge_duplicate.success/failure`
|
|
# mapping in several locales). Parse the raw YAML AST so duplicates can't
|
|
# sneak back in.
|
|
def test_no_duplicate_keys_within_locale_files
|
|
offenses = []
|
|
|
|
Dir[File.expand_path("../config/locales/**/*.yml", __dir__)].sort.each do |file|
|
|
Psych.parse_stream(File.read(file), filename: file).children.each do |doc|
|
|
offenses.concat(duplicate_key_offenses(doc.root, [], file))
|
|
end
|
|
end
|
|
|
|
assert_empty offenses,
|
|
"Duplicate keys found in locale files (the last occurrence silently wins):\n" \
|
|
"#{offenses.map { |offense| " #{offense}" }.join("\n")}"
|
|
end
|
|
|
|
private
|
|
def german_locale_paths
|
|
@german_locale_paths ||= locale_paths.select { |path| path.basename.to_s.match?(/(^|[._-])de\.yml\z/) }
|
|
end
|
|
|
|
def locale_paths
|
|
@locale_paths ||= GERMAN_COVERAGE_GLOBS.flat_map { |glob| Pathname.pwd.glob(glob) }.uniq
|
|
end
|
|
|
|
def duplicate_key_offenses(node, path, file)
|
|
offenses = []
|
|
|
|
case node
|
|
when Psych::Nodes::Mapping
|
|
first_definition_lines = {}
|
|
|
|
node.children.each_slice(2) do |key_node, value_node|
|
|
if key_node.is_a?(Psych::Nodes::Scalar)
|
|
key_path = path + [ key_node.value ]
|
|
|
|
if (first_line = first_definition_lines[key_node.value])
|
|
offenses << "#{file}:#{key_node.start_line + 1} duplicate key `#{key_path.join(".")}` (first defined on line #{first_line})"
|
|
else
|
|
first_definition_lines[key_node.value] = key_node.start_line + 1
|
|
end
|
|
|
|
offenses.concat(duplicate_key_offenses(value_node, key_path, file))
|
|
else
|
|
offenses.concat(duplicate_key_offenses(value_node, path, file))
|
|
end
|
|
end
|
|
when Psych::Nodes::Sequence
|
|
node.children.each { |child| offenses.concat(duplicate_key_offenses(child, path, file)) }
|
|
end
|
|
|
|
offenses
|
|
end
|
|
end
|