FEATURE: add Untranslated filter to admin text customization (#27555)

Adds a checkbox to filter untranslated text strings in the admin UI, behind a hidden and default `false` site setting `admin_allow_filter_untranslated_text`.
This commit is contained in:
Renato Atilio 2024-06-24 06:24:06 -03:00 committed by GitHub
parent ce00f83173
commit 55da8a7701
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 91 additions and 6 deletions

View File

@ -19,13 +19,14 @@ export default class AdminSiteTextIndexController extends Controller {
@tracked q;
@tracked overridden;
@tracked outdated;
@tracked untranslated;
@tracked model;
@tracked searching = false;
@tracked preferred = false;
queryParams = ["q", "overridden", "outdated", "locale"];
queryParams = ["q", "overridden", "outdated", "locale", "untranslated"];
get resolvedOverridden() {
return [true, "true"].includes(this.overridden) ?? false;
@ -35,10 +36,21 @@ export default class AdminSiteTextIndexController extends Controller {
return [true, "true"].includes(this.outdated) ?? false;
}
get resolvedUntranslated() {
return [true, "true"].includes(this.untranslated) ?? false;
}
get resolvedLocale() {
return this.locale ?? this.siteSettings.default_locale;
}
get showUntranslated() {
return (
this.siteSettings.admin_allow_filter_untranslated_text &&
this.resolvedLocale !== "en"
);
}
async _performSearch() {
try {
this.model = await this.store.find("site-text", {
@ -46,6 +58,7 @@ export default class AdminSiteTextIndexController extends Controller {
overridden: this.resolvedOverridden,
outdated: this.resolvedOutdated,
locale: this.resolvedLocale,
untranslated: this.resolvedUntranslated,
});
} finally {
this.searching = false;
@ -95,6 +108,17 @@ export default class AdminSiteTextIndexController extends Controller {
discourseDebounce(this, this._performSearch, 400);
}
@action
toggleUntranslated() {
if (this.resolvedUntranslated) {
this.untranslated = null;
} else {
this.untranslated = true;
}
this.searching = true;
discourseDebounce(this, this._performSearch, 400);
}
@action
search() {
const q = this.q;

View File

@ -11,6 +11,7 @@ export default class AdminSiteTextIndexRoute extends Route {
q: { replace: true },
overridden: { replace: true },
outdated: { replace: true },
untranslated: { replace: true },
locale: { replace: true },
};
@ -19,6 +20,7 @@ export default class AdminSiteTextIndexRoute extends Route {
q: params.q,
overridden: params.overridden ?? false,
outdated: params.outdated ?? false,
untranslated: params.untranslated ?? false,
locale: params.locale ?? this.siteSettings.default_locale,
});
}

View File

@ -51,6 +51,18 @@
/>
{{i18n "admin.site_text.show_outdated"}}
</label>
{{#if this.showUntranslated}}
<label class="checkbox-label">
<input
id="toggle-untranslated"
type="checkbox"
checked={{this.resolvedUntranslated}}
{{on "click" this.toggleUntranslated}}
/>
{{i18n "admin.site_text.show_untranslated"}}
</label>
{{/if}}
</p>
</div>

View File

@ -21,17 +21,18 @@ class Admin::SiteTextsController < Admin::AdminController
def index
overridden = params[:overridden] == "true"
outdated = params[:outdated] == "true"
untranslated = params[:untranslated] == "true"
extras = {}
query = params[:q] || ""
locale = fetch_locale(params[:locale])
if query.blank? && !overridden && !outdated
if query.blank? && !overridden && !outdated && !untranslated
extras[:recommended] = true
results = self.class.preferred_keys.map { |k| record_for(key: k, locale: locale) }
else
results = find_translations(query, overridden, outdated, locale)
results = find_translations(query, overridden, outdated, locale, untranslated)
if results.any?
extras[:regex] = I18n::Backend::DiscourseI18n.create_search_regexp(query, as_string: true)
@ -209,9 +210,12 @@ class Admin::SiteTextsController < Admin::AdminController
raise Discourse::NotFound
end
def find_translations(query, overridden, outdated, locale)
def find_translations(query, overridden, outdated, locale, untranslated)
translations = Hash.new { |hash, key| hash[key] = {} }
search_results = I18n.with_locale(locale) { I18n.search(query, only_overridden: overridden) }
search_results =
I18n.with_locale(locale) do
I18n.search(query, only_overridden: overridden, only_untranslated: untranslated)
end
if outdated
outdated_keys =

View File

@ -6706,6 +6706,7 @@ en:
recommended: "We recommend customizing the following text to suit your needs:"
show_overriden: "Only show overridden"
show_outdated: "Only show outdated/invalid"
show_untranslated: "Only show untranslated"
locale: "Language:"
more_than_50_results: "There are more than 50 results. Please refine your search."
no_results: "No matching site texts found"

View File

@ -3225,3 +3225,7 @@ dashboard:
hot_topics_recent_days:
hidden: true
default: 7
admin_allow_filter_untranslated_text:
hidden: true
default: false
client: true

View File

@ -78,7 +78,17 @@ module I18n
results = {}
regexp = I18n::Backend::DiscourseI18n.create_search_regexp(query)
if opts[:only_overridden]
if opts[:only_untranslated]
target = opts[:backend] || backend
target_strings = target.search(locale, query)
override_strings = overrides_by_locale(locale)
all_locale_strings = target_strings.merge(override_strings)
original_strings = target.search(:en, query)
untranslated =
original_strings.reject { |key, value| all_locale_strings.key?(key) || !value.present? }
add_if_matches(untranslated, results, regexp)
elsif opts[:only_overridden]
add_if_matches(overrides_by_locale(locale), results, regexp)
else
target = opts[:backend] || backend

View File

@ -187,6 +187,34 @@ RSpec.describe Admin::SiteTextsController do
expect(value).to eq("education.new-topic override")
end
it "returns only untranslated (english) strings" do
available_locales = I18n.config.available_locales
I18n.config.available_locales = %i[en test]
I18n.backend.store_translations(:en, { shrubbery: "Shrubbery" })
I18n.backend.store_translations(:en, { shrubbery2: "Shrubbery2" })
params = { q: "shrubbery", locale: "test", untranslated: "true" }
get "/admin/customize/site_texts.json", params: params
expect(response.status).to eq(200)
expect(response.parsed_body["site_texts"].size).to eq(2)
I18n.backend.store_translations(:test, { shrubbery: "Arbusto" })
get "/admin/customize/site_texts.json", params: params
expect(response.status).to eq(200)
expect(response.parsed_body["site_texts"].size).to eq(1)
TranslationOverride.upsert!(:test, "shrubbery2", "Arbusto2")
get "/admin/customize/site_texts.json", params: params
expect(response.status).to eq(200)
expect(response.parsed_body["site_texts"].size).to eq(0)
I18n.config.available_locales = available_locales
end
context "with plural keys" do
before do
I18n.backend.store_translations(