DEV: Add endpoint for dismissing outdated translations (#22509)

Recently we started giving admins a notice in the advice panel when their translations have become outdated due to changes in core. However, we didn't include any additional information.

This PR adds more information about the outdated translation inside the site text edit page, together with an option to dismiss the warning.
This commit is contained in:
Ted Johansson 2023-07-19 23:06:13 +08:00 committed by GitHub
parent 3820fae041
commit 341acacba8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 11 deletions

View File

@ -16,6 +16,11 @@ export default Controller.extend(bufferedProperty("siteText"), {
return this.siteText.value === value; return this.siteText.value === value;
}, },
@discourseComputed("siteText.status")
isOutdated(status) {
return status === "outdated";
},
@action @action
saveChanges() { saveChanges() {
const attrs = this.buffered.getProperties("value"); const attrs = this.buffered.getProperties("value");
@ -49,6 +54,16 @@ export default Controller.extend(bufferedProperty("siteText"), {
}); });
}, },
@action
dismissOutdated() {
this.siteText
.dismissOutdated(this.locale)
.then(() => {
this.siteText.set("status", "up_to_date");
})
.catch(popupAjaxError);
},
get interpolationKeys() { get interpolationKeys() {
return this.siteText.interpolation_keys.join(", "); return this.siteText.interpolation_keys.join(", ");
}, },

View File

@ -8,4 +8,13 @@ export default class SiteText extends RestModel {
type: "DELETE", type: "DELETE",
}).then((result) => getProperties(result.site_text, "value", "can_revert")); }).then((result) => getProperties(result.site_text, "value", "can_revert"));
} }
dismissOutdated(locale) {
return ajax(
`/admin/customize/site_texts/${this.id}/dismiss_outdated?locale=${locale}`,
{
type: "PUT",
}
);
}
} }

View File

