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:
parent
7ac9a4d2ec
commit
ea8b5c18db
|
@ -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}}
|
||||
|
|
|
@ -8,6 +8,10 @@ import { test } from "qunit";
|
|||
|
||||
acceptance("Admin - Site Texts", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
available_locales: JSON.stringify([{ name: "English", value: "en" }]),
|
||||
default_locale: "en",
|
||||
});
|
||||
|
||||
test("search for a key", async function (assert) {
|
||||
await visit("/admin/customize/site_texts");
|
||||
|
@ -31,7 +35,7 @@ acceptance("Admin - Site Texts", function (needs) {
|
|||
});
|
||||
|
||||
test("edit and revert a site text by key", async function (assert) {
|
||||
await visit("/admin/customize/site_texts/site.test");
|
||||
await visit("/admin/customize/site_texts/site.test?locale=en");
|
||||
|
||||
assert.equal(queryAll(".title h3").text(), "site.test");
|
||||
assert.ok(!exists(".saved"));
|
||||
|
|
|
@ -644,9 +644,15 @@ export function applyDefaultHandlers(pretender) {
|
|||
|
||||
pretender.get("/admin/customize/site_texts", (request) => {
|
||||
if (request.queryParams.overridden) {
|
||||
return response(200, { site_texts: [overridden] });
|
||||
return response(200, {
|
||||
site_texts: [overridden],
|
||||
extras: { locale: "en" },
|
||||
});
|
||||
} else {
|
||||
return response(200, { site_texts: [siteText, overridden] });
|
||||
return response(200, {
|
||||
site_texts: [siteText, overridden],
|
||||
extras: { locale: "en" },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -204,6 +204,12 @@ $mobile-breakpoint: 700px;
|
|||
.reseed {
|
||||
float: right;
|
||||
}
|
||||
.locale {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.locale-search {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
.text-highlight {
|
||||
font-weight: bold;
|
||||
|
|
|
@ -21,14 +21,13 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
|
||||
query = params[:q] || ""
|
||||
|
||||
locale = params[:locale] || I18n.locale
|
||||
raise Discourse::InvalidParameters.new(:locale) if !I18n.locale_available?(locale)
|
||||
locale = fetch_locale(params[:locale])
|
||||
|
||||
if query.blank? && !overridden
|
||||
extras[:recommended] = true
|
||||
results = I18n.with_locale(locale) { self.class.preferred_keys.map { |k| record_for(k) } }
|
||||
results = self.class.preferred_keys.map { |k| record_for(key: k, locale: locale) }
|
||||
else
|
||||
results = I18n.with_locale(locale) { find_translations(query, overridden) }
|
||||
results = find_translations(query, overridden, locale)
|
||||
|
||||
if results.any?
|
||||
extras[:regex] = I18n::Backend::DiscourseI18n.create_search_regexp(query, as_string: true)
|
||||
|
@ -53,20 +52,37 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
last = first + per_page
|
||||
|
||||
extras[:has_more] = true if results.size > last
|
||||
render_serialized(results[first..last - 1], SiteTextSerializer, root: 'site_texts', rest_serializer: true, extras: extras, overridden_keys: overridden_keys)
|
||||
|
||||
if LocaleSiteSetting.fallback_locale(locale).present?
|
||||
extras[:fallback_locale] = LocaleSiteSetting.fallback_locale(locale)
|
||||
end
|
||||
|
||||
overridden = overridden_keys(locale)
|
||||
render_serialized(
|
||||
results[first..last - 1],
|
||||
SiteTextSerializer,
|
||||
root: 'site_texts',
|
||||
rest_serializer: true,
|
||||
extras: extras,
|
||||
overridden_keys: overridden,
|
||||
)
|
||||
end
|
||||
|
||||
def show
|
||||
site_text = find_site_text
|
||||
locale = fetch_locale(params[:locale])
|
||||
site_text = find_site_text(locale)
|
||||
render_serialized(site_text, SiteTextSerializer, root: 'site_text', rest_serializer: true)
|
||||
end
|
||||
|
||||
def update
|
||||
site_text = find_site_text
|
||||
value = site_text[:value] = params[:site_text][:value]
|
||||
locale = fetch_locale(params.dig(:site_text, :locale))
|
||||
|
||||
site_text = find_site_text(locale)
|
||||
value = site_text[:value] = params.dig(:site_text, :value)
|
||||
id = site_text[:id]
|
||||
old_value = I18n.t(id)
|
||||
translation_override = TranslationOverride.upsert!(I18n.locale, id, value)
|
||||
old_value = I18n.t(id, locale: locale)
|
||||
|
||||
translation_override = TranslationOverride.upsert!(locale, id, value)
|
||||
|
||||
if translation_override.errors.empty?
|
||||
StaffActionLogger.new(current_user).log_site_text_change(id, value, old_value)
|
||||
|
@ -88,11 +104,14 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
end
|
||||
|
||||
def revert
|
||||
site_text = find_site_text
|
||||
locale = fetch_locale(params[:locale])
|
||||
|
||||
site_text = find_site_text(locale)
|
||||
id = site_text[:id]
|
||||
old_text = I18n.t(id)
|
||||
TranslationOverride.revert!(I18n.locale, id)
|
||||
site_text = find_site_text
|
||||
old_text = I18n.t(id, locale: locale)
|
||||
TranslationOverride.revert!(locale, id)
|
||||
|
||||
site_text = find_site_text(locale)
|
||||
StaffActionLogger.new(current_user).log_site_text_change(id, site_text[:value], old_text)
|
||||
system_badge_id = Badge.find_system_badge_id_from_translation_key(id)
|
||||
if system_badge_id.present?
|
||||
|
@ -137,39 +156,39 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
badge_parts[0] == 'badges' && badge_parts[2] == 'name'
|
||||
end
|
||||
|
||||
def record_for(key, value = nil)
|
||||
def record_for(key:, value: nil, locale:)
|
||||
if key.ends_with?("_MF")
|
||||
override = TranslationOverride.where(translation_key: key, locale: I18n.locale).pluck(:value)
|
||||
override = TranslationOverride.where(translation_key: key, locale: locale).pluck(:value)
|
||||
value = override&.first
|
||||
end
|
||||
|
||||
value ||= I18n.t(key)
|
||||
value ||= I18n.t(key, locale: locale)
|
||||
{ id: key, value: value }
|
||||
end
|
||||
|
||||
PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)$/
|
||||
|
||||
def find_site_text
|
||||
def find_site_text(locale)
|
||||
if self.class.restricted_keys.include?(params[:id])
|
||||
raise Discourse::InvalidAccess.new(nil, nil, custom_message: 'email_template_cant_be_modified')
|
||||
end
|
||||
|
||||
if I18n.exists?(params[:id]) || TranslationOverride.exists?(locale: I18n.locale, translation_key: params[:id])
|
||||
return record_for(params[:id])
|
||||
if I18n.exists?(params[:id], locale) || TranslationOverride.exists?(locale: locale, translation_key: params[:id])
|
||||
return record_for(key: params[:id], locale: locale)
|
||||
end
|
||||
|
||||
if PLURALIZED_REGEX.match(params[:id])
|
||||
value = fix_plural_keys($1, {}).fetch($2.to_sym)
|
||||
return record_for(params[:id], value) if value
|
||||
value = fix_plural_keys($1, {}, locale).detect { |plural| plural[0] == $2.to_sym }
|
||||
return record_for(key: params[:id], value: value[1], locale: value[2]) if value
|
||||
end
|
||||
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
|
||||
def find_translations(query, overridden)
|
||||
def find_translations(query, overridden, locale)
|
||||
translations = Hash.new { |hash, key| hash[key] = {} }
|
||||
|
||||
I18n.search(query, overridden: overridden).each do |key, value|
|
||||
I18n.search(query, overridden: overridden, locale: locale).each do |key, value|
|
||||
if PLURALIZED_REGEX.match(key)
|
||||
translations[$1][$2] = value
|
||||
else
|
||||
|
@ -183,30 +202,46 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||
next unless I18n.exists?(key, :en)
|
||||
|
||||
if value&.is_a?(Hash)
|
||||
value = fix_plural_keys(key, value)
|
||||
value.each do |plural_key, plural_value|
|
||||
results << record_for("#{key}.#{plural_key}", plural_value)
|
||||
fix_plural_keys(key, value, locale).each do |plural|
|
||||
plural_key = plural[0]
|
||||
plural_value = plural[1]
|
||||
|
||||
results << record_for(
|
||||
key: "#{key}.#{plural_key}", value: plural_value, locale: plural.last
|
||||
)
|
||||
end
|
||||
else
|
||||
results << record_for(key, value)
|
||||
results << record_for(key: key, value: value, locale: locale)
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def fix_plural_keys(key, value)
|
||||
def fix_plural_keys(key, value, locale)
|
||||
value = value.with_indifferent_access
|
||||
plural_keys = I18n.t('i18n.plural.keys')
|
||||
plural_keys = I18n.t('i18n.plural.keys', locale: locale)
|
||||
return value if value.keys.size == plural_keys.size && plural_keys.all? { |k| value.key?(k) }
|
||||
|
||||
fallback_value = I18n.t(key, locale: :en, default: {})
|
||||
plural_keys.map do |k|
|
||||
[k, value[k] || fallback_value[k] || fallback_value[:other]]
|
||||
end.to_h
|
||||
if value[k]
|
||||
[k, value[k], locale]
|
||||
else
|
||||
[k, fallback_value[k] || fallback_value[:other], :en]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def overridden_keys
|
||||
TranslationOverride.where(locale: I18n.locale).pluck(:translation_key)
|
||||
def overridden_keys(locale)
|
||||
TranslationOverride.where(locale: locale).pluck(:translation_key)
|
||||
end
|
||||
|
||||
def fetch_locale(locale_from_params)
|
||||
locale_from_params.tap do |locale|
|
||||
if locale.blank? || !I18n.locale_available?(locale)
|
||||
raise Discourse::InvalidParameters.new(:locale)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4826,6 +4826,8 @@ en:
|
|||
go_back: "Back to Search"
|
||||
recommended: "We recommend customizing the following text to suit your needs:"
|
||||
show_overriden: "Only show overridden"
|
||||
locale: "Language:"
|
||||
fallback_locale_warning: "You are editing a variety of %{fallback}. Users who choose %{fallback} as their interface language won't see your changes."
|
||||
more_than_50_results: "There are more than 50 results. Please refine your search."
|
||||
|
||||
settings: # used by theme and site settings
|
||||
|
|
|
@ -71,7 +71,7 @@ module I18n
|
|||
opts ||= {}
|
||||
|
||||
target = opts[:backend] || backend
|
||||
results = opts[:overridden] ? {} : target.search(config.locale, query)
|
||||
results = opts[:overridden] ? {} : target.search(locale, query)
|
||||
|
||||
regexp = I18n::Backend::DiscourseI18n.create_search_regexp(query)
|
||||
(overrides_by_locale(locale) || {}).each do |k, v|
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'rails_helper'
|
|||
RSpec.describe Admin::SiteTextsController do
|
||||
fab!(:admin) { Fabricate(:admin) }
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:default_locale) { I18n.locale }
|
||||
|
||||
after do
|
||||
TranslationOverride.delete_all
|
||||
|
@ -18,7 +19,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
context "when not logged in as an admin" do
|
||||
it "raises an error if you aren't logged in" do
|
||||
put '/admin/customize/site_texts/some_key.json', params: {
|
||||
site_text: { value: 'foo' }
|
||||
site_text: { value: 'foo' }, locale: default_locale
|
||||
}
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
|
@ -28,7 +29,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
sign_in(user)
|
||||
|
||||
put "/admin/customize/site_texts/some_key.json", params: {
|
||||
site_text: { value: 'foo' }
|
||||
site_text: { value: 'foo' }, locale: default_locale
|
||||
}
|
||||
expect(response.status).to eq(404)
|
||||
|
||||
|
@ -46,13 +47,13 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
describe '#index' do
|
||||
it 'returns json' do
|
||||
get "/admin/customize/site_texts.json", params: { q: 'title' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'title', locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts']).to include(include("id" => "title"))
|
||||
end
|
||||
|
||||
it 'sets has_more to true if more than 50 results were found' do
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e', locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts'].size).to eq(50)
|
||||
expect(response.parsed_body['extras']['has_more']).to be_truthy
|
||||
|
@ -61,23 +62,23 @@ RSpec.describe Admin::SiteTextsController do
|
|||
it 'works with pages' do
|
||||
texts = Set.new
|
||||
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e', locale: default_locale }
|
||||
response.parsed_body['site_texts'].each { |text| texts << text['id'] }
|
||||
expect(texts.size).to eq(50)
|
||||
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e', page: 1 }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'e', page: 1, locale: default_locale }
|
||||
response.parsed_body['site_texts'].each { |text| texts << text['id'] }
|
||||
expect(texts.size).to eq(100)
|
||||
end
|
||||
|
||||
it 'works with locales' do
|
||||
get "/admin/customize/site_texts.json", params: { q: 'yes_value', locale: 'en' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'yes_value', locale: default_locale }
|
||||
value = response.parsed_body['site_texts'].find { |text| text['id'] == 'js.yes_value' }['value']
|
||||
expect(value).to eq(I18n.with_locale(:en) { I18n.t('js.yes_value') })
|
||||
expect(value).to eq(I18n.t('js.yes_value', locale: default_locale))
|
||||
|
||||
get "/admin/customize/site_texts.json", params: { q: 'yes_value', locale: 'de' }
|
||||
value = response.parsed_body['site_texts'].find { |text| text['id'] == 'js.yes_value' }['value']
|
||||
expect(value).to eq(I18n.with_locale(:de) { I18n.t('js.yes_value') })
|
||||
expect(value).to eq(I18n.t('js.yes_value', locale: :de))
|
||||
end
|
||||
|
||||
it 'returns an error on invalid locale' do
|
||||
|
@ -85,9 +86,14 @@ RSpec.describe Admin::SiteTextsController do
|
|||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it 'returns an error on empty locale' do
|
||||
get "/admin/customize/site_texts.json"
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it 'normalizes quotes during search' do
|
||||
value = %q|“That’s a ‘magic’ sock.”|
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: value } }
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: value, locale: default_locale } }
|
||||
|
||||
[
|
||||
%q|That's a 'magic' sock.|,
|
||||
|
@ -97,7 +103,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
%q|«That's a 'magic' sock.»|,
|
||||
%q|„That’s a ‚magic‘ sock.“|
|
||||
].each do |search_term|
|
||||
get "/admin/customize/site_texts.json", params: { q: search_term }
|
||||
get "/admin/customize/site_texts.json", params: { q: search_term, locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts']).to include(include("id" => "title", "value" => value))
|
||||
end
|
||||
|
@ -105,14 +111,14 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
it 'normalizes ellipsis' do
|
||||
value = "Loading Discussion…"
|
||||
put "/admin/customize/site_texts/embed.loading.json", params: { site_text: { value: value } }
|
||||
put "/admin/customize/site_texts/embed.loading.json", params: { site_text: { value: value, locale: default_locale } }
|
||||
|
||||
[
|
||||
"Loading Discussion",
|
||||
"Loading Discussion...",
|
||||
"Loading Discussion…"
|
||||
].each do |search_term|
|
||||
get "/admin/customize/site_texts.json", params: { q: search_term }
|
||||
get "/admin/customize/site_texts.json", params: { q: search_term, locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts']).to include(include("id" => "embed.loading", "value" => value))
|
||||
end
|
||||
|
@ -123,11 +129,11 @@ RSpec.describe Admin::SiteTextsController do
|
|||
TranslationOverride.create!(locale: :ru, translation_key: 'missing_plural_key.one', value: 'ONE')
|
||||
TranslationOverride.create!(locale: :ru, translation_key: 'another_missing_key', value: 'foo')
|
||||
|
||||
get "/admin/customize/site_texts.json", params: { q: 'missing_plural_key' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'missing_plural_key', locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts']).to be_empty
|
||||
|
||||
get "/admin/customize/site_texts.json", params: { q: 'another_missing_key' }
|
||||
get "/admin/customize/site_texts.json", params: { q: 'another_missing_key', locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['site_texts']).to be_empty
|
||||
end
|
||||
|
@ -141,7 +147,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
it 'finds the correct plural keys for the locale' do
|
||||
SiteSetting.default_locale = locale
|
||||
|
||||
get '/admin/customize/site_texts.json', params: { q: 'colour' }
|
||||
get '/admin/customize/site_texts.json', params: { q: 'colour', locale: locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = ::JSON.parse(response.body, symbolize_names: true)
|
||||
|
@ -202,7 +208,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
describe '#show' do
|
||||
it 'returns a site text for a key that exists' do
|
||||
get "/admin/customize/site_texts/js.topic.list.json"
|
||||
get "/admin/customize/site_texts/js.topic.list.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
@ -214,7 +220,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
end
|
||||
|
||||
it 'returns a site text for a key with ampersand' do
|
||||
get "/admin/customize/site_texts/js.emoji_picker.food_&_drink.json"
|
||||
get "/admin/customize/site_texts/js.emoji_picker.food_&_drink.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
@ -226,30 +232,46 @@ RSpec.describe Admin::SiteTextsController do
|
|||
end
|
||||
|
||||
it 'returns not found for missing keys' do
|
||||
get "/admin/customize/site_texts/made_up_no_key_exists.json"
|
||||
get "/admin/customize/site_texts/made_up_no_key_exists.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
|
||||
it 'returns overridden = true if there is a translation_overrides record for the key' do
|
||||
key = 'js.topic.list'
|
||||
put "/admin/customize/site_texts/#{key}.json", params: {
|
||||
site_text: { value: I18n.t(key) }
|
||||
site_text: { value: I18n.t(key, locale: default_locale), locale: default_locale }
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
get "/admin/customize/site_texts/#{key}.json"
|
||||
get "/admin/customize/site_texts/#{key}.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['overridden']).to eq(true)
|
||||
|
||||
TranslationOverride.destroy_all
|
||||
|
||||
get "/admin/customize/site_texts/#{key}.json"
|
||||
get "/admin/customize/site_texts/#{key}.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['overridden']).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns a site text in the given locale' do
|
||||
get "/admin/customize/site_texts/js.topic.list.json", params: { locale: 'es' }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
site_text = json['site_text']
|
||||
|
||||
expect(site_text['id']).to eq('js.topic.list')
|
||||
expect(site_text['value']).to eq(I18n.t("js.topic.list", locale: :es))
|
||||
end
|
||||
|
||||
it 'fails if locale is not given' do
|
||||
get "/admin/customize/site_texts/js.topic.list.json"
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
context 'plural keys' do
|
||||
before do
|
||||
I18n.backend.store_translations(:en, colour: { one: '%{count} colour', other: '%{count} colours' })
|
||||
|
@ -257,12 +279,10 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
shared_examples 'has correct plural keys' do
|
||||
it 'returns the correct plural keys for the locale' do
|
||||
SiteSetting.default_locale = locale
|
||||
|
||||
expected_translations.each do |key, value|
|
||||
id = "colour.#{key}"
|
||||
|
||||
get "/admin/customize/site_texts/#{id}.json"
|
||||
get "/admin/customize/site_texts/#{id}.json", params: { locale: locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
@ -307,7 +327,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
describe '#update & #revert' do
|
||||
it "returns 'not found' when an unknown key is used" do
|
||||
put '/admin/customize/site_texts/some_key.json', params: {
|
||||
site_text: { value: 'foo' }
|
||||
site_text: { value: 'foo', locale: default_locale }
|
||||
}
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
|
@ -318,7 +338,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
it "works as expected with correct keys" do
|
||||
put '/admin/customize/site_texts/js.emoji_picker.animals_%26_nature.json', params: {
|
||||
site_text: { value: 'foo' }
|
||||
site_text: { value: 'foo', locale: default_locale }
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
@ -332,7 +352,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
|
||||
it "does not update restricted keys" do
|
||||
put '/admin/customize/site_texts/user_notifications.confirm_old_email.title.json', params: {
|
||||
site_text: { value: 'foo' }
|
||||
site_text: { value: 'foo', locale: default_locale }
|
||||
}
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
|
@ -347,7 +367,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
I18n.backend.store_translations(SiteSetting.default_locale, some_key: '%{first} %{second}')
|
||||
|
||||
put "/admin/customize/site_texts/some_key.json", params: {
|
||||
site_text: { value: 'hello %{key} %{omg}' }
|
||||
site_text: { value: 'hello %{key} %{omg}', locale: default_locale }
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
|
@ -364,7 +384,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
original_title = I18n.t(:title)
|
||||
|
||||
put "/admin/customize/site_texts/title.json", params: {
|
||||
site_text: { value: 'yay' }
|
||||
site_text: { value: 'yay', locale: default_locale }
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
|
@ -374,7 +394,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
expect(log.new_value).to eq('yay')
|
||||
expect(log.action).to eq(UserHistory.actions[:change_site_text])
|
||||
|
||||
delete "/admin/customize/site_texts/title.json"
|
||||
delete "/admin/customize/site_texts/title.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
log = UserHistory.last
|
||||
|
@ -387,7 +407,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
it 'updates and reverts the key' do
|
||||
orig_title = I18n.t(:title)
|
||||
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: 'hello' } }
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: 'hello', locale: default_locale } }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
@ -398,7 +418,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
expect(site_text['value']).to eq('hello')
|
||||
|
||||
# Revert
|
||||
delete "/admin/customize/site_texts/title.json"
|
||||
delete "/admin/customize/site_texts/title.json", params: { locale: default_locale }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
@ -410,34 +430,34 @@ RSpec.describe Admin::SiteTextsController do
|
|||
end
|
||||
|
||||
it 'returns site texts for the correct locale' do
|
||||
SiteSetting.default_locale = :ru
|
||||
locale = :ru
|
||||
|
||||
ru_title = 'title ru'
|
||||
ru_mf_text = 'ru {NUM_RESULTS, plural, one {1 result} other {many} }'
|
||||
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: ru_title } }
|
||||
put "/admin/customize/site_texts/title.json", params: { site_text: { value: ru_title, locale: locale } }
|
||||
expect(response.status).to eq(200)
|
||||
put "/admin/customize/site_texts/js.topic.read_more_MF.json", params: { site_text: { value: ru_mf_text } }
|
||||
put "/admin/customize/site_texts/js.topic.read_more_MF.json", params: { site_text: { value: ru_mf_text, locale: locale } }
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
get "/admin/customize/site_texts/title.json"
|
||||
get "/admin/customize/site_texts/title.json", params: { locale: locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['value']).to eq(ru_title)
|
||||
|
||||
get "/admin/customize/site_texts/js.topic.read_more_MF.json"
|
||||
get "/admin/customize/site_texts/js.topic.read_more_MF.json", params: { locale: locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['value']).to eq(ru_mf_text)
|
||||
|
||||
SiteSetting.default_locale = :en
|
||||
en_locale = :en
|
||||
|
||||
get "/admin/customize/site_texts/title.json"
|
||||
get "/admin/customize/site_texts/title.json", params: { locale: en_locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['value']).to_not eq(ru_title)
|
||||
|
||||
get "/admin/customize/site_texts/js.topic.read_more_MF.json"
|
||||
get "/admin/customize/site_texts/js.topic.read_more_MF.json", params: { locale: en_locale }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
expect(json['site_text']['value']).to_not eq(ru_mf_text)
|
||||
|
@ -459,7 +479,7 @@ RSpec.describe Admin::SiteTextsController do
|
|||
action: Jobs::BulkUserTitleUpdate::UPDATE_ACTION
|
||||
}) do
|
||||
put '/admin/customize/site_texts/badges.regular.name.json', params: {
|
||||
site_text: { value: 'Terminator' }
|
||||
site_text: { value: 'Terminator', locale: default_locale }
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -468,14 +488,14 @@ RSpec.describe Admin::SiteTextsController do
|
|||
granted_badge_id: badge.id,
|
||||
action: Jobs::BulkUserTitleUpdate::RESET_ACTION
|
||||
}) do
|
||||
delete "/admin/customize/site_texts/badges.regular.name.json"
|
||||
delete "/admin/customize/site_texts/badges.regular.name.json", params: { locale: default_locale }
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not update matching user titles when overriding non-title badge text' do
|
||||
expect_not_enqueued_with(job: :bulk_user_title_update) do
|
||||
put '/admin/customize/site_texts/badges.regular.long_description.json', params: {
|
||||
site_text: { value: 'Terminator' }
|
||||
site_text: { value: 'Terminator', locale: default_locale }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue