FEATURE: Show available interpolation keys when overriding translations (#22220)

This is the first of a number of PRs aimed at helping admins manage their translation overrides. It simply adds a list of available interpolation keys below the input field when editing an override.

It also includes custom interpolation key.
This commit is contained in:
Ted Johansson 2023-06-28 19:03:04 +08:00 committed by GitHub
parent 7791bb1276
commit 6fc62586a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 8 deletions

View File

@ -48,4 +48,8 @@ export default Controller.extend(bufferedProperty("siteText"), {
}, },
}); });
}, },
get interpolationKeys() {
return this.siteText.interpolation_keys.join(", ");
},
}); });

View File

@ -13,6 +13,12 @@
@class="site-text-value" @class="site-text-value"
/> />
{{#if this.siteText.has_interpolation_keys}}
<div class="desc">{{i18n "admin.site_text.interpolation_keys"}}
{{this.interpolationKeys}}
</div>
{{/if}}
<SaveControls <SaveControls
@model={{this.siteText}} @model={{this.siteText}}
@action={{action "saveChanges"}} @action={{action "saveChanges"}}

View File

@ -269,8 +269,13 @@ $mobile-breakpoint: 700px;
} }
.edit-site-text { .edit-site-text {
textarea { textarea {
display: block;
width: 100%; width: 100%;
max-width: 800px; max-width: 800px;
margin: 0;
}
.save-button {
margin-top: 1em;
} }
.save-button, .save-button,
.title { .title {
@ -284,6 +289,12 @@ $mobile-breakpoint: 700px;
.go-back { .go-back {
margin-top: 1em; margin-top: 1em;
} }
.desc {
padding-top: 3px;
font-size: var(--font-down-1);
line-height: var(--line-height-large);
color: var(--primary-medium);
}
} }
p.warning { p.warning {
color: var(--danger); color: var(--danger);

View File

@ -156,8 +156,12 @@ class Admin::SiteTextsController < Admin::AdminController
end end
def record_for(key:, value: nil, locale:) def record_for(key:, value: nil, locale:)
en_key = TranslationOverride.transform_pluralized_key(key)
value ||= I18n.with_locale(locale) { I18n.t(key) } value ||= I18n.with_locale(locale) { I18n.t(key) }
{ id: key, value: value, locale: locale } interpolation_keys =
I18nInterpolationKeysFinder.find(I18n.overrides_disabled { I18n.t(en_key, locale: :en) })
custom_keys = TranslationOverride.custom_interpolation_keys(en_key)
{ id: key, value: value, locale: locale, interpolation_keys: interpolation_keys + custom_keys }
end end
PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)\z/ PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)\z/

View File

@ -105,6 +105,21 @@ class TranslationOverride < ActiveRecord::Base
true true
end end
# We use English as the source of truth when extracting interpolation keys,
# but some languages, like Arabic, have plural forms (zero, two, few, many)
# which don't exist in English (one, other), so we map that here in order to
# find the correct, English translation key in which to look.
def self.transform_pluralized_key(key)
match = key.match(/(.*)\.(zero|two|few|many)\z/)
match ? match.to_a.second + ".other" : key
end
def self.custom_interpolation_keys(translation_key)
ALLOWED_CUSTOM_INTERPOLATION_KEYS.find do |keys, value|
break value if keys.any? { |k| translation_key.start_with?(k) }
end || []
end
private_class_method :reload_locale! private_class_method :reload_locale!
private_class_method :clear_cached_keys! private_class_method :clear_cached_keys!
private_class_method :i18n_changed private_class_method :i18n_changed
@ -113,7 +128,7 @@ class TranslationOverride < ActiveRecord::Base
private private
def check_interpolation_keys def check_interpolation_keys
transformed_key = transform_pluralized_key(translation_key) transformed_key = self.class.transform_pluralized_key(translation_key)
original_text = I18n.overrides_disabled { I18n.t(transformed_key, locale: :en) } original_text = I18n.overrides_disabled { I18n.t(transformed_key, locale: :en) }
@ -144,11 +159,6 @@ class TranslationOverride < ActiveRecord::Base
end end
end end
end end
def transform_pluralized_key(key)
match = key.match(/(.*)\.(zero|two|few|many)\z/)
match ? match.to_a.second + ".other" : key
end
end end
# == Schema Information # == Schema Information

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class SiteTextSerializer < ApplicationSerializer class SiteTextSerializer < ApplicationSerializer
attributes :id, :value, :overridden?, :can_revert? attributes :id, :value, :interpolation_keys, :has_interpolation_keys?, :overridden?, :can_revert?
def id def id
object[:id] object[:id]
@ -11,6 +11,14 @@ class SiteTextSerializer < ApplicationSerializer
object[:value] object[:value]
end end
def interpolation_keys
object[:interpolation_keys]
end
def has_interpolation_keys?
object[:interpolation_keys].present?
end
def overridden? def overridden?
if options[:overridden_keys] if options[:overridden_keys]
options[:overridden_keys].include?(object[:id]) options[:overridden_keys].include?(object[:id])

View File

@ -6069,6 +6069,7 @@ en:
show_overriden: "Only show overridden" show_overriden: "Only show overridden"
locale: "Language:" locale: "Language:"
more_than_50_results: "There are more than 50 results. Please refine your search." more_than_50_results: "There are more than 50 results. Please refine your search."
interpolation_keys: "Available interpolation keys:"
settings: # used by theme and site settings settings: # used by theme and site settings
show_overriden: "Only show overridden" show_overriden: "Only show overridden"

View File

@ -213,11 +213,21 @@ RSpec.describe Admin::SiteTextsController do
expected_translations.map do |key, value| expected_translations.map do |key, value|
overridden = overridden =
defined?(expected_overridden) ? expected_overridden[key] || false : false defined?(expected_overridden) ? expected_overridden[key] || false : false
interpolation_keys =
(
if defined?(expected_interpolation_keys)
expected_interpolation_keys[key] || []
else
[]
end
)
{ {
id: "colour.#{key}", id: "colour.#{key}",
value: value, value: value,
can_revert: overridden, can_revert: overridden,
overridden: overridden, overridden: overridden,
interpolation_keys: interpolation_keys,
has_interpolation_keys: interpolation_keys.present?,
} }
end end
@ -228,6 +238,7 @@ RSpec.describe Admin::SiteTextsController do
context "with English" do context "with English" do
let(:locale) { :en } let(:locale) { :en }
let(:expected_translations) { { one: "%{count} colour", other: "%{count} colours" } } let(:expected_translations) { { one: "%{count} colour", other: "%{count} colours" } }
let(:expected_interpolation_keys) { { one: ["count"], other: ["count"] } }
include_examples "finds correct plural keys" include_examples "finds correct plural keys"
end end
@ -242,6 +253,9 @@ RSpec.describe Admin::SiteTextsController do
other: "%{count} colours", other: "%{count} colours",
} }
end end
let(:expected_interpolation_keys) do
{ one: ["count"], few: ["count"], many: ["count"], other: ["count"] }
end
include_examples "finds correct plural keys" include_examples "finds correct plural keys"
end end
@ -266,6 +280,9 @@ RSpec.describe Admin::SiteTextsController do
other: "%{count} colours", other: "%{count} colours",
} }
end end
let(:expected_interpolation_keys) do
{ one: ["count"], few: ["count"], many: ["count"], other: ["count"] }
end
include_examples "finds correct plural keys" include_examples "finds correct plural keys"
end end
@ -287,6 +304,9 @@ RSpec.describe Admin::SiteTextsController do
let(:expected_translations) do let(:expected_translations) do
{ one: "ONE", few: "FEW", many: "%{count} цветов", other: "%{count} colours" } { one: "ONE", few: "FEW", many: "%{count} цветов", other: "%{count} colours" }
end end
let(:expected_interpolation_keys) do
{ one: ["count"], few: ["count"], many: ["count"], other: ["count"] }
end
let(:expected_overridden) { { one: true, few: true } } let(:expected_overridden) { { one: true, few: true } }
include_examples "finds correct plural keys" include_examples "finds correct plural keys"
@ -425,6 +445,27 @@ RSpec.describe Admin::SiteTextsController do
expect(site_text["value"]).to eq("education.new-topic override") expect(site_text["value"]).to eq("education.new-topic override")
end end
it "includes custom interpolation keys" do
TranslationOverride.upsert!(
:en,
"system_messages.welcome_user.title",
"system_messages.welcome_user.title override",
)
get "/admin/customize/site_texts/system_messages.welcome_user.title.json",
params: {
locale: "en_GB",
}
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["site_text"]["interpolation_keys"]).to include(
"username",
"name",
"name_or_username",
)
end
context "with plural keys" do context "with plural keys" do
before do before do
I18n.backend.store_translations( I18n.backend.store_translations(