UX: Text customization for different languages. (#11729)

Admins can now edit translations in different languages without having to change their locale. We display a warning when there's a fallback language set.
This commit is contained in:
Roman Rizzi
2021-01-18 14:53:45 -03:00
committed by GitHub
parent 7ac9a4d2ec
commit ea8b5c18db
14 changed files with 255 additions and 96 deletions
@@ -7,6 +7,7 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend(bufferedProperty("siteText"), {
saved: false,
queryParams: ["locale"],
@discourseComputed("buffered.value")
saveDisabled(value) {
@@ -15,9 +16,11 @@ export default Controller.extend(bufferedProperty("siteText"), {
actions: {
saveChanges() {
const buffered = this.buffered;
const attrs = this.buffered.getProperties("value");
attrs.locale = this.locale;
this.siteText
.save(buffered.getProperties("value"))
.save(attrs)
.then(() => {
this.commitBuffer();
this.set("saved", true);
@@ -27,10 +30,11 @@ export default Controller.extend(bufferedProperty("siteText"), {
revertChanges() {
this.set("saved", false);
bootbox.confirm(I18n.t("admin.site_text.revert_confirm"), (result) => {
if (result) {
this.siteText
.revert()
.revert(this.locale)
.then((props) => {
const buffered = this.buffered;
buffered.setProperties(props);
@@ -1,4 +1,5 @@
import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce";
let lastSearch;
@@ -6,23 +7,49 @@ export default Controller.extend({
searching: false,
siteTexts: null,
preferred: false,
queryParams: ["q", "overridden"],
queryParams: ["q", "overridden", "locale"],
locale: null,
q: null,
overridden: false,
init() {
this._super(...arguments);
this.set("locale", this.siteSettings.default_locale);
},
_performSearch() {
this.store
.find("site-text", this.getProperties("q", "overridden"))
.find("site-text", this.getProperties("q", "overridden", "locale"))
.then((results) => {
this.set("siteTexts", results);
})
.finally(() => this.set("searching", false));
},
@discourseComputed()
availableLocales() {
return JSON.parse(this.siteSettings.available_locales);
},
@discourseComputed("locale")
fallbackLocaleFullName() {
if (this.siteTexts.extras.fallback_locale) {
return this.availableLocales.find((l) => {
return l.value === this.siteTexts.extras.fallback_locale;
}).name;
}
},
actions: {
edit(siteText) {
this.transitionToRoute("adminSiteText.edit", siteText.get("id"));
this.transitionToRoute("adminSiteText.edit", siteText.get("id"), {
queryParams: {
locale: this.locale,
localeFullName: this.availableLocales[this.locale],
},
});
},
toggleOverridden() {
@@ -39,5 +66,14 @@ export default Controller.extend({
lastSearch = q;
}
},
updateLocale(value) {
this.setProperties({
searching: true,
locale: value,
});
discourseDebounce(this, this._performSearch, 400);
},
},
});
@@ -1,10 +1,10 @@
import RestModel from "discourse/models/rest";
import { ajax } from "discourse/lib/ajax";
const { getProperties } = Ember;
import { getProperties } from "@ember/object";
export default RestModel.extend({
revert() {
return ajax(`/admin/customize/site_texts/${this.id}`, {
revert(locale) {
return ajax(`/admin/customize/site_texts/${this.id}?locale=${locale}`, {
type: "DELETE",
}).then((result) => getProperties(result.site_text, "value", "can_revert"));
},
@@ -1,10 +1,30 @@
import Route from "@ember/routing/route";
import { ajax } from "discourse/lib/ajax";
export default Route.extend({
queryParams: {
locale: { replace: true },
},
model(params) {
return this.store.find("site-text", params.id);
return ajax(
`/admin/customize/site_texts/${params.id}?locale=${params.locale}`
).then((result) => {
return this.store.createRecord("site-text", result.site_text);
});
},
setupController(controller, siteText) {
controller.setProperties({ siteText, saved: false });
const locales = JSON.parse(this.siteSettings.available_locales);
const localeFullName = locales.find((locale) => {
return locale.value === controller.locale;
}).name;
controller.setProperties({
siteText,
saved: false,
localeFullName: localeFullName,
});
},
});
@@ -6,12 +6,13 @@ export default Route.extend({
queryParams: {
q: { replace: true },
overridden: { replace: true },
locale: { replace: true },
},
model(params) {
return this.store.find(
"site-text",
getProperties(params, "q", "overridden")
getProperties(params, "q", "overridden", "locale")
);
},
@@ -1,9 +1,12 @@
<div class="edit-site-text">
<div class="title">
<h3>{{siteText.id}}</h3>
</div>
<div class="title">
<h4>{{i18n "admin.site_text.locale"}} {{localeFullName}}</h4>
</div>
{{expanding-text-area value=buffered.value rows="1" class="site-text-value"}}
{{#save-controls model=siteText action=(action "saveChanges") saved=saved saveDisabled=saveDisabled}}
@@ -12,7 +15,7 @@
{{/if}}
{{/save-controls}}
{{#link-to "adminSiteText.index" class="go-back"}}
{{#link-to "adminSiteText.index" (query-params locale=locale) class="go-back"}}
{{d-icon "arrow-left"}}
{{i18n "admin.site_text.go_back"}}
{{/link-to}}
@@ -16,6 +16,21 @@
</div>
<p class="filter-options">
<div class="locale">
<label>{{i18n "admin.site_text.locale"}}</label>
{{combo-box
valueProperty="value"
content=availableLocales
value=locale
onChange=(action "updateLocale")
class="locale-search"
options=(hash
filterable=true
none="user.locale.default"
)
}}
</div>
<label>
{{input type="checkbox" checked=overridden click=(action "toggleOverridden")}}
{{i18n "admin.site_text.show_overriden"}}
@@ -24,6 +39,13 @@
</div>
{{#conditional-loading-spinner condition=searching}}
{{#if fallbackLocaleFullName}}
<div class="alert alert-info">
{{d-icon "exclamation-circle"}}
{{i18n "admin.site_text.fallback_locale_warning" fallback=fallbackLocaleFullName}}
</div>
{{/if}}
{{#if siteTexts.extras.recommended}}
<p><b>{{i18n "admin.site_text.recommended"}}</b></p>
{{/if}}