From 497454f16141c87c1664392050c143e74a25c51e Mon Sep 17 00:00:00 2001 From: Cantin Xu Date: Thu, 4 Aug 2022 10:04:27 +0800 Subject: [PATCH] Detect plural default values --- lib/i18n/tasks/data/tree/traversal.rb | 32 +++++++++++++------ .../scanners/ast_matchers/base_matcher.rb | 16 ++++++++++ .../ast_matchers/message_receivers_matcher.rb | 9 ++++-- .../app/controllers/events_controller.rb | 2 ++ spec/i18n_tasks_spec.rb | 2 ++ 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/lib/i18n/tasks/data/tree/traversal.rb b/lib/i18n/tasks/data/tree/traversal.rb index fea1fcae..25a54a0c 100644 --- a/lib/i18n/tasks/data/tree/traversal.rb +++ b/lib/i18n/tasks/data/tree/traversal.rb @@ -174,15 +174,29 @@ def set_each_value!(val_pattern, key_pattern = nil, &value_proc) human_key = ActiveSupport::Inflector.humanize(node.key.to_s) full_key = node.full_key default = (node.data[:occurrences] || []).detect { |o| o.default_arg.presence }.try(:default_arg) - StringInterpolation.interpolate_soft( - val_pattern, - value: node_value, - human_key: human_key, - key: full_key, - default: default, - value_or_human_key: node_value.presence || human_key, - value_or_default_or_human_key: node_value.presence || default || human_key - ) + if default.is_a?(Hash) + default.each_with_object({}) do |(k, v), h| + h[k] = StringInterpolation.interpolate_soft( + val_pattern, + value: node_value, + human_key: human_key, + key: full_key, + default: v, + value_or_human_key: node_value.presence || human_key, + value_or_default_or_human_key: node_value.presence || v || human_key + ) + end + else + StringInterpolation.interpolate_soft( + val_pattern, + value: node_value, + human_key: human_key, + key: full_key, + default: default, + value_or_human_key: node_value.presence || human_key, + value_or_default_or_human_key: node_value.presence || default || human_key + ) + end end pattern_re = I18n::Tasks::KeyPatternMatching.compile_key_pattern(key_pattern) if key_pattern.present? keys.each do |key, node| diff --git a/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb b/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb index bad50eb9..887de1cf 100644 --- a/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +++ b/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb @@ -54,6 +54,22 @@ def extract_string(node, array_join_with: nil, array_flatten: false, array_rejec end end + # Extract the whole hash from a node of type `:hash` + # + # @param node [AST::Node] a node of type `:hash`. + # @return [Hash] the whole hash from the node + def extract_hash(node) + return {} if node.nil? + + if node.type == :hash + node.children.each_with_object({}) do |pair, h| + key = pair.children[0].children[0].to_s + value = pair.children[1].children[0] + h[key] = value + end + end + end + # Extract a hash pair with a given literal key. # # @param node [AST::Node] a node of type `:hash`. diff --git a/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb b/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb index 79fa9ea3..9f02327d 100644 --- a/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +++ b/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb @@ -77,8 +77,13 @@ def process_options(node:, key:) key = [scope, key].join('.') unless scope == '' end - default_arg_node = extract_hash_pair(node, 'default') - default_arg = extract_string(default_arg_node.children[1]) if default_arg_node + if default_arg_node = extract_hash_pair(node, 'default') + default_arg = if default_arg_node.children[1]&.type == :hash + extract_hash(default_arg_node.children[1]) + else + extract_string(default_arg_node.children[1]) + end + end [key, default_arg] end diff --git a/spec/fixtures/app/controllers/events_controller.rb b/spec/fixtures/app/controllers/events_controller.rb index a87c320f..b79bda6d 100644 --- a/spec/fixtures/app/controllers/events_controller.rb +++ b/spec/fixtures/app/controllers/events_controller.rb @@ -37,6 +37,8 @@ def show() # default arg I18n.t('default_arg', default: 'Default Text') + I18n.t('default_plural_arg', default: { one: 'One Text', other: 'Other Text' }) + # only `t()` calls can use relative keys and not `I18n.t()` calls. I18n.t('.not_relative') diff --git a/spec/i18n_tasks_spec.rb b/spec/i18n_tasks_spec.rb index 7d03da88..3669b138 100644 --- a/spec/i18n_tasks_spec.rb +++ b/spec/i18n_tasks_spec.rb @@ -79,6 +79,7 @@ index.my_custom_scanner.title magic_comment default_arg + default_plural_arg .not_relative scope.subscope.a.b scope.key_in_erb @@ -263,6 +264,7 @@ expect(YAML.load_file('config/locales/en.yml')['en']['used_but_missing']['key']).to eq 'Key' expect(YAML.load_file('config/locales/en.yml')['en']['present_in_es_but_not_en']['a']).to eq 'ES_TEXT' expect(YAML.load_file('config/locales/en.yml')['en']['default_arg']).to eq 'Default Text' + expect(YAML.load_file('config/locales/en.yml')['en']['default_plural_arg']).to eq({ 'one' => 'One Text', 'other' => 'Other Text' }) end end