mirror of
https://github.com/discourse/discourse.git
synced 2026-08-05 18:57:20 -05:00
FEATURE: Serve localized content in the site's default locale when user's language is unsupported (#36160)
The content localization feature we have (e.g. on meta) currently assumes that everyone has set their user preferences language to a language that we support. If Eleni sets Greek as her Interface language, it is a non-supported language. Posts written in Chinese or French will remain in Chinese or French, as there is no Greek translation. The commit shows Eleni localized posts in English (i.e. site default locale) when a post is written in a different language. A site setting `content_localization_use_default_locale_when_unsupported` is default true and introduced.
This commit is contained in:
@@ -5,9 +5,13 @@ module Localizable
|
||||
|
||||
included { has_many :localizations, class_name: "#{model_name}Localization", dependent: :destroy }
|
||||
|
||||
# Returns the localization for the given locale, or the best match if an exact match is not found.
|
||||
# Returns the localization for (in order of priority):
|
||||
# - the given locale,
|
||||
# - or the best match if an exact match is not found
|
||||
# - or the site default locale if `content_localization_use_default_locale_when_unsupported` enabled
|
||||
#
|
||||
# The query used to find the localization is optimized for performance, and assumes
|
||||
# that localizations are indexed by locale, and have been preloaded where necessary.
|
||||
# that localizations are indexed by locale, and have been preloaded.
|
||||
# @return [Localization, nil] the localization object for the given locale, or nil if no match is found.
|
||||
def get_localization(locale = I18n.locale)
|
||||
locale_str = locale.to_s.sub("-", "_")
|
||||
@@ -17,7 +21,14 @@ module Localizable
|
||||
return match
|
||||
end
|
||||
|
||||
localizations.find { |l| LocaleNormalizer.is_same?(l.locale, locale_str) }
|
||||
if match = localizations.find { |l| LocaleNormalizer.is_same?(l.locale, locale_str) }
|
||||
return match
|
||||
end
|
||||
|
||||
if SiteSetting.content_localization_use_default_locale_when_unsupported
|
||||
default_locale = SiteSetting.default_locale.to_s.sub("-", "_")
|
||||
localizations.find { |l| LocaleNormalizer.is_same?(l.locale, default_locale) }
|
||||
end
|
||||
end
|
||||
|
||||
def in_user_locale?
|
||||
|
||||
@@ -2773,6 +2773,7 @@ en:
|
||||
content_localization_allowed_groups: "Groups allowed to update localized content. Requires 'content localization enabled'."
|
||||
content_localization_language_switcher: "Show a language switcher in the header, allowing visitors to switch between translated versions of Discourse and user-contributed content. Uses languages defined in 'content localization supported locales'"
|
||||
content_localization_crawler_param: "Serve localized content to web crawlers when 'content localization enabled' and 'set locale from param' is enabled. The list of supported locales is defined in 'content localization supported locales'."
|
||||
content_localization_use_default_locale_when_unsupported: "Serve localized content in the site's default locale to users whose preferred language is not listed in 'content localization supported locales'."
|
||||
enable_upcoming_changes: "Enable upcoming changes"
|
||||
fake_upcoming_change: "This is a fake upcoming change for testing purposes. No need to translate this string."
|
||||
|
||||
|
||||
@@ -1745,6 +1745,9 @@ content_localization:
|
||||
content_localization_crawler_param:
|
||||
default: false
|
||||
area: "localization"
|
||||
content_localization_use_default_locale_when_unsupported:
|
||||
default: true
|
||||
area: "localization"
|
||||
|
||||
email:
|
||||
email_time_window_mins:
|
||||
|
||||
@@ -432,7 +432,7 @@ RSpec.describe Tag do
|
||||
end
|
||||
|
||||
it "returns nil when no localization exists" do
|
||||
Fabricate(:tag_localization, tag:, locale: "en")
|
||||
Fabricate(:tag_localization, tag:, locale: "el")
|
||||
|
||||
expect(tag.get_localization("es")).to be_nil
|
||||
end
|
||||
|
||||
@@ -22,13 +22,27 @@ RSpec.describe BasicPostSerializer do
|
||||
expect(json[:cooked]).to eq(post.cooked)
|
||||
end
|
||||
|
||||
it "returns the localized cooked" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
Fabricate(:post_localization, post: post, cooked: "X", locale: "ja")
|
||||
I18n.locale = "ja"
|
||||
post.update!(locale: "en")
|
||||
describe "localizations" do
|
||||
it "returns the localized cooked" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
Fabricate(:post_localization, post: post, cooked: "X", locale: "ja")
|
||||
I18n.locale = "ja"
|
||||
post.update!(locale: "en")
|
||||
|
||||
expect(json[:cooked]).to eq("X")
|
||||
expect(json[:cooked]).to eq("X")
|
||||
end
|
||||
|
||||
it "returns the site default locale cooked when no exact match found and `content_localization_use_default_locale_when_unsupported` is true" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
SiteSetting.content_localization_use_default_locale_when_unsupported = true
|
||||
SiteSetting.default_locale = "el"
|
||||
|
||||
Fabricate(:post_localization, post:, cooked: "site default cooked", locale: "el")
|
||||
I18n.locale = "ja"
|
||||
post.update!(locale: "en")
|
||||
|
||||
expect(json[:cooked]).to eq("site default cooked")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,15 +10,36 @@ describe BasicTopicSerializer do
|
||||
expect(json[:basic_topic][:fancy_title]).to eq(topic.title)
|
||||
end
|
||||
|
||||
it "returns the fancy title with a modifier" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
Fabricate(:topic_localization, topic:, fancy_title: "X", locale: "ja")
|
||||
I18n.locale = "ja"
|
||||
topic.update!(locale: "en")
|
||||
describe "localizations" do
|
||||
it "returns the fancy title with a modifier" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
Fabricate(:topic_localization, topic:, fancy_title: "X", locale: "ja")
|
||||
I18n.locale = "ja"
|
||||
topic.update!(locale: "en")
|
||||
|
||||
json = BasicTopicSerializer.new(topic).as_json
|
||||
json = BasicTopicSerializer.new(topic).as_json
|
||||
|
||||
expect(json[:basic_topic][:fancy_title]).to eq("X")
|
||||
expect(json[:basic_topic][:fancy_title]).to eq("X")
|
||||
end
|
||||
|
||||
it "returns the site default locale fancy title when no exact match found and `content_localization_use_default_locale_when_unsupported` is true" do
|
||||
SiteSetting.content_localization_enabled = true
|
||||
SiteSetting.content_localization_use_default_locale_when_unsupported = true
|
||||
SiteSetting.default_locale = "el"
|
||||
|
||||
Fabricate(
|
||||
:topic_localization,
|
||||
topic:,
|
||||
fancy_title: "site default fancy title",
|
||||
locale: "el",
|
||||
)
|
||||
I18n.locale = "ja"
|
||||
topic.update!(locale: "en")
|
||||
|
||||
json = BasicTopicSerializer.new(topic).as_json
|
||||
|
||||
expect(json[:basic_topic][:fancy_title]).to eq("site default fancy title")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,11 @@ describe "Content Localization" do
|
||||
)
|
||||
end
|
||||
fab!(:post_3) { Fabricate(:post, topic:, locale: "ja", raw: "将とは、智・信・仁・勇・厳なり。") }
|
||||
fab!(:topic_ja_localization) do
|
||||
Fabricate(:topic_localization, topic:, locale: "ja", fancy_title: "孫子兵法からの人生戦略")
|
||||
end
|
||||
|
||||
# page objects
|
||||
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||
let(:topic_list) { PageObjects::Components::TopicList.new }
|
||||
let(:composer) { PageObjects::Components::Composer.new }
|
||||
@@ -38,10 +42,6 @@ describe "Content Localization" do
|
||||
let(:post_3_obj) { PageObjects::Components::Post.new(3) }
|
||||
let(:post_4_obj) { PageObjects::Components::Post.new(4) }
|
||||
|
||||
fab!(:topic_ja_localization) do
|
||||
Fabricate(:topic_localization, topic:, locale: "ja", fancy_title: "孫子兵法からの人生戦略")
|
||||
end
|
||||
|
||||
def scroll_to_post(post_number)
|
||||
5.times do
|
||||
break if page.has_css?("#post_#{post_number} .cooked", visible: :all, wait: 0)
|
||||
@@ -371,6 +371,82 @@ describe "Content Localization" do
|
||||
expect(page).to have_title(shady_topic.title)
|
||||
end
|
||||
end
|
||||
|
||||
context "for a Greek user in an English forum with Japanese users" do
|
||||
fab!(:greek_user) { Fabricate(:user, locale: "el") }
|
||||
|
||||
fab!(:jap_post) { Fabricate(:post, locale: "ja", cooked: "皆さんは「ジョジョの奇妙な冒険」をご存知ですか?") }
|
||||
fab!(:jap_topic) do
|
||||
jap_post.topic.tap { |t| t.update(locale: "ja", fancy_title: "ジョジョの奇妙な冒険") }
|
||||
end
|
||||
fab!(:en_loc_jap_post) do
|
||||
Fabricate(
|
||||
:post_localization,
|
||||
locale: "en",
|
||||
post: jap_post,
|
||||
cooked: "Do you know “JoJo’s Bizarre Adventure”?",
|
||||
)
|
||||
end
|
||||
fab!(:en_loc_jap_topic) do
|
||||
Fabricate(
|
||||
:topic_localization,
|
||||
locale: "en",
|
||||
topic: jap_topic,
|
||||
fancy_title: "JoJo's Bizarre Adventure",
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
SiteSetting.default_locale = "en" # explicit
|
||||
SiteSetting.content_localization_use_default_locale_when_unsupported = true
|
||||
end
|
||||
|
||||
context "for a topic / post with no locale" do
|
||||
it "shows content as-is" do
|
||||
jap_post.update(locale: nil)
|
||||
jap_topic.update(locale: nil)
|
||||
|
||||
sign_in(greek_user)
|
||||
|
||||
topic_page.visit_topic(jap_topic)
|
||||
expect(topic_page).to have_topic_title(jap_topic.fancy_title)
|
||||
expect(post_1_obj).to have_cooked_content(jap_post.cooked)
|
||||
|
||||
SiteSetting.content_localization_enabled = false
|
||||
|
||||
page.refresh
|
||||
expect(topic_page).to have_topic_title(jap_topic.fancy_title)
|
||||
expect(post_1_obj).to have_cooked_content(jap_post.cooked)
|
||||
end
|
||||
end
|
||||
|
||||
context "for a topic / post written in Site default language (en)" do
|
||||
it "shows Site default language (en) translation to Greek user" do
|
||||
sign_in(greek_user)
|
||||
|
||||
topic_page.visit_topic(jap_topic)
|
||||
expect(topic_page).to have_topic_title(en_loc_jap_topic.fancy_title)
|
||||
expect(post_1_obj).to have_cooked_content(en_loc_jap_post.cooked)
|
||||
|
||||
SiteSetting.content_localization_use_default_locale_when_unsupported = false
|
||||
|
||||
page.refresh
|
||||
expect(topic_page).to have_topic_title(jap_topic.fancy_title)
|
||||
expect(post_1_obj).to have_cooked_content(jap_post.cooked)
|
||||
end
|
||||
end
|
||||
|
||||
it "shows content as-is when no localization exists" do
|
||||
en_loc_jap_topic.destroy
|
||||
en_loc_jap_post.destroy
|
||||
|
||||
sign_in(greek_user)
|
||||
|
||||
topic_page.visit_topic(jap_topic)
|
||||
expect(topic_page).to have_topic_title(jap_topic.fancy_title)
|
||||
expect(post_1_obj).to have_cooked_content(jap_post.cooked)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "for site settings" do
|
||||
|
||||
@@ -28,8 +28,12 @@ module PageObjects
|
||||
find("#embedded-posts__bottom--#{@post_number} .load-more-replies").click
|
||||
end
|
||||
|
||||
def cooked_content
|
||||
post.find(".contents > .cooked")
|
||||
end
|
||||
|
||||
def has_cooked_content?(value)
|
||||
post.find(".contents > .cooked").has_content?(value)
|
||||
cooked_content.has_content?(value)
|
||||
end
|
||||
|
||||
def has_replies?(count: nil)
|
||||
|
||||
Reference in New Issue
Block a user