@ -7,6 +7,22 @@
<h4>{{i18n "admin.site_text.locale"}} {{this.localeFullName}}</h4> <h4>{{i18n "admin.site_text.locale"}} {{this.localeFullName}}</h4>
</div> </div>
{{#if this.isOutdated}}
<div class="outdated">
<h4>{{i18n "admin.site_text.outdated.title"}}</h4>
<p>{{i18n "admin.site_text.outdated.description"}}</p>
<h5>{{i18n "admin.site_text.outdated.old_default"}}</h5>
<p>{{this.siteText.old_default}}</p>
<h5>{{i18n "admin.site_text.outdated.new_default"}}</h5>
<p>{{this.siteText.new_default}}</p>
<DButton
@class="btn-default"
@action={{action "dismissOutdated"}}
@label="admin.site_text.outdated.dismiss"
/>
</div>
{{/if}}
<ExpandingTextArea <ExpandingTextArea
@value={{this.buffered.value}} @value={{this.buffered.value}}
@rows="1" @rows="1"

View File

@ -295,6 +295,18 @@ $mobile-breakpoint: 700px;
line-height: var(--line-height-large); line-height: var(--line-height-large);
color: var(--primary-medium); color: var(--primary-medium);
} }
.outdated {
border: 1px solid var(--primary-low);
box-sizing: border-box;
color: var(--primary);
margin-bottom: 1em;
max-width: 800px;
padding: 1em;
p {
color: var(--primary-high);
}
}
} }
p.warning { p.warning {
color: var(--danger); color: var(--danger);

View File

@ -128,6 +128,26 @@ class Admin::SiteTextsController < Admin::AdminController
render_serialized(site_text, SiteTextSerializer, root: "site_text", rest_serializer: true) render_serialized(site_text, SiteTextSerializer, root: "site_text", rest_serializer: true)
end end
def dismiss_outdated
locale = fetch_locale(params[:locale])
override = TranslationOverride.find_by(locale: locale, translation_key: params[:id])
raise Discourse::NotFound if override.blank?
if override.outdated?
override.update!(
status: "up_to_date",
original_translation:
I18n.overrides_disabled do
I18n.t(TranslationOverride.transform_pluralized_key(params[:id]), locale: :en)
end,
)
render json: success_json
else
render json: failed_json.merge(message: "Can only dismiss outdated translations"), status: 422
end
end
def get_reseed_options def get_reseed_options
render_json_dump( render_json_dump(
categories: SeedData::Categories.with_default_locale.reseed_options, categories: SeedData::Categories.with_default_locale.reseed_options,

View File

@ -138,19 +138,13 @@ class TranslationOverride < ActiveRecord::Base
def original_translation_updated? def original_translation_updated?
return false if original_translation.blank? return false if original_translation.blank?
transformed_key = self.class.transform_pluralized_key(translation_key) original_translation != current_default
original_translation != I18n.overrides_disabled { I18n.t(transformed_key, locale: :en) }
end end
def invalid_interpolation_keys def invalid_interpolation_keys
transformed_key = self.class.transform_pluralized_key(translation_key) return [] if current_default.blank?
original_text = I18n.overrides_disabled { I18n.t(transformed_key, locale: :en) } original_interpolation_keys = I18nInterpolationKeysFinder.find(current_default)
return [] if original_text.blank?
original_interpolation_keys = I18nInterpolationKeysFinder.find(original_text)
new_interpolation_keys = I18nInterpolationKeysFinder.find(value) new_interpolation_keys = I18nInterpolationKeysFinder.find(value)
custom_interpolation_keys = [] custom_interpolation_keys = []
@ -162,6 +156,10 @@ class TranslationOverride < ActiveRecord::Base
custom_interpolation_keys custom_interpolation_keys
end end
def current_default
I18n.overrides_disabled { I18n.t(transformed_key, locale: :en) }
end
private private
def transformed_key def transformed_key

View File

@ -1,7 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
class SiteTextSerializer < ApplicationSerializer class SiteTextSerializer < ApplicationSerializer
attributes :id, :value, :interpolation_keys, :has_interpolation_keys?, :overridden?, :can_revert? attributes :id,
:value,
:status,
:old_default,
:new_default,
:interpolation_keys,
:has_interpolation_keys?,
:overridden?,
:can_revert?
def id def id
object[:id] object[:id]
@ -11,6 +19,22 @@ class SiteTextSerializer < ApplicationSerializer
object[:value] object[:value]
end end
def status
if override.present?
override.status
else
"up_to_date"
end
end
def old_default
override.original_translation if override.present?
end
def new_default
override.current_default if override.present?
end
def interpolation_keys def interpolation_keys
object[:interpolation_keys] object[:interpolation_keys]
end end
@ -23,9 +47,15 @@ class SiteTextSerializer < ApplicationSerializer
if options[:overridden_keys] if options[:overridden_keys]
options[:overridden_keys].include?(object[:id]) options[:overridden_keys].include?(object[:id])
else else
TranslationOverride.exists?(locale: object[:locale], translation_key: object[:id]) override.present?
end end
end end
alias_method :can_revert?, :overridden? alias_method :can_revert?, :overridden?
private
def override
TranslationOverride.find_by(locale: object[:locale], translation_key: object[:id])
end
end end

View File

@ -6098,6 +6098,12 @@ en:
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:" interpolation_keys: "Available interpolation keys:"
outdated:
title: "This translation is outdated"
description: "The default translation for this key has changed since this override was created. Please check below that your translation matches any changes that have been made to the original intent."
old_default: "Old default"
new_default: "New default"
dismiss: "Dismiss"
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

@ -257,6 +257,14 @@ Discourse::Application.routes.draw do
id: /[\w.\-\+\%\&]+/i, id: /[\w.\-\+\%\&]+/i,
} }
delete "site_texts/:id" => "site_texts#revert", :constraints => { id: /[\w.\-\+\%\&]+/i } delete "site_texts/:id" => "site_texts#revert", :constraints => { id: /[\w.\-\+\%\&]+/i }
put "site_texts/:id/dismiss_outdated" => "site_texts#dismiss_outdated",
:constraints => {
id: /[\w.\-\+\%\&]+/i,
}
put "site_texts/:id/dismiss_outdated.json" => "site_texts#dismiss_outdated",
:constraints => {
id: /[\w.\-\+\%\&]+/i,
}
get "reseed" => "site_texts#get_reseed_options" get "reseed" => "site_texts#get_reseed_options"
post "reseed" => "site_texts#reseed" post "reseed" => "site_texts#reseed"

View File

@ -224,6 +224,9 @@ RSpec.describe Admin::SiteTextsController do
{ {
id: "colour.#{key}", id: "colour.#{key}",
value: value, value: value,
status: "up_to_date",
old_default: nil,
new_default: nil,
can_revert: overridden, can_revert: overridden,
overridden: overridden, overridden: overridden,
interpolation_keys: interpolation_keys, interpolation_keys: interpolation_keys,
@ -815,6 +818,70 @@ RSpec.describe Admin::SiteTextsController do
end end
end end
describe "#dismiss_outdated" do
before { sign_in(admin) }
context "when using a key which isn't overridden" do
it "returns a not found error" do
put "/admin/customize/site_texts/title/dismiss_outdated.json",
params: {
locale: default_locale,
}
expect(response.status).to eq(404)
json = response.parsed_body
expect(json["error_type"]).to eq("not_found")
end
end
context "when the override isn't outdated" do
before do
Fabricate(
:translation_override,
locale: default_locale,
translation_key: "title",
value: "My Forum",
)
end
it "returns an unprocessable entity error" do
put "/admin/customize/site_texts/title/dismiss_outdated.json",
params: {
locale: default_locale,
}
expect(response.status).to eq(422)
json = response.parsed_body
expect(json["failed"]).to eq("FAILED")
expect(json["message"]).to eq("Can only dismiss outdated translations")
end
end
context "when the override is outdated" do
before do
Fabricate(
:translation_override,
locale: default_locale,
translation_key: "title",
value: "My Forum",
status: "outdated",
)
end
it "returns success" do
put "/admin/customize/site_texts/title/dismiss_outdated.json",
params: {
locale: default_locale,
}
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
end
end
end
context "when reseeding" do context "when reseeding" do
before do before do
staff_category = Fabricate(:category, name: "Staff EN", user: Discourse.system_user) staff_category = Fabricate(:category, name: "Staff EN", user: Discourse.system_user)