Skip to content

Commit 7a3064c

Browse files
committed
DEV: Add the ability to translate a single piece of text
1 parent f4724ab commit 7a3064c

File tree

11 files changed

+149
-40
lines changed

11 files changed

+149
-40
lines changed

app/services/discourse_translator/provider/amazon.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
140140
end
141141
end
142142

143+
def self.translate_text!(text, target_locale_sym = I18n.locale)
144+
begin
145+
client.translate_text(
146+
{
147+
text: truncate(text),
148+
source_language_code: "auto",
149+
target_language_code: SUPPORTED_LANG_MAPPING[target_locale_sym],
150+
},
151+
)
152+
rescue Aws::Translate::Errors::UnsupportedLanguagePairException
153+
raise I18n.t("translator.not_supported")
154+
end
155+
end
156+
143157
def self.client
144158
opts = { region: SiteSetting.translator_aws_region }
145159

app/services/discourse_translator/provider/base_provider.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
6565
raise "Not Implemented"
6666
end
6767

68+
def self.translate_text(text, target_locale_sym = I18n.locale)
69+
raise "Not Implemented"
70+
end
71+
6872
# Returns the stored detected locale of a post or topic.
6973
# If the locale does not exist yet, it will be detected first via the API then stored.
7074
# @param translatable [Post|Topic]

app/services/discourse_translator/provider/discourse_ai.rb

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,13 @@ def self.language_supported?(detected_lang)
1010
end
1111

1212
def self.detect!(topic_or_post)
13-
unless required_settings_enabled
14-
raise TranslatorError.new(
15-
I18n.t(
16-
"translator.discourse_ai.ai_helper_required",
17-
{ base_url: Discourse.base_url },
18-
),
19-
)
20-
end
13+
required_settings_enabled!
2114

2215
::DiscourseAi::LanguageDetector.new(text_for_detection(topic_or_post)).detect
2316
end
2417

2518
def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
26-
unless required_settings_enabled
27-
raise TranslatorError.new(
28-
I18n.t(
29-
"translator.discourse_ai.ai_helper_required",
30-
{ base_url: Discourse.base_url },
31-
),
32-
)
33-
end
19+
required_settings_enabled!
3420

3521
language = get_language_name(target_locale_sym)
3622
translated =
@@ -51,11 +37,25 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
5137
DiscourseTranslator::TranslatedContentNormalizer.normalize(translatable, translated)
5238
end
5339

40+
def self.translate_text!(text, target_locale_sym = I18n.locale)
41+
required_settings_enabled!
42+
43+
language = get_language_name(target_locale_sym)
44+
::DiscourseAi::ShortTextTranslator.new(text, language).translate
45+
end
46+
5447
private
5548

56-
def self.required_settings_enabled
57-
SiteSetting.translator_enabled && SiteSetting.translator_provider == "DiscourseAi" &&
58-
SiteSetting.discourse_ai_enabled && SiteSetting.ai_helper_enabled
49+
def self.required_settings_enabled!
50+
unless SiteSetting.translator_enabled && SiteSetting.translator_provider == "DiscourseAi" &&
51+
SiteSetting.discourse_ai_enabled && SiteSetting.ai_helper_enabled
52+
raise TranslatorError.new(
53+
I18n.t(
54+
"translator.discourse_ai.ai_helper_required",
55+
{ base_url: Discourse.base_url },
56+
),
57+
)
58+
end
5959
end
6060

6161
def self.get_language_name(target_locale_sym)

app/services/discourse_translator/provider/google.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
106106
res["translations"][0]["translatedText"]
107107
end
108108

109+
def self.translate_text!(text, target_locale_sym = I18n.locale)
110+
res = result(TRANSLATE_URI, q: text, target: SUPPORTED_LANG_MAPPING[target_locale_sym])
111+
res["translations"][0]["translatedText"]
112+
end
113+
109114
def self.result(url, body)
110115
body[:key] = access_token
111116

app/services/discourse_translator/provider/libre_translate.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
105105
res["translatedText"]
106106
end
107107

108+
def self.translate_text!(text, target_locale_sym = I18n.locale)
109+
# Unsupported - see https://libretranslate.com/docs/#/translate/post_translate
110+
# requires a source language
111+
raise TranslatorError.new(I18n.t("translator.not_supported"))
112+
end
113+
108114
def self.get(url)
109115
begin
110116
response = Excon.get(url)

app/services/discourse_translator/provider/microsoft.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
171171
response_body.first["translations"].first["text"]
172172
end
173173

174+
def self.translate_text!(text, target_locale_sym = I18n.locale)
175+
locale =
176+
SUPPORTED_LANG_MAPPING[target_locale_sym] || (raise I18n.t("translator.not_supported"))
177+
178+
query = default_query.merge("to" => locale, "textType" => "html")
179+
body = [{ "Text" => text }].to_json
180+
uri = URI(translate_endpoint)
181+
uri.query = URI.encode_www_form(query)
182+
response_body = result(uri.to_s, body, default_headers)
183+
response_body.first["translations"].first["text"]
184+
end
185+
174186
def self.translate_supported?(detected_lang, target_lang)
175187
SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) &&
176188
SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s)

