diff --git a/assets/javascripts/discourse/components/translated-post.gjs b/assets/javascripts/discourse/components/translated-post.gjs index d5ea3148..a938d984 100644 --- a/assets/javascripts/discourse/components/translated-post.gjs +++ b/assets/javascripts/discourse/components/translated-post.gjs @@ -31,10 +31,6 @@ export default class TranslatedPost extends Component { return this.post.translatedTitle; } - get showTranslation() { - return !this.siteSettings.experimental_inline_translation; - } - diff --git a/assets/javascripts/discourse/initializers/extend-for-translate-button.js b/assets/javascripts/discourse/initializers/extend-for-translate-button.js index d194f134..61f3e34c 100644 --- a/assets/javascripts/discourse/initializers/extend-for-translate-button.js +++ b/assets/javascripts/discourse/initializers/extend-for-translate-button.js @@ -26,42 +26,6 @@ function initializeTranslation(api) { ); } - if ( - siteSettings.experimental_inline_translation && - (currentUser || siteSettings.experimental_anon_language_switcher) - ) { - api.renderInOutlet("topic-navigation", ShowOriginalContent); - - api.registerCustomPostMessageCallback( - "translated_post", - (topicController, data) => { - if ( - new URLSearchParams(window.location.search).get("show") === "original" - ) { - return; - } - const postStream = topicController.get("model.postStream"); - postStream.triggerChangedPost(data.id, data.updated_at).then(() => { - topicController.appEvents.trigger("post-stream:refresh", { - id: data.id, - }); - }); - } - ); - - api.includePostAttributes("is_translated", "detected_language"); - api.decorateWidget("post-date:before", (dec) => { - if (dec.attrs.is_translated && dec.attrs.detected_language) { - return new RenderGlimmer( - dec.widget, - "div.post-info.post-translated-indicator", - TranslatedPostIndicator, - { detectedLanguage: dec.attrs.detected_language } - ); - } - }); - } - customizePostMenu(api); } diff --git a/assets/javascripts/discourse/services/translator.js b/assets/javascripts/discourse/services/translator.js index 1a29bafb..93258d21 100644 --- a/assets/javascripts/discourse/services/translator.js +++ b/assets/javascripts/discourse/services/translator.js @@ -15,16 +15,6 @@ export default class TranslatorService extends Service { post.detectedLang = response.detected_lang; post.translatedText = response.translation; post.translatedTitle = response.title_translation; - if (this.siteSettings.experimental_inline_translation) { - if (post.post_number === 1) { - post.topic.set("fancy_title", response.title_translation); - this.appEvents.trigger("header:update-topic", post.topic); - this.documentTitle.setTitle(response.title_translation); - } - post.set("cooked", response.translation); - post.set("can_translate", false); - this.appEvents.trigger("post-stream:refresh", { id: post.id }); - } } clearPostTranslation(post) { diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2bba77c8..c78afefb 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -21,7 +21,6 @@ en: errors: needs_nonzero_backfill: "Automatic language translation requires the 'automatic_translation_backfill_rate' hidden setting to be a non-zero value. Please approach your site administrator to increase this limit." experimental_anon_language_switcher_requirements: "The experimental language switcher requires the `set locale from cookie` site setting to be enabled, and the `automatic translation target languages` to have at least one language." - experimental_inline_translation: "Enable experimental inline translation feature. This replaces existing parallel translation, allowing site visitors with a non-default locale to view content in their language." automatic_translation_target_languages: "The languages to automatically translate user content (posts, topics) to. If empty, no languages will be automatically translated." translator: failed: diff --git a/config/settings.yml b/config/settings.yml index 4cf99732..4a324e35 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -126,9 +126,6 @@ discourse_translator: default: false client: true validator: "DiscourseTranslator::Validators::LanguageSwitcherSettingValidator" - experimental_inline_translation: - default: false - client: true experimental_content_translation: default: false hidden: true diff --git a/lib/discourse_translator/extensions/guardian_extension.rb b/lib/discourse_translator/extensions/guardian_extension.rb index 47ba307f..b517c2b7 100644 --- a/lib/discourse_translator/extensions/guardian_extension.rb +++ b/lib/discourse_translator/extensions/guardian_extension.rb @@ -31,14 +31,9 @@ def can_translate?(post) # this prevents the 🌐from appearing and then disappearing if the lang is same as user's lang return false if post.updated_at > POST_DETECTION_BUFFER.ago && post.detected_locale.blank? - if SiteSetting.experimental_inline_translation - locale = DiscourseTranslator::InlineTranslation.effective_locale - return false if post.locale_matches?(locale) - post.translation_for(locale).nil? - else - return false if post.locale_matches?(I18n.locale) - poster_group_allow_translate?(post) - end + locale = I18n.locale + return false if post.locale_matches?(locale) + poster_group_allow_translate?(post) end end end diff --git a/lib/discourse_translator/extensions/topic_view_serializer_extension.rb b/lib/discourse_translator/extensions/topic_view_serializer_extension.rb deleted file mode 100644 index 4c4ec56b..00000000 --- a/lib/discourse_translator/extensions/topic_view_serializer_extension.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -module DiscourseTranslator - module Extensions - module TopicViewSerializerExtension - def posts - if SiteSetting.translator_enabled? - posts_query = object.posts.includes(:content_locale) - # this is kind of a micro-optimization. - # we do not want to eager load translations if the user is using the site's language. - # we will only load them if the user is using a different language that is supported by the site. - if SiteSetting.experimental_inline_translation && - !LocaleMatcher.user_locale_is_default? && - LocaleMatcher.user_locale_in_target_languages? - locale = InlineTranslation.effective_locale.to_s.gsub("_", "-") - posts_query = - posts_query - .includes(:translations) - .references(:translations) - .where(translations: { locale: [nil, locale] }) - end - object.instance_variable_set(:@posts, posts_query) - end - super - end - end - end -end diff --git a/lib/discourse_translator/inline_translation.rb b/lib/discourse_translator/inline_translation.rb deleted file mode 100644 index f9f3834a..00000000 --- a/lib/discourse_translator/inline_translation.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -module DiscourseTranslator - class InlineTranslation - def self.effective_locale - if LocaleMatcher.user_locale_is_default? || LocaleMatcher.user_locale_in_target_languages? - I18n.locale - else - SiteSetting.default_locale - end - end - - SHOW_ORIGINAL_COOKIE = "discourse-translator-show-original" - - def inject(plugin) - plugin.register_anonymous_cache_key :showoriginal do - @request.cookies[SHOW_ORIGINAL_COOKIE].present? ? "1" : "0" - end - - # since locales are eager loaded but translations may not, - # always return early if topic and posts are in the user's effective_locale. - # this prevents the need to load translations. - - plugin.register_modifier(:basic_post_serializer_cooked) do |cooked, serializer| - if show_translation?(serializer.object, serializer.scope) - serializer.object.translation_for(InlineTranslation.effective_locale).presence - else - cooked - end - end - - plugin.register_modifier(:topic_serializer_fancy_title) do |fancy_title, serializer| - if show_translation?(serializer.object, serializer.scope) - serializer - .object - .translation_for(InlineTranslation.effective_locale) - .presence - &.then { |t| Topic.fancy_title(t) } - else - fancy_title - end - end - - plugin.register_modifier(:topic_view_serializer_fancy_title) do |fancy_title, serializer| - if show_translation?(serializer.object.topic, serializer.scope) - serializer - .object - .topic - .translation_for(InlineTranslation.effective_locale) - .presence - &.then { |t| Topic.fancy_title(t) } - else - fancy_title - end - end - - plugin.add_to_serializer(:basic_post, :is_translated) do - SiteSetting.experimental_inline_translation && - !object.locale_matches?(InlineTranslation.effective_locale) && - !scope&.request&.cookies&.key?(SHOW_ORIGINAL_COOKIE) && - object.translation_for(InlineTranslation.effective_locale).present? - end - - plugin.add_to_serializer(:basic_post, :detected_language) do - if SiteSetting.experimental_inline_translation && object.detected_locale.present? - LocaleToLanguage.get_language(object.detected_locale) - end - end - - plugin.add_to_serializer(:topic_view, :show_translation_toggle) do - return false if !SiteSetting.experimental_inline_translation - # either the topic or any of the posts has a translation - # also, check the locale first as it is cheaper than loading translation - ( - !object.topic.locale_matches?(InlineTranslation.effective_locale) && - object.topic.translation_for(InlineTranslation.effective_locale).present? - ) || - ( - object.posts.any? do |post| - !post.locale_matches?(InlineTranslation.effective_locale) && - post.translation_for(InlineTranslation.effective_locale).present? - end - ) - end - - plugin.register_topic_preloader_associations(:content_locale) do - SiteSetting.translator_enabled && SiteSetting.experimental_inline_translation - end - plugin.register_topic_preloader_associations(:translations) do - SiteSetting.translator_enabled && SiteSetting.experimental_inline_translation - end - end - - def show_translation?(translatable, scope) - SiteSetting.experimental_inline_translation && - !translatable.locale_matches?(InlineTranslation.effective_locale) && - !scope&.request&.cookies&.key?(SHOW_ORIGINAL_COOKIE) - end - end -end diff --git a/lib/discourse_translator/locale_matcher.rb b/lib/discourse_translator/locale_matcher.rb deleted file mode 100644 index f5786b2b..00000000 --- a/lib/discourse_translator/locale_matcher.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module DiscourseTranslator - class LocaleMatcher - def self.user_locale_in_target_languages? - # e.g. "en|es|pt_BR" vs :en_UK - SiteSetting.automatic_translation_target_languages.split("|").include?(I18n.locale.to_s) - end - - def self.user_locale_is_default? - # e.g. :en_UK vs "en_UK" - I18n.locale.to_s == SiteSetting.default_locale - end - end -end diff --git a/lib/discourse_translator/locale_to_language.rb b/lib/discourse_translator/locale_to_language.rb deleted file mode 100644 index 6e9aa210..00000000 --- a/lib/discourse_translator/locale_to_language.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module DiscourseTranslator - class LocaleToLanguage - def self.get_language(locale) - LocaleSiteSetting.values.find { |v| v[:value] == locale.to_s.sub("-", "_") }&.[](:name) - end - end -end diff --git a/plugin.rb b/plugin.rb index 12157bf6..909ef113 100644 --- a/plugin.rb +++ b/plugin.rb @@ -29,7 +29,6 @@ module ::DiscourseTranslator Guardian.prepend(DiscourseTranslator::Extensions::GuardianExtension) Post.prepend(DiscourseTranslator::Extensions::PostExtension) Topic.prepend(DiscourseTranslator::Extensions::TopicExtension) - TopicViewSerializer.prepend(DiscourseTranslator::Extensions::TopicViewSerializerExtension) end add_to_serializer :post, :can_translate do @@ -37,7 +36,5 @@ module ::DiscourseTranslator end DiscourseTranslator::ParallelTextTranslation.new.inject(self) - DiscourseTranslator::InlineTranslation.new.inject(self) - DiscourseTranslator::AutomaticTranslations.new.inject(self) end diff --git a/spec/lib/guardian_extension_spec.rb b/spec/lib/guardian_extension_spec.rb index 3a656f48..b858bdfc 100644 --- a/spec/lib/guardian_extension_spec.rb +++ b/spec/lib/guardian_extension_spec.rb @@ -138,28 +138,6 @@ expect(Guardian.new.can_translate?(post)).to eq(false) end - describe "anon user" do - before { SiteSetting.restrict_translation_by_group = "#{Group::AUTO_GROUPS[:everyone]}" } - - it "cannot translate" do - SiteSetting.experimental_inline_translation = true - expect(Guardian.new.can_translate?(post)).to eq(false) - - SiteSetting.experimental_inline_translation = false - expect(Guardian.new.can_translate?(post)).to eq(false) - end - end - - it "cannot translate when post detected locale matches i18n locale" do - post.set_detected_locale("pt") - - SiteSetting.experimental_inline_translation = true - expect(guardian.can_translate?(post)).to eq(false) - - SiteSetting.experimental_inline_translation = false - expect(guardian.can_translate?(post)).to eq(false) - end - it "allows translation depending on when the post is updated" do SiteSetting.restrict_translation_by_group = "#{group.id}" @@ -176,76 +154,38 @@ expect(guardian.can_translate?(post)).to eq(false) end - describe "when experimental_inline_translation enabled" do - before do - SiteSetting.experimental_inline_translation = true - - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "pt" - end - - describe "logged in user" do - it "cannot translate when user is not in restrict_translation_by_group" do - SiteSetting.restrict_translation_by_group = "#{group.id + 1}" - - expect(guardian.can_translate?(post)).to eq(false) - end - - describe "user is in restrict_translation_by_group" do - before { SiteSetting.restrict_translation_by_group = "#{group.id}" } + it "cannot translate when user is not in restrict_translation_by_group" do + SiteSetting.restrict_translation_by_group = "#{group.id + 1}" - it "cannot translate when post has translation for user locale" do - post.set_detected_locale("ja") - post.set_translation("pt", "Olá, mundo!") + expect(guardian.can_translate?(post)).to eq(false) + end - expect(guardian.can_translate?(post)).to eq(false) - end + describe "user is in restrict_translation_by_group" do + before { SiteSetting.restrict_translation_by_group = "#{group.id}" } - it "can translate when post does not have translation for user locale" do - post.set_detected_locale("jp") + it "can translate when post's detected locale does not match i18n locale, regardless of translation presence" do + post.set_detected_locale("jp") + expect(guardian.can_translate?(post)).to eq(true) - expect(guardian.can_translate?(post)).to eq(true) - end - end + post.set_translation("pt", "Olá, mundo!") + expect(guardian.can_translate?(post)).to eq(true) end - end - describe "when experimental inline translation disabled" do - before { SiteSetting.experimental_inline_translation = false } - - it "cannot translate when user is not in restrict_translation_by_group" do - SiteSetting.restrict_translation_by_group = "#{group.id + 1}" + it "cannot translate if poster is not in restrict_translation_by_poster_group" do + SiteSetting.restrict_translation_by_poster_group = "#{Group::AUTO_GROUPS[:staff]}" expect(guardian.can_translate?(post)).to eq(false) end - describe "user is in restrict_translation_by_group" do - before { SiteSetting.restrict_translation_by_group = "#{group.id}" } - - it "can translate when post's detected locale does not match i18n locale, regardless of translation presence" do - post.set_detected_locale("jp") - expect(guardian.can_translate?(post)).to eq(true) - - post.set_translation("pt", "Olá, mundo!") - expect(guardian.can_translate?(post)).to eq(true) - end - - it "cannot translate if poster is not in restrict_translation_by_poster_group" do - SiteSetting.restrict_translation_by_poster_group = "#{Group::AUTO_GROUPS[:staff]}" - - expect(guardian.can_translate?(post)).to eq(false) - end - - it "can translate if poster is in restrict_translation_by_poster_group" do - poster = post.user - poster_group = Fabricate(:group, users: [poster]) + it "can translate if poster is in restrict_translation_by_poster_group" do + poster = post.user + poster_group = Fabricate(:group, users: [poster]) - SiteSetting.restrict_translation_by_poster_group = "#{poster_group.id}" - expect(guardian.can_translate?(post)).to eq(true) + SiteSetting.restrict_translation_by_poster_group = "#{poster_group.id}" + expect(guardian.can_translate?(post)).to eq(true) - SiteSetting.restrict_translation_by_poster_group = "" - expect(guardian.can_translate?(post)).to eq(true) - end + SiteSetting.restrict_translation_by_poster_group = "" + expect(guardian.can_translate?(post)).to eq(true) end end end diff --git a/spec/lib/locale_to_language_spec.rb b/spec/lib/locale_to_language_spec.rb deleted file mode 100644 index 392bd1d3..00000000 --- a/spec/lib/locale_to_language_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -describe DiscourseTranslator::LocaleToLanguage do - describe ".get_language" do - it "returns the language name for a valid locale" do - expect(DiscourseTranslator::LocaleToLanguage.get_language("en")).to eq("English (US)") - expect(DiscourseTranslator::LocaleToLanguage.get_language("es")).to eq("Español") - end - - it "returns nil for a locale that doesn't exist" do - expect(DiscourseTranslator::LocaleToLanguage.get_language("xx")).to be_nil - end - - it "handles symbol locales" do - expect(DiscourseTranslator::LocaleToLanguage.get_language(:en_GB)).to eq("English (UK)") - end - end -end diff --git a/spec/models/hyphenate_locales_spec.rb b/spec/models/hyphenate_locales_spec.rb deleted file mode 100644 index e14f4c3e..00000000 --- a/spec/models/hyphenate_locales_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../db/migrate/20250210171147_hyphenate_translator_locales" - -module DiscourseTranslator - describe HyphenateTranslatorLocales do - let(:migration) { described_class.new } - - it "normalizes underscores to dashes in all translator tables" do - topic = Fabricate(:topic) - post = Fabricate(:post, topic: topic) - - DiscourseTranslator::TopicTranslation.create!(topic:, locale: "en_GB", translation: "test") - DiscourseTranslator::PostTranslation.create!(post:, locale: "fr_CA", translation: "test") - DiscourseTranslator::TopicLocale.create!(topic:, detected_locale: "es_MX") - DiscourseTranslator::PostLocale.create!(post:, detected_locale: "pt_BR") - - migration.up - - expect(DiscourseTranslator::TopicTranslation.last.locale).to eq("en-GB") - expect(DiscourseTranslator::PostTranslation.last.locale).to eq("fr-CA") - expect(DiscourseTranslator::TopicLocale.last.detected_locale).to eq("es-MX") - expect(DiscourseTranslator::PostLocale.last.detected_locale).to eq("pt-BR") - end - - it "handles multiple batches" do - described_class.const_set(:BATCH_SIZE, 2) - - topic = Fabricate(:topic) - post = Fabricate(:post, topic: topic) - - 5.times { |i| post.set_translation("en_#{i}", "test#{i}") } - 5.times { |i| post.set_translation("en-#{i + 10}", "test#{i}") } - 5.times { |i| post.set_translation("en_#{i + 20}", "test#{i}") } - - migration.up - - locales = DiscourseTranslator::PostTranslation.pluck(:locale) - expect(locales).to all(match(/\A[a-z]+-\d+\z/)) - expect(locales).not_to include(match(/_/)) - end - - it "only updates records containing underscores" do - topic = Fabricate(:topic) - - topic.set_translation("en_GB", "test") - DiscourseTranslator::TopicTranslation.create!( - topic: topic, - locale: "fr_CA", - translation: "test2", - ) - - expect { migration.up }.to change { - DiscourseTranslator::TopicTranslation.where("locale LIKE ? ESCAPE '\\'", "%\\_%").count - }.from(1).to(0) - - expect(DiscourseTranslator::TopicTranslation.pluck(:locale)).to contain_exactly( - "en-GB", - "fr-CA", - ) - end - end -end diff --git a/spec/models/translation_tables_spec.rb b/spec/models/translation_tables_spec.rb deleted file mode 100644 index 091e4253..00000000 --- a/spec/models/translation_tables_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../db/migrate/20250205082401_move_translations_custom_fields_to_table" - -module DiscourseTranslator - describe MoveTranslationsCustomFieldsToTable do - DETECTED_LANG_CUSTOM_FIELD = "post_detected_lang".freeze - TRANSLATED_CUSTOM_FIELD = "translated_text".freeze - - let!(:batch_size) { 3 } - - before { described_class.const_set(:BATCH_SIZE, batch_size) } - - def create_translation_custom_fields(count) - count.times do - t_post = Fabricate(:post) - t_topic = Fabricate(:topic) - - t_post.custom_fields[DETECTED_LANG_CUSTOM_FIELD] = "pt" - t_post.save_custom_fields - - t_topic.custom_fields[DETECTED_LANG_CUSTOM_FIELD] = "es" - t_topic.save_custom_fields - - t_post.custom_fields[TRANSLATED_CUSTOM_FIELD] = { - en_GB: "The Romance of the Three Kingdoms", - de: "Die Romanze der Drei Königreiche", - } - t_post.save_custom_fields - - t_topic.custom_fields[TRANSLATED_CUSTOM_FIELD] = { - en_GB: "The Romance of the Three Kingdoms", - de: "Die Romanze der Drei Königreiche", - } - t_topic.save_custom_fields - end - end - - it "correctly migrates custom fields in batches" do - # batch size is 3 - create_translation_custom_fields(4) - # create some random custom fields in between - # to test the migrate loop doesn't end prematurely - 4.times do - post = Fabricate(:post) - post.custom_fields["x"] = "x" - post.save_custom_fields - - topic = Fabricate(:topic) - topic.custom_fields["x"] = "x" - topic.save_custom_fields - end - # another 4 - create_translation_custom_fields(4) - - migration = described_class.new - migration.up - - expect(PostLocale.count).to eq(8) - expect(PostTranslation.count).to eq(16) - - expect(TopicLocale.count).to eq(8) - expect(TopicTranslation.count).to eq(16) - - expect(PostLocale.last.detected_locale).to eq("pt") - - expect(PostTranslation.where(post_id: Post.last.id).pluck(:locale, :translation)).to include( - ["en_GB", "The Romance of the Three Kingdoms"], - ["de", "Die Romanze der Drei Königreiche"], - ) - - migration.down - expect(PostLocale.count).to eq(0) - expect(PostTranslation.count).to eq(0) - expect(TopicLocale.count).to eq(0) - expect(TopicTranslation.count).to eq(0) - end - - it "ignores invalid JSON in translated_text" do - post = Fabricate(:post) - post.custom_fields[TRANSLATED_CUSTOM_FIELD] = "invalid json" - post.save_custom_fields(true) - - migration = described_class.new - expect { migration.up }.not_to raise_error - expect(PostTranslation.count).to eq(0) - end - - it "ignores translations with locale longer than 20 chars" do - post = Fabricate(:post) - post.custom_fields[TRANSLATED_CUSTOM_FIELD] = { very_very_long_locale_name: "test" } - post.custom_fields[DETECTED_LANG_CUSTOM_FIELD] = "very_very_long_locale_name" - post.save_custom_fields(true) - - migration = described_class.new - expect { migration.up }.not_to raise_error - - expect(PostLocale.count).to eq(0) - expect(PostTranslation.count).to eq(0) - end - end -end diff --git a/spec/serializers/basic_topic_serializer_spec.rb b/spec/serializers/basic_topic_serializer_spec.rb deleted file mode 100644 index 27b7dee3..00000000 --- a/spec/serializers/basic_topic_serializer_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -describe BasicTopicSerializer do - fab!(:user) { Fabricate(:user, locale: "ja") } - fab!(:topic) - - before do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - end - - describe "#fancy_title" do - let!(:guardian) { Guardian.new(user) } - let!(:original_title) { "

FUS ROH DAAHHH

" } - let!(:jap_title) { "

フス・ロ・ダ・ア

" } - - before do - topic.title = original_title - SiteSetting.experimental_inline_translation = true - I18n.locale = "ja" - - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - end - - def serialize_topic(guardian_user: user, cookie: "") - env = create_request_env.merge("HTTP_COOKIE" => cookie) - request = ActionDispatch::Request.new(env) - guardian = Guardian.new(guardian_user, request) - BasicTopicSerializer.new(topic, scope: guardian) - end - - it "does not replace fancy_title with translation when experimental_inline_translation is disabled" do - SiteSetting.experimental_inline_translation = false - topic.set_translation("ja", jap_title) - - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "does not replace fancy_title with translation when show_original param is present" do - topic.set_translation("ja", jap_title) - expect( - serialize_topic( - cookie: DiscourseTranslator::InlineTranslation::SHOW_ORIGINAL_COOKIE, - ).fancy_title, - ).to eq(topic.fancy_title) - end - - it "does not replace fancy_title with translation when no translation exists" do - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "does not replace fancy_title when topic is already in correct locale" do - I18n.locale = "ja" - topic.set_detected_locale("ja") - topic.set_translation("ja", jap_title) - - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "does not replace fancy_title when user's locale is not in target languages" do - I18n.locale = "es" - topic.set_detected_locale("en") - topic.set_translation("es", jap_title) - - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "returns translated title in fancy_title when translation exists for current locale" do - topic.set_translation("ja", jap_title) - expect(serialize_topic.fancy_title).to eq("<h1>フス・ロ・ダ・ア</h1>") - end - end -end diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb index 23e616a0..9aebcc74 100644 --- a/spec/serializers/post_serializer_spec.rb +++ b/spec/serializers/post_serializer_spec.rb @@ -55,118 +55,4 @@ end end end - - describe "#is_translated" do - fab!(:post) - - it "returns false when translator disabled" do - SiteSetting.translator_enabled = false - serializer = PostSerializer.new(post, scope: Guardian.new) - - expect(serializer.is_translated).to eq(false) - end - - it "returns false when experimental inline translation disabled" do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = false - serializer = PostSerializer.new(post, scope: Guardian.new) - - expect(serializer.is_translated).to eq(false) - end - - it "returns true when there is a translation for the user's locale in target languages" do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - I18n.locale = "ja" - post.set_detected_locale("en") - post.set_translation("ja", "こんにちは") - serializer = PostSerializer.new(post, scope: Guardian.new) - - expect(serializer.is_translated).to eq(true) - end - - it "returns false when there is a translation for the user's locale not in target languages" do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "es" - I18n.locale = "ja" - post.set_detected_locale("en") - post.set_translation("ja", "こんにちは") - serializer = PostSerializer.new(post, scope: Guardian.new) - - expect(serializer.is_translated).to eq(false) - end - - it "returns false when there is no translation for the current locale in target languages" do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - I18n.locale = "ja" - post.set_translation("es", "Hola") - serializer = PostSerializer.new(post, scope: Guardian.new) - - expect(serializer.is_translated).to eq(false) - end - end - - describe "#cooked" do - def serialize_post(guardian_user: user, cookie: "") - env = create_request_env.merge("HTTP_COOKIE" => cookie) - request = ActionDispatch::Request.new(env) - guardian = Guardian.new(guardian_user, request) - PostSerializer.new(post, scope: guardian) - end - - before do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - end - - it "does not return translated_cooked when experimental_inline_translation is disabled" do - SiteSetting.experimental_inline_translation = false - expect(serialize_post.cooked).to eq(post.cooked) - end - - it "does not return translated_cooked when show original translation cookie is present" do - I18n.locale = "ja" - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - post.set_translation("ja", "こんにちは") - - expect( - serialize_post(cookie: DiscourseTranslator::InlineTranslation::SHOW_ORIGINAL_COOKIE).cooked, - ).to eq(post.cooked) - expect(serialize_post(cookie: "derp").cooked).to eq("こんにちは") - end - - it "does not return translated_cooked when post is already in correct locale" do - I18n.locale = "ja" - post.set_detected_locale("ja") - post.set_translation("ja", "こんにちは") - - expect(serialize_post.cooked).to eq(post.cooked) - end - - it "returns translated content based on locale presence in target languages" do - SiteSetting.automatic_translation_backfill_rate = 1 - post.set_translation("ja", "こんにちは") - post.set_translation("es", "Hola") - I18n.locale = "ja" - - SiteSetting.automatic_translation_target_languages = "ja" - expect(serialize_post.cooked).to eq("こんにちは") - - SiteSetting.automatic_translation_target_languages = "es" - expect(serialize_post.cooked).to eq(post.cooked) - end - - it "does not return translated_cooked when plugin is disabled" do - SiteSetting.translator_enabled = false - expect(serialize_post.cooked).to eq(post.cooked) - end - end end diff --git a/spec/serializers/topic_list_serializer_spec.rb b/spec/serializers/topic_list_serializer_spec.rb deleted file mode 100644 index 8ae41144..00000000 --- a/spec/serializers/topic_list_serializer_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -describe TopicListSerializer do - fab!(:topic1) { Fabricate(:topic) } - fab!(:topic2) { Fabricate(:topic) } - fab!(:topic3) { Fabricate(:topic) } - fab!(:user) - - let(:topic_list) { TopicList.new(nil, user, [topic1, topic2, topic3]) } - - before do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - - # required for other core parts of topic_view - [topic1, topic2, topic3].each { |topic| topic.tap { |t| t.allowed_user_ids = [t.user_id] } } - end - - describe "preloading" do - it "preloads locale and translations without N+1 queries" do - serializer = described_class.new(topic_list, scope: Guardian.new(user)) - - queries = track_sql_queries { serializer.as_json } - - locale_queries = queries.count { |q| q.include?("discourse_translator_topic_locales") } - expect(locale_queries).to eq(1) # would be 3 if not preloaded - - translation_queries = - queries.count { |q| q.include?("discourse_translator_topic_translations") } - expect(translation_queries).to eq(1) # would be 3 if not preloaded - - expect(topic_list.topics.first.association(:content_locale)).to be_loaded - end - - it "never preloads translations if SiteSetting.experimental_inline_translations is false" do - SiteSetting.experimental_inline_translation = false - - serializer = described_class.new(topic_list, scope: Guardian.new(user)) - queries = track_sql_queries { serializer.as_json } - - locale_queries = queries.count { |q| q.include?("discourse_translator_topic_locales") } - expect(locale_queries).to eq(0) - translation_queries = - queries.count { |q| q.include?("discourse_translator_topic_translations") } - expect(translation_queries).to eq(0) - end - end -end diff --git a/spec/serializers/topic_view_serializer_spec.rb b/spec/serializers/topic_view_serializer_spec.rb deleted file mode 100644 index 78dd96a4..00000000 --- a/spec/serializers/topic_view_serializer_spec.rb +++ /dev/null @@ -1,213 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -describe TopicViewSerializer do - fab!(:topic) - - before do - SiteSetting.translator_enabled = true - SiteSetting.restrict_translation_by_group = "#{Group::AUTO_GROUPS[:everyone]}" - SiteSetting.restrict_translation_by_poster_group = "#{Group::AUTO_GROUPS[:everyone]}" - end - - describe "preloading" do - fab!(:user) - fab!(:en_post) do - post = Fabricate(:post, topic: topic) - post.set_detected_locale("en") - post - end - fab!(:es_post) do - post = Fabricate(:post, topic: topic) - post.set_detected_locale("es") - post - end - fab!(:ja_post) do - post = Fabricate(:post, topic: topic) - post.set_detected_locale("ja") - post - end - - it "always preloads locale without N+1 queries" do - topic_view = TopicView.new(topic) - serializer = TopicViewSerializer.new(topic_view, scope: Guardian.new(user), root: false) - - json = {} - queries = track_sql_queries { json = serializer.as_json } - expect(json[:post_stream][:posts].map { |p| p[:can_translate] }).to eq([false, true, true]) - - translation_queries = queries.count { |q| q.include?("discourse_translator_post_locales") } - expect(translation_queries).to eq(1) # would be 3 (posts) if not preloaded - - expect(topic_view.posts.first.association(:content_locale)).to be_loaded - end - - it "never preloads translations if SiteSetting.experimental_inline_translations is false" do - SiteSetting.experimental_inline_translation = false - - topic_view = TopicView.new(topic) - serializer = TopicViewSerializer.new(topic_view, scope: Guardian.new(user), root: false) - - queries = track_sql_queries { serializer.as_json } - translation_queries = - queries.count { |q| q.include?("discourse_translator_post_translations") } - expect(translation_queries).to eq(0) - end - - describe "SiteSetting.experimental_inline_translations enabled with target language 'es'" do - before do - SiteSetting.experimental_inline_translation = true - - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "es" - - SiteSetting.default_locale = "en" - en_post.set_translation("es", "Hola") - en_post.set_translation("ja", "こんにちは") - es_post.set_translation("en", "Hello") - end - - it "does not preload translations when user locale matches site default locale as we assume most posts are written in default locale" do - SiteSetting.default_locale = "en" - I18n.locale = "en" - - topic_view = TopicView.new(topic) - serializer = TopicViewSerializer.new(topic_view, scope: Guardian.new(user), root: false) - - queries = track_sql_queries { serializer.as_json } - # has to manually load the es and ja post - expect(queries.count { |q| q.include?("discourse_translator_post_translations") }).to eq(2) - end - - it "does not preload translations when user locale is not site default and not in automatic_translation_target_languages" do - SiteSetting.default_locale = "en" - I18n.locale = "de" - - topic_view = TopicView.new(topic) - serializer = TopicViewSerializer.new(topic_view, scope: Guardian.new(user), root: false) - - queries = track_sql_queries { serializer.as_json } - - # english post is not loaded - expect(topic_view.posts.first.user_id).to eq en_post.user_id - expect(topic_view.posts.first.association(:translations)).not_to be_loaded - expect(queries.count { |q| q.include?("discourse_translator_post_translations") }).to eq(2) - end - - it "preloads translations when locales are different and in automatic_translation_target_languages" do - SiteSetting.default_locale = "en" - I18n.locale = "es" - - topic_view = TopicView.new(topic) - serializer = TopicViewSerializer.new(topic_view, scope: Guardian.new(user), root: false) - - topic_view.posts.reload - - queries = track_sql_queries { serializer.as_json } - - expect(queries.count { |q| q.include?("discourse_translator_post_translations") }).to eq(1) - expect(topic_view.posts.first.association(:translations)).to be_loaded - end - end - end - - describe "Inline translations" do - describe "#fancy_title" do - fab!(:user) { Fabricate(:user, locale: "ja") } - - let!(:original_title) { "

FUS ROH DAAHHH

" } - let!(:jap_title) { "

フス・ロ・ダ・ア

" } - - before do - topic.title = original_title - SiteSetting.experimental_inline_translation = true - I18n.locale = "en" - end - - def serialize_topic(guardian_user: user, cookie: "") - env = create_request_env.merge("HTTP_COOKIE" => cookie) - request = ActionDispatch::Request.new(env) - guardian = Guardian.new(guardian_user, request) - TopicViewSerializer.new(TopicView.new(topic), scope: guardian) - end - - it "does not replace fancy_title with translation when experimental_inline_translation is disabled" do - SiteSetting.experimental_inline_translation = false - topic.set_translation("ja", jap_title) - - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "does not replace fancy_title with translation when show_original param is present" do - topic.set_translation("ja", jap_title) - expect( - serialize_topic( - cookie: DiscourseTranslator::InlineTranslation::SHOW_ORIGINAL_COOKIE, - ).fancy_title, - ).to eq(topic.fancy_title) - end - - it "does not replace fancy_title with translation when no translation exists" do - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "does not replace fancy_title when topic is already in correct locale" do - I18n.locale = "ja" - topic.set_detected_locale("ja") - topic.set_translation("ja", jap_title) - - expect(serialize_topic.fancy_title).to eq(topic.fancy_title) - end - - it "returns translated title in fancy_title when translation exists for current locale" do - I18n.locale = "ja" - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - topic.set_translation("ja", jap_title) - expect(serialize_topic.fancy_title).to eq("<h1>フス・ロ・ダ・ア</h1>") - end - end - - describe "#show_translation_toggle" do - fab!(:user) - fab!(:post_1) { Fabricate(:post, topic:) } - fab!(:post_2) { Fabricate(:post, topic:) } - - before do - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - end - - def serialize_topic(guardian_user: user) - TopicViewSerializer.new(TopicView.new(topic), scope: Guardian.new(guardian_user)) - end - - it "returns depending on translator disabled or experimental inline translation disabled" do - I18n.locale = "ja" - topic.set_translation("ja", "こんにちは") - - SiteSetting.translator_enabled = false - SiteSetting.experimental_inline_translation = false - expect(serialize_topic.show_translation_toggle).to eq(false) - - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = false - expect(serialize_topic.show_translation_toggle).to eq(false) - - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - expect(serialize_topic.show_translation_toggle).to eq(true) - end - - it "returns true when there is translation for the topic" do - SiteSetting.translator_enabled = true - SiteSetting.experimental_inline_translation = true - I18n.locale = "ja" - topic.set_translation("ja", "こんにちは") - - expect(serialize_topic.show_translation_toggle).to eq(true) - end - end - end -end diff --git a/spec/system/anon_language_switcher_spec.rb b/spec/system/anon_language_switcher_spec.rb index cb598adb..8844b4d3 100644 --- a/spec/system/anon_language_switcher_spec.rb +++ b/spec/system/anon_language_switcher_spec.rb @@ -37,51 +37,4 @@ switcher.expand expect(switcher).not_to have_content("日本語") end - - describe "with Spanish and Japanese" do - before do - SiteSetting.automatic_translation_target_languages = "es|ja" - SiteSetting.experimental_anon_language_switcher = true - SiteSetting.experimental_inline_translation = true - - topic.set_detected_locale("en") - topic.set_translation("ja", "孫子兵法からの人生戦略") - topic.set_translation("es", "Estrategias de vida de El arte de la guerra") - end - - it "shows the correct language based on the selected language and login status" do - visit("/") - expect(find(".nav-item_latest")).to have_content("Latest") - - switcher.expand - switcher.click_button("Español") - expect(find(".nav-item_latest")).to have_content("Recientes") - - sign_in(japanese_user) - visit("/") - expect(find(".nav-item_latest")).to have_content("最新") - end - - it "shows the most recently selected language" do - visit("/") - - switcher.expand - switcher.click_button("Español") - - expect(find(".nav-item_latest")).to have_content("Recientes") - - topic_page.visit_topic(topic) - - expect(topic_page).to have_topic_title("Estrategias de vida de El arte de la guerra") - - switcher.expand - switcher.click_button("日本語") - - expect(topic_page).to have_topic_title("孫子兵法からの人生戦略") - - visit("/") - - expect(find(".nav-item_latest")).to have_content("最新") - end - end end diff --git a/spec/system/full_page_translation_spec.rb b/spec/system/full_page_translation_spec.rb deleted file mode 100644 index c51edf39..00000000 --- a/spec/system/full_page_translation_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "Inline translation", type: :system do - fab!(:japanese_user) { Fabricate(:user, locale: "ja") } - fab!(:site_local_user) { Fabricate(:user, locale: "en") } - fab!(:author) { Fabricate(:user) } - - fab!(:topic) { Fabricate(:topic, title: "Life strategies from The Art of War", user: author) } - fab!(:post_1) do - Fabricate(:post, topic: topic, raw: "The masterpiece isn’t just about military strategy") - end - fab!(:post_2) do - Fabricate(:post, topic: topic, raw: "The greatest victory is that which requires no battle") - end - fab!(:post_3) { Fabricate(:post, topic: topic, raw: "将とは、智・信・仁・勇・厳なり。") } - - let(:topic_page) { PageObjects::Pages::Topic.new } - let(:topic_list) { PageObjects::Components::TopicList.new } - - before do - # topic translation setup - topic.set_detected_locale("en") - post_1.set_detected_locale("en") - post_2.set_detected_locale("en") - post_3.set_detected_locale("ja") - - topic.set_translation("ja", "孫子兵法からの人生戦略") - topic.set_translation("es", "Estrategias de vida de El arte de la guerra") - post_1.set_translation("ja", "傑作は単なる軍事戦略についてではありません") - post_2.set_translation("ja", "最大の勝利は戦いを必要としないものです") - post_3.set_translation("en", "A general is one who possesses wisdom, sincerity...") - end - - context "when the feature is enabled" do - before do - SiteSetting.translator_enabled = true - SiteSetting.allow_user_locale = true - SiteSetting.set_locale_from_cookie = true - SiteSetting.set_locale_from_param = true - SiteSetting.experimental_inline_translation = true - SiteSetting.automatic_translation_backfill_rate = 1 - SiteSetting.automatic_translation_target_languages = "ja" - SiteSetting.experimental_anon_language_switcher = true - end - - it "shows the correct language based on the selected language and login status" do - visit("/t/#{topic.slug}/#{topic.id}?lang=ja") - expect(topic_page.has_topic_title?("孫子兵法からの人生戦略")).to eq(true) - expect(find(topic_page.post_by_number_selector(1))).to have_content("傑作は単なる軍事戦略についてではありません") - - visit("/t/#{topic.id}") - expect(topic_page.has_topic_title?("Life strategies from The Art of War")).to eq(true) - expect(find(topic_page.post_by_number_selector(1))).to have_content( - "The masterpiece isn’t just about military strategy", - ) - expect(find(topic_page.post_by_number_selector(3))).to have_css( - "div.post-translated-indicator", - ) - find("#{topic_page.post_by_number_selector(3)} .post-translated-indicator").hover - expect( - PageObjects::Components::Tooltips.new("discourse-translator_translated-post-indicator"), - ).to be_present(text: "This post was originally written in 日本語") - - sign_in(japanese_user) - visit("/") - visit("/t/#{topic.id}") - expect(topic_page.has_topic_title?("孫子兵法からの人生戦略")).to eq(true) - end - - it "shows original content when 'Show Original' is selected" do - sign_in(japanese_user) - - visit("/") - topic_list.visit_topic_with_title("孫子兵法からの人生戦略") - - expect(topic_page.has_topic_title?("孫子兵法からの人生戦略")).to eq(true) - page.find(".discourse-translator_toggle-original button").click - - expect(topic_page.has_topic_title?("Life strategies from The Art of War")).to eq(true) - - visit("/") - topic_list.visit_topic_with_title("Life strategies from The Art of War") - end - end -end diff --git a/test/javascripts/integration/translated-post-test.js b/test/javascripts/integration/translated-post-test.js index c677d329..80e0b387 100644 --- a/test/javascripts/integration/translated-post-test.js +++ b/test/javascripts/integration/translated-post-test.js @@ -17,7 +17,6 @@ module("Integration | Component | translated-post", function (hooks) { }, }); - this.siteSettings.experimental_inline_translation = false; this.siteSettings.translator_provider = "Google"; await render(hbs` @@ -29,23 +28,4 @@ module("Integration | Component | translated-post", function (hooks) { assert.dom(".post-attribution").hasText("Translated from ja by Google"); assert.dom(".cooked").hasText("こんにちは"); }); - - test("hides translation when experimental_inline_translation is enabled", async function (assert) { - this.set("outletArgs", { - post: { - isTranslated: true, - isTranslating: false, - translatedText: "Bonjour monde", - }, - }); - - this.siteSettings.experimental_inline_translation = true; - - await render(hbs` - - `); - - assert.dom(".topic-attribution").doesNotExist(); - assert.dom(".post-attribution").doesNotExist(); - }); }); diff --git a/test/javascripts/service/translator-test.js b/test/javascripts/service/translator-test.js index 06dcebfc..7a886d6a 100644 --- a/test/javascripts/service/translator-test.js +++ b/test/javascripts/service/translator-test.js @@ -28,57 +28,6 @@ module("Unit | Service | translator", function (hooks) { assert.strictEqual(post.translatedTitle, "Surprise!"); }); - test("translatePost - with experimental translation for first post", async function (assert) { - const service = this.owner.lookup("service:translator"); - - service.siteSettings.experimental_inline_translation = true; - - let headerUpdateCalled = false; - let postStreamRefreshCalled = false; - let titleSet = null; - - service.appEvents.on( - "header:update-topic", - () => (headerUpdateCalled = true) - ); - service.appEvents.on( - "post-stream:refresh", - () => (postStreamRefreshCalled = true) - ); - service.documentTitle.setTitle = (title) => (titleSet = title); - - pretender.post("/translator/translate", () => { - return response({ - detected_lang: "ja", - translation: "I am a cat", - title_translation: "Surprise!", - }); - }); - - const topic = { - set: function (key, value) { - this[key] = value; - }, - }; - const post = { - id: 1, - post_number: 1, - topic, - set: function (key, value) { - this[key] = value; - }, - }; - - await service.translatePost(post); - - assert.true(headerUpdateCalled); - assert.true(postStreamRefreshCalled); - assert.strictEqual(titleSet, "Surprise!"); - assert.strictEqual(post.cooked, "I am a cat"); - assert.false(post.can_translate); - assert.strictEqual(topic.fancy_title, "Surprise!"); - }); - test("clearPostTranslation", function (assert) { const service = this.owner.lookup("service:translator");