diff --git a/lib/i18n.rb b/lib/i18n.rb index d3369704..06fb7844 100644 --- a/lib/i18n.rb +++ b/lib/i18n.rb @@ -231,6 +231,35 @@ def translate!(key, **options) end alias :t! :translate! + # Returns an array of interpolation keys for the given translation key + # + # *Examples* + # + # Suppose we have the following: + # I18n.t 'example.zero' == 'Zero interpolations' + # I18n.t 'example.one' == 'One interpolation %{foo}' + # I18n.t 'example.two' == 'Two interpolations %{foo} %{bar}' + # I18n.t 'example.three' == ['One %{foo}', 'Two %{bar}', 'Three %{baz}'] + # I18n.t 'example.one', locale: :other == 'One interpolation %{baz}' + # + # Then we can expect the following results: + # I18n.interpolation_keys('example.zero') #=> [] + # I18n.interpolation_keys('example.one') #=> ['foo'] + # I18n.interpolation_keys('example.two') #=> ['foo', 'bar'] + # I18n.interpolation_keys('example.three') #=> ['foo', 'bar', 'baz'] + # I18n.interpolation_keys('one', scope: 'example', locale: :other) #=> ['baz'] + # I18n.interpolation_keys('does-not-exist') #=> [] + # I18n.interpolation_keys('example') #=> [] + def interpolation_keys(key, **options) + raise I18n::ArgumentError if !key.is_a?(String) || key.empty? + + return [] unless exists?(key, **options.slice(:locale, :scope)) + + translation = translate(key, **options.slice(:locale, :scope)) + interpolation_keys_from_translation(translation) + .flatten.compact + end + # Returns true if a translation exists for a given key, otherwise returns false. def exists?(key, _locale = nil, locale: _locale, **options) locale ||= config.locale @@ -429,6 +458,17 @@ def normalize_key(key, separator) keys end end + + def interpolation_keys_from_translation(translation) + case translation + when ::String + translation.scan(Regexp.union(I18n.config.interpolation_patterns)) + when ::Array + translation.map { |element| interpolation_keys_from_translation(element) } + else + [] + end + end end extend Base diff --git a/test/i18n_test.rb b/test/i18n_test.rb index 3f576fb8..85679d40 100644 --- a/test/i18n_test.rb +++ b/test/i18n_test.rb @@ -287,6 +287,34 @@ def setup assert_equal I18n.config.available_locales.size * 2, I18n.config.available_locales_set.size end + test "interpolation_keys returns an array of keys" do + store_translations(:en, "example_two" => "Two interpolations %{foo} %{bar}") + assert_equal ["foo", "bar"], I18n.interpolation_keys("example_two") + end + + test "interpolation_keys returns an empty array when no interpolations " do + store_translations(:en, "example_zero" => "Zero interpolations") + assert_equal [], I18n.interpolation_keys("example_zero") + end + + test "interpolation_keys returns an empty array when missing translation " do + assert_equal [], I18n.interpolation_keys("does-not-exist") + end + + test "interpolation_keys returns an empty array when nested translation" do + store_translations(:en, "example_nested" => { "one" => "One %{foo}", "two" => "Two %{bar}" }) + assert_equal [], I18n.interpolation_keys("example_nested") + end + + test "interpolation_keys returns an array of keys when translation is an Array" do + store_translations(:en, "example_array" => ["One %{foo}", ["Two %{bar}", ["Three %{baz}"]]]) + assert_equal ["foo", "bar", "baz"], I18n.interpolation_keys("example_array") + end + + test "interpolation_keys raises I18n::ArgumentError when non-string argument" do + assert_raises(I18n::ArgumentError) { I18n.interpolation_keys(["bad-argument"]) } + end + test "exists? given an existing key will return true" do assert_equal true, I18n.exists?(:currency) end