From 4c5d8e74dea4ff006706772a356f1c2efa1445fa Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 01:56:55 +0800 Subject: [PATCH 1/6] DEV: Move saving of translations into base class --- app/jobs/scheduled/detect_posts_language.rb | 4 -- app/services/discourse_translator/amazon.rb | 32 ++++------ app/services/discourse_translator/base.rb | 60 ++++++++++++++++++- .../discourse_translator/discourse_ai.rb | 10 +--- app/services/discourse_translator/google.rb | 59 ++++++------------ .../discourse_translator/libre_translate.rb | 50 ++++++---------- .../discourse_translator/microsoft.rb | 47 ++++++--------- app/services/discourse_translator/yandex.rb | 53 +++++++--------- spec/services/microsoft_spec.rb | 5 +- spec/services/yandex_spec.rb | 2 +- 10 files changed, 153 insertions(+), 169 deletions(-) diff --git a/app/jobs/scheduled/detect_posts_language.rb b/app/jobs/scheduled/detect_posts_language.rb index 0fa896d4..04a7f7c6 100644 --- a/app/jobs/scheduled/detect_posts_language.rb +++ b/app/jobs/scheduled/detect_posts_language.rb @@ -26,10 +26,6 @@ def process_batch(post_ids) begin translator = "DiscourseTranslator::#{SiteSetting.translator}".constantize translator.detect(post) - if !post.custom_fields_clean? - post.save_custom_fields - post.publish_change_to_clients!(:revised) - end rescue ::DiscourseTranslator::ProblemCheckedTranslationError # problem-checked translation errors gracefully end diff --git a/app/services/discourse_translator/amazon.rb b/app/services/discourse_translator/amazon.rb index c5752486..2c0f4eff 100644 --- a/app/services/discourse_translator/amazon.rb +++ b/app/services/discourse_translator/amazon.rb @@ -107,15 +107,12 @@ def self.access_token_key "aws-translator" end - def self.detect(topic_or_post) - text = truncate text_for_detection(topic_or_post) - return if text.blank? - - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= ( + def self.detect!(topic_or_post) + save_detected_locale(topic_or_post) do begin client.translate_text( { - text: text, + text: truncate(text_for_detection(topic_or_post)), source_language_code: "auto", target_language_code: SUPPORTED_LANG_MAPPING[I18n.locale], }, @@ -123,22 +120,21 @@ def self.detect(topic_or_post) rescue Aws::Errors::MissingCredentialsError raise I18n.t("translator.amazon.invalid_credentials") end - ) + end end - def self.translate(topic_or_post) + def self.translate!(topic_or_post) detected_lang = detect(topic_or_post) - from_custom_fields(topic_or_post) do + save_translation(topic_or_post) do begin - result = - client.translate_text( - { - text: truncate(text_for_translation(topic_or_post)), - source_language_code: "auto", - target_language_code: SUPPORTED_LANG_MAPPING[I18n.locale], - }, - ) + client.translate_text( + { + text: truncate(text_for_translation(topic_or_post)), + source_language_code: "auto", + target_language_code: SUPPORTED_LANG_MAPPING[I18n.locale], + }, + ) rescue Aws::Translate::Errors::UnsupportedLanguagePairException raise I18n.t( "translator.failed", @@ -146,8 +142,6 @@ def self.translate(topic_or_post) target_locale: I18n.locale, ) end - - [detected_lang, result.translated_text] end end diff --git a/app/services/discourse_translator/base.rb b/app/services/discourse_translator/base.rb index ac78e99e..f75dd1a9 100644 --- a/app/services/discourse_translator/base.rb +++ b/app/services/discourse_translator/base.rb @@ -24,11 +24,39 @@ def self.cache_key "#{key_prefix}#{access_token_key}" end - def self.translate(post) + def self.translate(topic_or_post) + return if text_for_translation(topic_or_post).blank? + detected_lang = detect(topic_or_post) + + return detected_lang, get_text(topic_or_post) if (detected_lang&.to_s.eql? I18n.locale.to_s) + unless translate_supported?(detected_lang, I18n.locale) + raise TranslatorError.new( + I18n.t( + "translator.failed", + source_locale: detected_lang, + target_locale: I18n.locale, + ), + ) + end + + translated_text = translate!(topic_or_post) + + [detected_lang, translated_text] + end + + def self.translate!(post) raise "Not Implemented" end - def self.detect(post) + # Returns the stored detected locale of a post or topic. + # If the locale does not exist yet, it will be detected first via the API then stored. + # @param topic_or_post [Post|Topic] + def self.detect(topic_or_post) + return if text_for_detection(topic_or_post).blank? + get_detected_locale(topic_or_post) || detect!(topic_or_post) + end + + def self.detect!(post) raise "Not Implemented" end @@ -36,7 +64,13 @@ def self.access_token raise "Not Implemented" end - def self.from_custom_fields(topic_or_post) + def self.get_translation(topic_or_post) + translated_custom_field = + topic_or_post.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] || {} + translated_custom_field[I18n.locale] + end + + def self.save_translation(topic_or_post) translated_custom_field = topic_or_post.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] || {} translated_text = translated_custom_field[I18n.locale] @@ -54,6 +88,22 @@ def self.from_custom_fields(topic_or_post) translated_text end + def self.get_detected_locale(topic_or_post) + topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] + end + + def self.save_detected_locale(topic_or_post) + detected_locale = yield + topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] = detected_locale + + if !topic_or_post.custom_fields_clean? + topic_or_post.save_custom_fields + topic_or_post.publish_change_to_clients!(:revised) if topic_or_post.class.name == "Post" + end + + detected_locale + end + def self.get_text(topic_or_post) case topic_or_post.class.name when "Post" @@ -70,6 +120,10 @@ def self.language_supported?(detected_lang) detected_lang != supported_lang[I18n.locale] end + def self.translate_supported?(detected_lang, target_lang) + true + end + private def self.strip_tags_for_detection(detection_text) diff --git a/app/services/discourse_translator/discourse_ai.rb b/app/services/discourse_translator/discourse_ai.rb index 47426f3d..8537accd 100644 --- a/app/services/discourse_translator/discourse_ai.rb +++ b/app/services/discourse_translator/discourse_ai.rb @@ -11,16 +11,12 @@ def self.language_supported?(detected_lang) detected_lang != locale_without_region end - def self.detect(topic_or_post) + def self.detect!(topic_or_post) return unless required_settings_enabled - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= begin + save_detected_locale(topic_or_post) do ::DiscourseAi::LanguageDetector.new(text_for_detection(topic_or_post)).detect end - rescue => e - Rails.logger.warn( - "#{::DiscourseTranslator::PLUGIN_NAME}: Failed to detect language for #{topic_or_post.class.name} #{topic_or_post.id}: #{e}", - ) end def self.translate(topic_or_post) @@ -28,7 +24,7 @@ def self.translate(topic_or_post) detected_lang = detect(topic_or_post) translated_text = - from_custom_fields(topic_or_post) do + save_translation(topic_or_post) do ::DiscourseAi::Translator.new(text_for_translation(topic_or_post), I18n.locale).translate end diff --git a/app/services/discourse_translator/google.rb b/app/services/discourse_translator/google.rb index 753affa4..55e65a1d 100644 --- a/app/services/discourse_translator/google.rb +++ b/app/services/discourse_translator/google.rb @@ -74,17 +74,14 @@ def self.access_token raise ProblemCheckedTranslationError.new("NotFound: Google Api Key not set.") end - def self.detect(topic_or_post) - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= result( - DETECT_URI, - q: text_for_detection(topic_or_post), - )[ - "detections" - ][ - 0 - ].max { |a, b| a.confidence <=> b.confidence }[ - "language" - ] + def self.detect!(topic_or_post) + save_detected_locale(topic_or_post) do + result(DETECT_URI, q: text_for_detection(topic_or_post))["detections"][0].max do |a, b| + a.confidence <=> b.confidence + end[ + "language" + ] + end end def self.translate_supported?(source, target) @@ -92,36 +89,18 @@ def self.translate_supported?(source, target) res["languages"].any? { |obj| obj["language"] == source } end - def self.translate(topic_or_post) - detected_lang = detect(topic_or_post) - - # the translate button appears if a given post is in a foreign language. - # however the title of the topic may be in a different language, and may be in the user's language. - # if this is the case, when this is called for a topic, the detected_lang will be the user's language, - # so the user's language and the detected language will be the same. For example, both could be "en" - # google will choke on this and return an error instead of gracefully handling it by returning the original - # string. - # --- - # here we handle that situation by returning the original string if the source and target lang are the same. - return detected_lang, get_text(topic_or_post) if (detected_lang&.to_s.eql? I18n.locale.to_s) - - unless translate_supported?(detected_lang, I18n.locale) - raise I18n.t("translator.failed", source_locale: detected_lang, target_locale: I18n.locale) + def self.translate!(topic_or_post) + detected_locale = detect(topic_or_post) + save_translation(topic_or_post) do + res = + result( + TRANSLATE_URI, + q: text_for_translation(topic_or_post), + source: detected_locale, + target: SUPPORTED_LANG_MAPPING[I18n.locale], + ) + res["translations"][0]["translatedText"] end - - translated_text = - from_custom_fields(topic_or_post) do - res = - result( - TRANSLATE_URI, - q: text_for_translation(topic_or_post), - source: detected_lang, - target: SUPPORTED_LANG_MAPPING[I18n.locale], - ) - res["translations"][0]["translatedText"] - end - - [detected_lang, translated_text] end def self.result(url, body) diff --git a/app/services/discourse_translator/libre_translate.rb b/app/services/discourse_translator/libre_translate.rb index 0996a809..f464c9c3 100644 --- a/app/services/discourse_translator/libre_translate.rb +++ b/app/services/discourse_translator/libre_translate.rb @@ -78,19 +78,14 @@ def self.access_token SiteSetting.translator_libretranslate_api_key end - def self.detect(topic_or_post) - res = - result( - detect_uri, - q: ActionController::Base.helpers.strip_tags(text_for_detection(topic_or_post)), - ) - - if !res.empty? - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= res[0][ - "language" - ] - else - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= "en" + def self.detect!(topic_or_post) + save_detected_locale(topic_or_post) do + res = + result( + detect_uri, + q: ActionController::Base.helpers.strip_tags(text_for_detection(topic_or_post)), + ) + !res.empty? ? res[0]["language"] : "en" end end @@ -100,27 +95,20 @@ def self.translate_supported?(source, target) res.any? { |obj| obj["code"] == source } && res.any? { |obj| obj["code"] == lang } end - def self.translate(topic_or_post) + def self.translate!(topic_or_post) detected_lang = detect(topic_or_post) - unless translate_supported?(detected_lang, I18n.locale) - raise I18n.t("translator.failed", source_locale: detected_lang, target_locale: I18n.locale) + save_translation(topic_or_post) do + res = + result( + translate_uri, + q: text_for_translation(topic_or_post), + source: detected_lang, + target: SUPPORTED_LANG_MAPPING[I18n.locale], + format: "html", + ) + res["translatedText"] end - - translated_text = - from_custom_fields(topic_or_post) do - res = - result( - translate_uri, - q: text_for_translation(topic_or_post), - source: detected_lang, - target: SUPPORTED_LANG_MAPPING[I18n.locale], - format: "html", - ) - res["translatedText"] - end - - [detected_lang, translated_text] end def self.get(url) diff --git a/app/services/discourse_translator/microsoft.rb b/app/services/discourse_translator/microsoft.rb index 58354cb7..4a4d1653 100644 --- a/app/services/discourse_translator/microsoft.rb +++ b/app/services/discourse_translator/microsoft.rb @@ -148,51 +148,38 @@ def self.access_token_key "microsoft-translator" end - def self.detect(topic_or_post) - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= begin + def self.detect!(topic_or_post) + save_detected_locale(topic_or_post) do body = [{ "Text" => text_for_detection(topic_or_post) }].to_json - uri = URI(detect_endpoint) uri.query = URI.encode_www_form(self.default_query) - - response_body = result(uri.to_s, body, default_headers) - - response_body.first["language"] + result(uri.to_s, body, default_headers).first["language"] end end - def self.translate(topic_or_post) + def self.translate!(topic_or_post) detected_lang = detect(topic_or_post) - if !SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) && - !SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s) - raise TranslatorError.new( - I18n.t( - "translator.failed", - source_locale: detected_lang, - target_locale: I18n.locale, - ), - ) - end - - if get_text(topic_or_post).length > LENGTH_LIMIT + if text_for_translation(topic_or_post).length > LENGTH_LIMIT raise TranslatorError.new(I18n.t("translator.too_long")) end - translated_text = - from_custom_fields(topic_or_post) do - query = default_query.merge("from" => detected_lang, "to" => locale, "textType" => "html") + save_translation(topic_or_post) do + query = default_query.merge("from" => detected_lang, "to" => locale, "textType" => "html") - body = [{ "Text" => text_for_translation(topic_or_post) }].to_json + body = [{ "Text" => text_for_translation(topic_or_post) }].to_json - uri = URI(translate_endpoint) - uri.query = URI.encode_www_form(query) + uri = URI(translate_endpoint) + uri.query = URI.encode_www_form(query) - response_body = result(uri.to_s, body, default_headers) - response_body.first["translations"].first["text"] - end + response_body = result(uri.to_s, body, default_headers) + response_body.first["translations"].first["text"] + end + end - [detected_lang, translated_text] + def self.translate_supported?(detected_lang, target_lang) + SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) && + SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s) end private diff --git a/app/services/discourse_translator/yandex.rb b/app/services/discourse_translator/yandex.rb index 2b8af875..cadeed61 100644 --- a/app/services/discourse_translator/yandex.rb +++ b/app/services/discourse_translator/yandex.rb @@ -123,50 +123,37 @@ def self.access_token (raise TranslatorError.new("NotFound: Yandex API Key not set.")) end - def self.detect(topic_or_post) - topic_or_post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= begin + def self.detect!(topic_or_post) + save_detected_locale(topic_or_post) do query = default_query.merge("text" => text_for_detection(topic_or_post)) - uri = URI(DETECT_URI) uri.query = URI.encode_www_form(query) - - response_body = result(uri.to_s, "", default_headers) - - response_body["lang"] + result(uri.to_s, "", default_headers)["lang"] end end - def self.translate(topic_or_post) + def self.translate!(topic_or_post) detected_lang = detect(topic_or_post) - if !SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) && - !SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s) - raise TranslatorError.new( - I18n.t( - "translator.failed", - source_locale: detected_lang, - target_locale: I18n.locale, - ), - ) - end - - translated_text = - from_custom_fields(topic_or_post) do - query = - default_query.merge( - "lang" => "#{detected_lang}-#{locale}", - "text" => text_for_translation(topic_or_post), - "format" => "html", - ) + save_translation(topic_or_post) do + query = + default_query.merge( + "lang" => "#{detected_lang}-#{locale}", + "text" => text_for_translation(topic_or_post), + "format" => "html", + ) - uri = URI(TRANSLATE_URI) - uri.query = URI.encode_www_form(query) + uri = URI(TRANSLATE_URI) + uri.query = URI.encode_www_form(query) - response_body = result(uri.to_s, "", default_headers) - response_body["text"][0] - end + response_body = result(uri.to_s, "", default_headers) + response_body["text"][0] + end + end - [detected_lang, translated_text] + def self.translate_supported?(detected_lang, target_lang) + SUPPORTED_LANG_MAPPING.keys.include?(detected_lang.to_sym) && + SUPPORTED_LANG_MAPPING.values.include?(detected_lang.to_s) end private diff --git a/spec/services/microsoft_spec.rb b/spec/services/microsoft_spec.rb index 53dcecf3..1ca1f22a 100644 --- a/spec/services/microsoft_spec.rb +++ b/spec/services/microsoft_spec.rb @@ -184,6 +184,8 @@ def translate_endpoint end it "raises an error if the post is too long to be translated" do + I18n.locale = "ja" + SiteSetting.max_characters_per_translation = 100_000 post.update_columns(cooked: "*" * (DiscourseTranslator::Microsoft::LENGTH_LIMIT + 1)) expect { described_class.translate(post) }.to raise_error( @@ -193,9 +195,10 @@ def translate_endpoint end it "raises an error on failure" do + I18n.locale = "ja" stub_request( :post, - "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=en&textType=html&to=en", + "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=en&textType=html&to=ja", ).with( body: "[{\"Text\":\"\\u003cp\\u003eHello world\\u003c/p\\u003e\"}]", headers: { diff --git a/spec/services/yandex_spec.rb b/spec/services/yandex_spec.rb index a9c01e08..24ce0e41 100644 --- a/spec/services/yandex_spec.rb +++ b/spec/services/yandex_spec.rb @@ -41,7 +41,7 @@ it "raises an error on failure" do described_class.expects(:access_token).returns("12345") - described_class.expects(:detect).returns("en") + described_class.expects(:detect).at_least_once.returns("de") Excon.expects(:post).returns( mock_response.new( From 6cd3449ea35eaa51e530454824736378ccd79605 Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 18:46:49 +0800 Subject: [PATCH 2/6] Remove unnecessary method --- app/services/discourse_translator/amazon.rb | 7 ------- lib/discourse_translator/post_extension.rb | 4 ++-- lib/discourse_translator/topic_extension.rb | 4 ++-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/services/discourse_translator/amazon.rb b/app/services/discourse_translator/amazon.rb index 2c0f4eff..4a6b3771 100644 --- a/app/services/discourse_translator/amazon.rb +++ b/app/services/discourse_translator/amazon.rb @@ -164,12 +164,5 @@ def self.client @client ||= Aws::Translate::Client.new(opts) end - - def self.assign_lang_custom_field(post, value) - if value.nil? - return post.custom_fields.delete(DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD) - end - post.custom_fields[DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD] ||= value - end end end diff --git a/lib/discourse_translator/post_extension.rb b/lib/discourse_translator/post_extension.rb index 6e0ba386..08b87d0e 100644 --- a/lib/discourse_translator/post_extension.rb +++ b/lib/discourse_translator/post_extension.rb @@ -4,11 +4,11 @@ module DiscourseTranslator module PostExtension extend ActiveSupport::Concern - prepended { before_update :clear_translator_custom_fields, if: :raw_changed? } + prepended { before_update :clear_translations, if: :raw_changed? } private - def clear_translator_custom_fields + def clear_translations return if !SiteSetting.translator_enabled self.custom_fields.delete(DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD) diff --git a/lib/discourse_translator/topic_extension.rb b/lib/discourse_translator/topic_extension.rb index fb47b1be..d0e3f4ec 100644 --- a/lib/discourse_translator/topic_extension.rb +++ b/lib/discourse_translator/topic_extension.rb @@ -4,11 +4,11 @@ module DiscourseTranslator module TopicExtension extend ActiveSupport::Concern - prepended { before_update :clear_translator_custom_fields, if: :title_changed? } + prepended { before_update :clear_translations, if: :title_changed? } private - def clear_translator_custom_fields + def clear_translations return if !SiteSetting.translator_enabled self.custom_fields.delete(DiscourseTranslator::DETECTED_LANG_CUSTOM_FIELD) From 976088736dcb47fbbac68c17269ae5366760ba2b Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 19:03:49 +0800 Subject: [PATCH 3/6] Add some docs --- app/services/discourse_translator/base.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/services/discourse_translator/base.rb b/app/services/discourse_translator/base.rb index f75dd1a9..8a1f728f 100644 --- a/app/services/discourse_translator/base.rb +++ b/app/services/discourse_translator/base.rb @@ -24,6 +24,10 @@ def self.cache_key "#{key_prefix}#{access_token_key}" end + # Returns the stored translation of a post or topic. + # If the translation does not exist yet, it will be translated first via the API then stored. + # If the detected language is the same as the target language, the original text will be returned. + # @param topic_or_post [Post|Topic] def self.translate(topic_or_post) return if text_for_translation(topic_or_post).blank? detected_lang = detect(topic_or_post) @@ -39,12 +43,15 @@ def self.translate(topic_or_post) ) end - translated_text = translate!(topic_or_post) + translated_text = get_detected_locale(topic_or_post) || translate!(topic_or_post) [detected_lang, translated_text] end - def self.translate!(post) + # Subclasses must implement this method to translate the text of a post or topic + # then use the save_translation method to store the translated text. + # @param topic_or_post [Post|Topic] + def self.translate!(topic_or_post) raise "Not Implemented" end @@ -56,6 +63,9 @@ def self.detect(topic_or_post) get_detected_locale(topic_or_post) || detect!(topic_or_post) end + # Subclasses must implement this method to translate the text of a post or topic + # then use the save_translation method to store the translated text. + # @param topic_or_post [Post|Topic] def self.detect!(post) raise "Not Implemented" end From 7607b46d35c40825c872b33226875f7f0c2c408c Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 19:06:44 +0800 Subject: [PATCH 4/6] eql to == when comparing strings --- app/services/discourse_translator/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/discourse_translator/base.rb b/app/services/discourse_translator/base.rb index 8a1f728f..9d47ab62 100644 --- a/app/services/discourse_translator/base.rb +++ b/app/services/discourse_translator/base.rb @@ -32,7 +32,7 @@ def self.translate(topic_or_post) return if text_for_translation(topic_or_post).blank? detected_lang = detect(topic_or_post) - return detected_lang, get_text(topic_or_post) if (detected_lang&.to_s.eql? I18n.locale.to_s) + return detected_lang, get_text(topic_or_post) if (detected_lang&.to_s == I18n.locale.to_s) unless translate_supported?(detected_lang, I18n.locale) raise TranslatorError.new( I18n.t( From 8c24ecfc4f46f242804ef507aa3802c8793fa93f Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 19:08:27 +0800 Subject: [PATCH 5/6] Use correct method - completion error --- app/services/discourse_translator/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/discourse_translator/base.rb b/app/services/discourse_translator/base.rb index 9d47ab62..3da3d03b 100644 --- a/app/services/discourse_translator/base.rb +++ b/app/services/discourse_translator/base.rb @@ -43,7 +43,7 @@ def self.translate(topic_or_post) ) end - translated_text = get_detected_locale(topic_or_post) || translate!(topic_or_post) + translated_text = get_translation(topic_or_post) || translate!(topic_or_post) [detected_lang, translated_text] end From 0e06bab788185a88a18dd8361fa85f9b80fce39f Mon Sep 17 00:00:00 2001 From: Nat Date: Thu, 6 Feb 2025 20:44:35 +0800 Subject: [PATCH 6/6] Add further base class tests --- app/services/discourse_translator/base.rb | 9 ++-- spec/services/base_spec.rb | 62 +++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/app/services/discourse_translator/base.rb b/app/services/discourse_translator/base.rb index 3da3d03b..6d927e22 100644 --- a/app/services/discourse_translator/base.rb +++ b/app/services/discourse_translator/base.rb @@ -33,6 +33,10 @@ def self.translate(topic_or_post) detected_lang = detect(topic_or_post) return detected_lang, get_text(topic_or_post) if (detected_lang&.to_s == I18n.locale.to_s) + + existing_translation = get_translation(topic_or_post) + return detected_lang, existing_translation if existing_translation.present? + unless translate_supported?(detected_lang, I18n.locale) raise TranslatorError.new( I18n.t( @@ -42,10 +46,7 @@ def self.translate(topic_or_post) ), ) end - - translated_text = get_translation(topic_or_post) || translate!(topic_or_post) - - [detected_lang, translated_text] + [detected_lang, translate!(topic_or_post)] end # Subclasses must implement this method to translate the text of a post or topic diff --git a/spec/services/base_spec.rb b/spec/services/base_spec.rb index eb917c31..3d9aa5f0 100644 --- a/spec/services/base_spec.rb +++ b/spec/services/base_spec.rb @@ -99,4 +99,66 @@ class EmptyTranslator < DiscourseTranslator::Base ) end end + + describe ".detect" do + fab!(:post) + + it "returns nil when text is blank" do + post.cooked = "" + expect(TestTranslator.detect(post)).to be_nil + end + + it "returns cached detection if available" do + TestTranslator.save_detected_locale(post) { "en" } + + TestTranslator.expects(:detect!).never + expect(TestTranslator.detect(post)).to eq("en") + end + + it "performs detection if no cached result" do + TestTranslator.save_detected_locale(post) { nil } + TestTranslator.expects(:detect!).with(post).returns("es") + + expect(TestTranslator.detect(post)).to eq("es") + end + end + + describe ".translate" do + fab!(:post) + + it "returns nil when text is blank" do + post.cooked = "" + expect(TestTranslator.translate(post)).to be_nil + end + + it "returns original text when detected language matches current locale" do + TestTranslator.save_detected_locale(post) { I18n.locale.to_s } + post.cooked = "hello" + + expect(TestTranslator.translate(post)).to eq(%w[en hello]) + end + + it "returns cached translation if available" do + TestTranslator.save_detected_locale(post) { "es" } + TestTranslator.save_translation(post) { "hello" } + + expect(TestTranslator.translate(post)).to eq(%w[es hello]) + end + + it "raises error when translation not supported" do + TestTranslator.save_detected_locale(post) { "xx" } + TestTranslator.save_translation(post) { nil } + TestTranslator.expects(:translate_supported?).with("xx", :en).returns(false) + + expect { TestTranslator.translate(post) }.to raise_error(DiscourseTranslator::TranslatorError) + end + + it "performs translation when needed" do + TestTranslator.save_detected_locale(post) { "es" } + TestTranslator.save_translation(post) { nil } + TestTranslator.expects(:translate!).returns("hello") + + expect(TestTranslator.translate(post)).to eq(%w[es hello]) + end + end end