From 81c329fbb8cb141335fd59c596dc5cde3bfb402a Mon Sep 17 00:00:00 2001 From: Gerhard Schlager <mail@gerhard-schlager.at> Date: Sat, 11 May 2019 01:52:27 +0200 Subject: [PATCH] FIX: Customizing missing pluralized translations didn't work --- .../admin/site_texts_controller.rb | 76 ++++++++--- app/serializers/site_text_serializer.rb | 8 +- .../admin/site_texts_controller_spec.rb | 119 ++++++++++++++++++ 3 files changed, 184 insertions(+), 19 deletions(-) diff --git a/app/controllers/admin/site_texts_controller.rb b/app/controllers/admin/site_texts_controller.rb index 65e60629f71..c1be047c3e3 100644 --- a/app/controllers/admin/site_texts_controller.rb +++ b/app/controllers/admin/site_texts_controller.rb @@ -27,13 +27,9 @@ class Admin::SiteTextsController < Admin::AdminController extras[:recommended] = true results = self.class.preferred_keys.map { |k| record_for(k) } else - results = [] - translations = I18n.search(query, overridden: overridden) - translations.each do |k, v| - results << record_for(k, v) - end + results = find_translations(query, overridden) - unless translations.empty? + if results.any? extras[:regex] = I18n::Backend::DiscourseI18n.create_search_regexp(query, as_string: true) end @@ -110,20 +106,70 @@ class Admin::SiteTextsController < Admin::AdminController protected - def record_for(k, value = nil) - if k.ends_with?("_MF") - ovr = TranslationOverride.where(translation_key: k, locale: I18n.locale).pluck(:value) - value = ovr[0] if ovr.present? + def record_for(key, value = nil) + if key.ends_with?("_MF") + override = TranslationOverride.where(translation_key: key, locale: I18n.locale).pluck(:value) + value = override&.first end - value ||= I18n.t(k) - { id: k, value: value } + value ||= I18n.t(key) + { id: key, value: value } end + PLURALIZED_REGEX = /(.*)\.(zero|one|two|few|many|other)$/ + def find_site_text - raise Discourse::NotFound unless I18n.exists?(params[:id]) - raise Discourse::InvalidAccess.new(nil, nil, custom_message: 'email_template_cant_be_modified') if self.class.restricted_keys.include?(params[:id]) - record_for(params[:id]) + 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]) + end + + if PLURALIZED_REGEX.match(params[:id]) + value = fix_plural_keys($1, {}).fetch($2.to_sym) + return record_for(params[:id], value) if value + end + + raise Discourse::NotFound end + def find_translations(query, overridden) + translations = Hash.new { |hash, key| hash[key] = {} } + + I18n.search(query, overridden: overridden).each do |key, value| + if PLURALIZED_REGEX.match(key) + translations[$1][$2] = value + else + translations[key] = value + end + end + + results = [] + + translations.each do |key, value| + 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) + end + else + results << record_for(key, value) + end + end + + results + end + + def fix_plural_keys(key, value) + value = value.with_indifferent_access + plural_keys = I18n.t('i18n.plural.keys') + return value if value.keys.size == plural_keys.size && plural_keys.all? { |k| value.key?(k) } + + fallback_value = I18n.t(key, locale: :en) + plural_keys.map do |k| + [k, value[k] || fallback_value[k] || fallback_value[:other]] + end.to_h + end end diff --git a/app/serializers/site_text_serializer.rb b/app/serializers/site_text_serializer.rb index 0b7af1e2c4f..06987330b05 100644 --- a/app/serializers/site_text_serializer.rb +++ b/app/serializers/site_text_serializer.rb @@ -12,10 +12,10 @@ class SiteTextSerializer < ApplicationSerializer end def overridden? - current_val = value - - I18n.overrides_disabled do - return I18n.t(object[:id]) != current_val + if I18n.exists?(object[:id]) + I18n.overrides_disabled { I18n.t(object[:id]) != object[:value] } + else + TranslationOverride.exists?(locale: I18n.locale, translation_key: object[:id]) end end diff --git a/spec/requests/admin/site_texts_controller_spec.rb b/spec/requests/admin/site_texts_controller_spec.rb index 6f8a0d46bf7..234129a0e65 100644 --- a/spec/requests/admin/site_texts_controller_spec.rb +++ b/spec/requests/admin/site_texts_controller_spec.rb @@ -91,6 +91,72 @@ RSpec.describe Admin::SiteTextsController do end end + context 'plural keys' do + before do + I18n.backend.store_translations(:en, colour: { one: '%{count} colour', other: '%{count} colours' }) + end + + shared_examples 'finds correct plural keys' do + it 'finds the correct plural keys for the locale' do + SiteSetting.default_locale = locale + + get '/admin/customize/site_texts.json', params: { q: 'colour' } + expect(response.status).to eq(200) + + json = ::JSON.parse(response.body, symbolize_names: true) + expect(json).to be_present + + site_texts = json[:site_texts] + expect(site_texts).to be_present + + expected_search_result = expected_translations.map do |key, value| + overridden = defined?(expected_overridden) ? expected_overridden[key] || false : false + { id: "colour.#{key}", value: value, can_revert: overridden, overridden: overridden } + end + + expect(site_texts).to match_array(expected_search_result) + end + end + + context 'English' do + let(:locale) { :en } + let(:expected_translations) { { one: '%{count} colour', other: '%{count} colours' } } + + include_examples 'finds correct plural keys' + end + + context 'language with different plural keys and missing translations' do + let(:locale) { :ru } + let(:expected_translations) { { one: '%{count} colour', few: '%{count} colours', other: '%{count} colours' } } + + include_examples 'finds correct plural keys' + end + + context 'language with different plural keys and partial translation' do + before do + I18n.backend.store_translations(:ru, colour: { few: '%{count} цвета', many: '%{count} цветов' }) + end + + let(:locale) { :ru } + let(:expected_translations) { { one: '%{count} colour', few: '%{count} цвета', other: '%{count} colours' } } + + include_examples 'finds correct plural keys' + end + + context 'with overridden translation not in original translation' do + before do + I18n.backend.store_translations(:ru, colour: { few: '%{count} цвета', many: '%{count} цветов' }) + TranslationOverride.create!(locale: :ru, translation_key: 'colour.one', value: 'ONE') + TranslationOverride.create!(locale: :ru, translation_key: 'colour.few', value: 'FEW') + end + + let(:locale) { :ru } + let(:expected_translations) { { one: 'ONE', few: 'FEW', other: '%{count} colours' } } + let(:expected_overridden) { { one: true, few: true } } + + include_examples 'finds correct plural keys' + end + end end describe '#show' do @@ -110,6 +176,59 @@ RSpec.describe Admin::SiteTextsController do get "/admin/customize/site_texts/made_up_no_key_exists.json" expect(response.status).to eq(404) end + + context 'plural keys' do + before do + I18n.backend.store_translations(:en, colour: { one: '%{count} colour', other: '%{count} colours' }) + end + + 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" + expect(response.status).to eq(200) + + json = ::JSON.parse(response.body) + expect(json).to be_present + + site_text = json['site_text'] + expect(site_text).to be_present + + expect(site_text['id']).to eq(id) + expect(site_text['value']).to eq(value) + end + end + end + + context 'English' do + let(:locale) { :en } + let(:expected_translations) { { one: '%{count} colour', other: '%{count} colours' } } + + include_examples 'has correct plural keys' + end + + context 'language with different plural keys and missing translations' do + let(:locale) { :ru } + let(:expected_translations) { { one: '%{count} colour', few: '%{count} colours', other: '%{count} colours' } } + + include_examples 'has correct plural keys' + end + + context 'language with different plural keys and partial translation' do + before do + I18n.backend.store_translations(:ru, colour: { few: '%{count} цвета' }) + end + + let(:locale) { :ru } + let(:expected_translations) { { one: '%{count} colour', few: '%{count} цвета', other: '%{count} colours' } } + + include_examples 'has correct plural keys' + end + end end describe '#update & #revert' do