Skip to content

Commit 00a202f

Browse files
authored
FIX: 'Show original' button only shows in topics where there are translated content (#240)
Currently, the button above shows up even when there are no posts translated. With this commit, we will not show the button if there are zero posts in the topic that is translated. This commit also - only returns translations of supported languages (in SiteSetting.automatic_translation_target_languages) - optimises loading translations when the feature is enabled under certain circumstances
1 parent 9aba526 commit 00a202f

File tree

13 files changed

+352
-77
lines changed

13 files changed

+352
-77
lines changed

app/models/concerns/discourse_translator/translatable.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ def set_translation(locale, text)
3030

3131
def translation_for(locale)
3232
locale = locale.to_s.gsub("_", "-")
33-
translations.find_by(locale: locale)&.translation
33+
# this is a tricky perf balancing act when loading translations for a topic with many posts.
34+
# the topic_view_serializer includes(:translations) for posts in the topic,
35+
# the alternative is to do a find_by(locale: locale) which would result in a query per post.
36+
translations.to_a.find { |t| t.locale == locale }&.translation
3437
end
3538

3639
def detected_locale

app/models/discourse_translator/post_translation.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ class PostTranslation < ActiveRecord::Base
1010
validates :locale, presence: true
1111
validates :translation, presence: true
1212
validates :locale, uniqueness: { scope: :post_id }
13-
14-
def self.translation_for(post_id, locale)
15-
find_by(post_id: post_id, locale: locale)&.translation
16-
end
1713
end
1814
end
1915

app/models/discourse_translator/topic_translation.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ class TopicTranslation < ActiveRecord::Base
1010
validates :locale, presence: true
1111
validates :translation, presence: true
1212
validates :locale, uniqueness: { scope: :topic_id }
13-
14-
def self.translation_for(topic_id, locale)
15-
find_by(topic_id: topic_id, locale: locale)&.translation
16-
end
1713
end
1814
end
1915

assets/javascripts/discourse/components/show-original-content.gjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import DButton from "discourse/components/d-button";
66
import concatClass from "discourse/helpers/concat-class";
77

88
export default class ShowOriginalContent extends Component {
9+
static shouldRender(args) {
10+
return (
11+
args.topic.is_translated ||
12+
args.topic.postStream.posts.some(({ is_translated }) => is_translated)
13+
);
14+
}
15+
916
@service router;
1017
@tracked isTranslated = true;
1118

lib/discourse_translator/guardian_extension.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ def can_detect_language?(post)
2424
def can_translate?(post)
2525
return false if post.user&.bot?
2626
return false if !user_group_allow_translate?
27-
return false if post.locale_matches?(I18n.locale)
2827

2928
# we want to return false if the post is created within a short buffer ago,
3029
# this prevents the 🌐from appearing and then disappearing if the lang is same as user's lang
3130
return false if post.created_at > POST_DETECTION_BUFFER.ago && post.detected_locale.blank?
3231

3332
if SiteSetting.experimental_inline_translation
34-
post.translation_for(I18n.locale).nil?
33+
locale = DiscourseTranslator::InlineTranslation.effective_locale
34+
return false if post.locale_matches?(locale)
35+
post.translation_for(locale).nil?
3536
else
37+
return false if post.locale_matches?(I18n.locale)
3638
poster_group_allow_translate?(post)
3739
end
3840
end

lib/discourse_translator/inline_translation.rb

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,69 @@
22

33
module DiscourseTranslator
44
class InlineTranslation
5+
def self.effective_locale
6+
if LocaleMatcher.user_locale_is_default? || LocaleMatcher.user_locale_in_target_languages?
7+
I18n.locale
8+
else
9+
SiteSetting.default_locale
10+
end
11+
end
12+
513
def inject(plugin)
14+
# since locales are eager loaded but translations may not,
15+
# always return early if topic and posts are in the user's effective_locale.
16+
# this prevents the need to load translations.
17+
618
plugin.register_modifier(:basic_post_serializer_cooked) do |cooked, serializer|
719
if !SiteSetting.experimental_inline_translation ||
8-
serializer.scope&.request&.params&.[]("show") == "original" ||
9-
serializer.object.detected_locale == I18n.locale.to_s.gsub("_", "-")
20+
serializer.object.locale_matches?(InlineTranslation.effective_locale) ||
21+
serializer.scope&.request&.params&.[]("show") == "original"
1022
cooked
1123
else
12-
serializer.object.translation_for(I18n.locale).presence
24+
serializer.object.translation_for(InlineTranslation.effective_locale).presence
1325
end
1426
end
1527

1628
plugin.register_modifier(:topic_serializer_fancy_title) do |fancy_title, serializer|
1729
if !SiteSetting.experimental_inline_translation ||
18-
serializer.scope&.request&.params&.[]("show") == "original" ||
19-
serializer.object.locale_matches?(I18n.locale)
30+
serializer.object.locale_matches?(InlineTranslation.effective_locale) ||
31+
serializer.scope&.request&.params&.[]("show") == "original"
2032
fancy_title
2133
else
22-
serializer.object.translation_for(I18n.locale).presence&.then { |t| Topic.fancy_title(t) }
34+
serializer
35+
.object
36+
.translation_for(InlineTranslation.effective_locale)
37+
.presence
38+
&.then { |t| Topic.fancy_title(t) }
2339
end
2440
end
2541

2642
plugin.register_modifier(:topic_view_serializer_fancy_title) do |fancy_title, serializer|
2743
if !SiteSetting.experimental_inline_translation ||
28-
serializer.scope&.request&.params&.[]("show") == "original" ||
29-
serializer.object.topic.locale_matches?(I18n.locale)
44+
serializer.object.topic.locale_matches?(InlineTranslation.effective_locale) ||
45+
serializer.scope&.request&.params&.[]("show") == "original"
3046
fancy_title
3147
else
3248
serializer
3349
.object
3450
.topic
35-
.translation_for(I18n.locale)
51+
.translation_for(InlineTranslation.effective_locale)
3652
.presence
3753
&.then { |t| Topic.fancy_title(t) }
3854
end
3955
end
56+
57+
plugin.add_to_serializer(:basic_post, :is_translated) do
58+
SiteSetting.experimental_inline_translation &&
59+
!object.locale_matches?(InlineTranslation.effective_locale) &&
60+
object.translation_for(InlineTranslation.effective_locale).present?
61+
end
62+
63+
plugin.add_to_serializer(:topic_view, :is_translated) do
64+
SiteSetting.experimental_inline_translation &&
65+
!object.topic.locale_matches?(InlineTranslation.effective_locale) &&
66+
object.topic.translation_for(InlineTranslation.effective_locale).present?
67+
end
4068
end
4169
end
4270
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseTranslator
4+
class LocaleMatcher
5+
def self.user_locale_in_target_languages?
6+
# e.g. "en|es|pt_BR" vs :en_UK
7+
SiteSetting.automatic_translation_target_languages.split("|").include?(I18n.locale.to_s)
8+
end
9+
10+
def self.user_locale_is_default?
11+
# e.g. :en_UK vs "en_UK"
12+
I18n.locale.to_s == SiteSetting.default_locale
13+
end
14+
end
15+
end

lib/discourse_translator/topic_view_serializer_extension.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,20 @@ module DiscourseTranslator
44
module TopicViewSerializerExtension
55
def posts
66
if SiteSetting.translator_enabled?
7-
object.instance_variable_set(:@posts, object.posts.includes(:content_locale))
7+
posts_query = object.posts.includes(:content_locale)
8+
# this is kind of a micro-optimization.
9+
# we do not want to eager load translations if the user is using the site's language.
10+
# we will only load them if the user is using a different language that is supported by the site.
11+
if SiteSetting.experimental_inline_translation && !LocaleMatcher.user_locale_is_default? &&
12+
LocaleMatcher.user_locale_in_target_languages?
13+
locale = InlineTranslation.effective_locale.to_s.gsub("_", "-")
14+
posts_query =
15+
posts_query
16+
.includes(:translations)
17+
.references(:translations)
18+
.where(translations: { locale: [nil, locale] })
19+
end
20+
object.instance_variable_set(:@posts, posts_query)
821
end
922
super
1023
end

spec/lib/guardian_extension_spec.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@
177177
end
178178

179179
describe "when experimental_inline_translation enabled" do
180-
before { SiteSetting.experimental_inline_translation = true }
180+
before do
181+
SiteSetting.experimental_inline_translation = true
182+
183+
SiteSetting.automatic_translation_backfill_maximum_translations_per_hour = 1
184+
SiteSetting.automatic_translation_target_languages = "pt"
185+
end
181186

182187
describe "logged in user" do
183188
it "cannot translate when user is not in restrict_translation_by_group" do
@@ -190,7 +195,7 @@
190195
before { SiteSetting.restrict_translation_by_group = "#{group.id}" }
191196

192197
it "cannot translate when post has translation for user locale" do
193-
post.set_detected_locale("jp")
198+
post.set_detected_locale("ja")
194199
post.set_translation("pt", "Olá, mundo!")
195200

196201
expect(guardian.can_translate?(post)).to eq(false)

spec/serializers/basic_topic_serializer_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
topic.title = original_title
2121
SiteSetting.experimental_inline_translation = true
2222
I18n.locale = "ja"
23+
24+
SiteSetting.automatic_translation_backfill_maximum_translations_per_hour = 1
25+
SiteSetting.automatic_translation_target_languages = "ja"
2326
end
2427

2528
def serialize_topic(guardian_user: user, params: {})
@@ -53,6 +56,14 @@ def serialize_topic(guardian_user: user, params: {})
5356
expect(serialize_topic.fancy_title).to eq(topic.fancy_title)
5457
end
5558

59+
it "does not replace fancy_title when user's locale is not in target languages" do
60+
I18n.locale = "es"
61+
topic.set_detected_locale("en")
62+
topic.set_translation("es", jap_title)
63+
64+
expect(serialize_topic.fancy_title).to eq(topic.fancy_title)
65+
end
66+
5667
it "returns translated title in fancy_title when translation exists for current locale" do
5768
topic.set_translation("ja", jap_title)
5869
expect(serialize_topic.fancy_title).to eq("&lt;h1&gt;フス・ロ・ダ・ア&lt;/h1&gt;")

0 commit comments

Comments
 (0)