Skip to content

Commit 07eabc3

Browse files
committed
FIX: Allow admins to define locales that should be normalized
The criteria to allow a post to be translated is if the user's locale is different from the site's default locale. For each translator (e.g. Google, Amazon, Azure), we maintain a list of mappings `SUPPORTED_LANG_MAPPING` for each locale. On top of that, th is mapping is also used to define what language the post should be translated to. When using most translators available in the plugin, "en_GB" forums will translate text to "en" due to these mappings. This is not a problem for Discourse AI provider, since there are no restrictions on language. This PR introduces a site setting `normalize_language_variants_map` so that admins can properly indicate if A. A. They want `"pt" == "pt_BR" == "pt_PT"` or `"zh" == "zh_CN" == "zh_TW"` or B. They would prefer that `"en"` is not the same as `"en_GB"`.
1 parent a3294db commit 07eabc3

File tree

8 files changed

+102
-23
lines changed

8 files changed

+102
-23
lines changed

app/services/discourse_translator/base.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ def self.get_text(topic_or_post)
6363
end
6464
end
6565

66-
def self.language_supported?(detected_lang)
66+
def self.language_supported?(detected_lang, site_lang)
6767
raise NotImplementedError unless self.const_defined?(:SUPPORTED_LANG_MAPPING)
68+
site_lang_sym = site_lang.to_sym
6869
supported_lang = const_get(:SUPPORTED_LANG_MAPPING)
69-
return false if supported_lang[I18n.locale].nil?
70-
detected_lang != supported_lang[I18n.locale]
70+
return false if supported_lang[site_lang_sym].nil?
71+
detected_lang.to_sym != supported_lang[site_lang_sym].to_sym
7172
end
7273

7374
private

app/services/discourse_translator/discourse_ai.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
module DiscourseTranslator
77
class DiscourseAi < Base
88
MAX_DETECT_LOCALE_TEXT_LENGTH = 1000
9-
def self.language_supported?(_)
9+
def self.language_supported?(_, _)
1010
true
1111
end
1212

config/settings.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ discourse_translator:
9494
client: true
9595
max_translations_per_minute:
9696
default: 3
97+
normalize_language_variants:
98+
type: list
99+
list_type: compact
100+
default: "en"
101+
client: true
97102
restrict_translation_by_group:
98103
default: "11" # default group trust_level_1
99104
client: true

lib/discourse_translator/guardian_extension.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,28 @@ def can_detect_language?(post)
1818
post&.user&.in_any_groups?(SiteSetting.restrict_translation_by_poster_group_map)
1919
) && post.raw.present? && post.post_type != Post.types[:small_action]
2020
end
21+
22+
def can_translate?(post)
23+
return false if !user_group_allow_translate?
24+
25+
# we will deal with strings (not syms) when comparing locales below
26+
detected_lang =
27+
post.custom_fields[::DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD].to_s.sub("-", "_")
28+
return false if detected_lang.blank?
29+
30+
locale_without_region = I18n.locale.to_s.split("_").first
31+
site_locale =
32+
(
33+
if SiteSetting.normalize_language_variants_map.include?(locale_without_region)
34+
locale_without_region
35+
else
36+
I18n.locale.to_s
37+
end
38+
)
39+
detected_lang != site_locale &&
40+
"DiscourseTranslator::#{SiteSetting.translator}".constantize.language_supported?(
41+
detected_lang,
42+
site_locale,
43+
)
44+
end
2145
end

plugin.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,6 @@ module ::DiscourseTranslator
4444
end
4545

4646
add_to_serializer :post, :can_translate do
47-
return false if !scope.user_group_allow_translate?
48-
49-
detected_lang = post_custom_fields[::DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD]
50-
return false if detected_lang.blank?
51-
52-
detected_lang.to_sym != I18n.locale &&
53-
"DiscourseTranslator::#{SiteSetting.translator}".constantize.language_supported?(
54-
detected_lang,
55-
)
47+
scope.can_translate?(object)
5648
end
5749
end