app/services/discourse_translator/provider/yandex.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ def self.translate_translatable!(translatable, target_locale_sym = I18n.locale)
148148
response_body["text"][0]
149149
end
150150

151+
def self.translate_text!(translatable, target_locale_sym = I18n.locale)
152+
# Not supported for v1.5 https://translate.yandex.com/developers
153+
raise TranslatorError.new(I18n.t("translator.not_supported"))
154+
end
155+
151156
def self.translate_supported?(detected_lang, target_lang)
152157
SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) &&
153158
SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s)

spec/services/amazon_spec.rb

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
end
4747
end
4848

49-
describe ".translate" do
49+
describe ".translate_translatable!" do
5050
let(:post) { Fabricate(:post) }
5151
let!(:client) { Aws::Translate::Client.new(stub_responses: true) }
5252

@@ -66,9 +66,33 @@
6666
end
6767

6868
it "raises an error when trying to translate an unsupported language" do
69-
expect { described_class.translate(post) }.to raise_error(
69+
expect { described_class.translate_translatable!(post) }.to raise_error(
7070
I18n.t("translator.failed.post", source_locale: "en", target_locale: "es"),
7171
)
7272
end
7373
end
74+
75+
describe ".translate_text!" do
76+
let!(:client) { Aws::Translate::Client.new(stub_responses: true) }
77+
78+
before do
79+
client.stub_responses(
80+
:translate_text,
81+
"UnsupportedLanguagePairException",
82+
{
83+
translated_text: "Probando traducciones",
84+
source_language_code: "en",
85+
target_language_code: "es",
86+
},
87+
)
88+
described_class.stubs(:client).returns(client)
89+
I18n.stubs(:locale).returns(:es)
90+
end
91+
92+
it "raises an error when trying to translate an unsupported language" do
93+
expect { described_class.translate_text!("derp") }.to raise_error(
94+
I18n.t("translator.not_supported", source_locale: "en", target_locale: "es"),
95+
)
96+
end
97+
end
7498
end

spec/services/discourse_ai_spec.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
end
3535
end
3636

37-
describe ".translate" do
37+
describe ".translate_translatable!" do
3838
before do
3939
post.set_detected_locale("de")
4040
topic.set_detected_locale("de")
@@ -44,8 +44,7 @@
4444
DiscourseAi::Completions::Llm.with_prepared_responses(
4545
[translation_json("some translated text")],
4646
) do
47-
locale, translated_text = DiscourseTranslator::Provider::DiscourseAi.translate(post)
48-
expect(locale).to eq "de"
47+
translated_text = DiscourseTranslator::Provider::DiscourseAi.translate_translatable!(post)
4948
expect(translated_text).to eq "<p>some translated text</p>"
5049
end
5150
end
@@ -55,8 +54,7 @@
5554
DiscourseAi::Completions::Llm.with_prepared_responses(
5655
[translation_json("some translated text")],
5756
) do
58-
locale, translated_text = DiscourseTranslator::Provider::DiscourseAi.translate(topic)
59-
expect(locale).to eq "de"
57+
translated_text = DiscourseTranslator::Provider::DiscourseAi.translate_translatable!(topic)
6058
expect(translated_text).to eq "some translated text"
6159
end
6260
end
@@ -73,6 +71,17 @@
7371
end
7472
end
7573

74+
describe ".translate_text!" do
75+
it "returns the translated text" do
76+
DiscourseAi::Completions::Llm.with_prepared_responses(
77+
[translation_json("some translated text")],
78+
) do
79+
translated_text = DiscourseTranslator::Provider::DiscourseAi.translate_text!("derp")
80+
expect(translated_text).to eq "some translated text"
81+
end
82+
end
83+
end
84+
7685
def locale_json(content)
7786
{ locale: content }.to_json
7887
end

spec/services/google_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,25 @@
187187
expect(described_class.translate_translatable!(post)).to eq(translated_text)
188188
end
189189
end
190+
191+
describe ".translate_text!" do
192+
it "translates plain text" do
193+
text = "ABCDEFG"
194+
body = { q: text, target: "ja", key: api_key }
195+
196+
translated_text = "hur dur hur dur"
197+
stub_request(:post, DiscourseTranslator::Provider::Google::TRANSLATE_URI).with(
198+
body: URI.encode_www_form(body),
199+
headers: {
200+
"Content-Type" => "application/x-www-form-urlencoded",
201+
"Referer" => "http://test.localhost",
202+
},
203+
).to_return(
204+
status: 200,
205+
body: %{ { "data": { "translations": [ { "translatedText": "#{translated_text}" } ] } } },
206+
)
207+
208+
expect(described_class.translate_text!(text, :ja)).to eq(translated_text)
209+
end
210+
end
190211
end

0 commit comments

Comments
 (0)