spec/lib/guardian_extension_spec.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,64 @@
113113
end
114114
end
115115
end
116+
117+
describe "#can_translate?" do
118+
fab!(:group)
119+
fab!(:user) { Fabricate(:user, locale: "en", groups: [group]) }
120+
fab!(:post)
121+
122+
let(:guardian) { Guardian.new(user) }
123+
124+
it "returns false when translator disabled" do
125+
SiteSetting.translator_enabled = false
126+
127+
expect(guardian.can_translate?(post)).to eq(false)
128+
end
129+
130+
describe "when translator enabled" do
131+
before { SiteSetting.translator_enabled = true }
132+
133+
describe "anon user" do
134+
before { SiteSetting.restrict_translation_by_group = "#{Group::AUTO_GROUPS[:everyone]}" }
135+
136+
it "cannot translate" do
137+
expect(Guardian.new.can_translate?(post)).to eq(false)
138+
end
139+
end
140+
141+
describe "logged in user" do
142+
it "cannot translate when user is not in restrict_translation_by_group" do
143+
SiteSetting.restrict_translation_by_group = "#{group.id + 1}"
144+
145+
expect(guardian.can_translate?(post)).to eq(false)
146+
end
147+
148+
describe "user is in restrict_translation_by_group" do
149+
before { SiteSetting.restrict_translation_by_group = "#{group.id}" }
150+
151+
describe "locale is :xx" do
152+
before { I18n.stubs(:locale).returns(:pt) }
153+
154+
it "cannot translate when post does not have DETECTED_LANG_CUSTOM_FIELD" do
155+
expect(guardian.can_translate?(post)).to eq(false)
156+
end
157+
158+
it "cannot translate when post has DETECTED_LANG_CUSTOM_FIELD matches locale" do
159+
post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] = "pt"
160+
post.save
161+
162+
expect(guardian.can_translate?(post)).to eq(false)
163+
end
164+
165+
it "can translate when post has DETECTED_LANG_CUSTOM_FIELD does not match locale" do
166+
post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] = "jp"
167+
post.save
168+
169+
expect(guardian.can_translate?(post)).to eq(true)
170+
end
171+
end
172+
end
173+
end
174+
end
175+
end
116176
end

spec/services/base_spec.rb

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,21 @@ class EmptyTranslator < DiscourseTranslator::Base
1212

1313
describe ".language_supported?" do
1414
it "raises an error when the method is not implemented" do
15-
expect { EmptyTranslator.language_supported?("en") }.to raise_error(NotImplementedError)
15+
expect { EmptyTranslator.language_supported?("en", "en") }.to raise_error(NotImplementedError)
1616
end
1717

1818
it "returns false when the locale is not supported" do
19-
I18n.stubs(:locale).returns(:xx)
20-
expect(TestTranslator.language_supported?("en")).to eq(false)
19+
expect(TestTranslator.language_supported?("en", "xx")).to eq(false)
2120
end
2221

2322
it "returns true when the detected language is not the current locale" do
24-
I18n.locale = :pt
25-
expect(TestTranslator.language_supported?("en")).to eq(true)
26-
expect(TestTranslator.language_supported?("ar")).to eq(true)
27-
expect(TestTranslator.language_supported?("es-MX")).to eq(true)
23+
expect(TestTranslator.language_supported?("en", "pt")).to eq(true)
24+
expect(TestTranslator.language_supported?("ar", "pt")).to eq(true)
25+
expect(TestTranslator.language_supported?("es-MX", "pt")).to eq(true)
2826
end
2927

3028
it "returns false when the detected language is the detected locale" do
31-
I18n.locale = :pt
32-
expect(TestTranslator.language_supported?("pt")).to eq(false)
29+
expect(TestTranslator.language_supported?("pt", "pt")).to eq(false)
3330
end
3431
end
3532

spec/services/discourse_ai_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
describe ".language_supported?" do
1818
it "returns true for any language" do
19-
expect(described_class.language_supported?("any-language")).to eq(true)
19+
expect(described_class.language_supported?("any-language", "??")).to eq(true)
2020
end
2121
end
2222

0 commit comments

Comments
 (0)