diff --git a/assets/javascripts/discourse/components/translated-post-indicator.gjs b/assets/javascripts/discourse/components/translated-post-indicator.gjs new file mode 100644 index 00000000..2dd97f1b --- /dev/null +++ b/assets/javascripts/discourse/components/translated-post-indicator.gjs @@ -0,0 +1,19 @@ +import Component from "@glimmer/component"; +import { i18n } from "discourse-i18n"; +import DTooltip from "float-kit/components/d-tooltip"; + +export default class TranslatedPostIndicator extends Component { + get tooltip() { + return i18n("translator.originally_written_in", { + language: this.args.data.detectedLanguage, + }); + } + + +} diff --git a/assets/javascripts/discourse/initializers/extend-for-translate-button.js b/assets/javascripts/discourse/initializers/extend-for-translate-button.js index c42cb6c7..d194f134 100644 --- a/assets/javascripts/discourse/initializers/extend-for-translate-button.js +++ b/assets/javascripts/discourse/initializers/extend-for-translate-button.js @@ -1,8 +1,10 @@ import { withPluginApi } from "discourse/lib/plugin-api"; +import RenderGlimmer from "discourse/widgets/render-glimmer"; import LanguageSwitcher from "../components/language-switcher"; import ToggleTranslationButton from "../components/post-menu/toggle-translation-button"; import ShowOriginalContent from "../components/show-original-content"; import TranslatedPost from "../components/translated-post"; +import TranslatedPostIndicator from "../components/translated-post-indicator"; function initializeTranslation(api) { const siteSettings = api.container.lookup("service:site-settings"); @@ -46,6 +48,18 @@ function initializeTranslation(api) { }); } ); + + 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/stylesheets/common/common.scss b/assets/stylesheets/common/common.scss index 92f96653..185fcbb0 100644 --- a/assets/stylesheets/common/common.scss +++ b/assets/stylesheets/common/common.scss @@ -22,3 +22,11 @@ color: var(--tertiary); } } + +.post-info .post-info.post-translated-indicator { + display: inline; + + svg { + color: var(--primary-med-or-secondary-med); + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2df6164e..c51e8093 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -12,3 +12,4 @@ en: content_translated: "Page is machine-translated. Click to view original" translated_from: "Translated from %{language} by %{translator}" translating: "Translating" + originally_written_in: "This post was originally written in %{language}" diff --git a/lib/discourse_translator/inline_translation.rb b/lib/discourse_translator/inline_translation.rb index f39a8027..fd7d93b5 100644 --- a/lib/discourse_translator/inline_translation.rb +++ b/lib/discourse_translator/inline_translation.rb @@ -63,9 +63,16 @@ def inject(plugin) 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, :is_translated) do SiteSetting.experimental_inline_translation && !object.topic.locale_matches?(InlineTranslation.effective_locale) && diff --git a/lib/discourse_translator/locale_to_language.rb b/lib/discourse_translator/locale_to_language.rb new file mode 100644 index 00000000..6e9aa210 --- /dev/null +++ b/lib/discourse_translator/locale_to_language.rb @@ -0,0 +1,9 @@ +# 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/spec/lib/locale_to_language_spec.rb b/spec/lib/locale_to_language_spec.rb new file mode 100644 index 00000000..392bd1d3 --- /dev/null +++ b/spec/lib/locale_to_language_spec.rb @@ -0,0 +1,18 @@ +# 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/system/full_page_translation_spec.rb b/spec/system/full_page_translation_spec.rb index 1a914ebd..c51edf39 100644 --- a/spec/system/full_page_translation_spec.rb +++ b/spec/system/full_page_translation_spec.rb @@ -12,6 +12,7 @@ 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 } @@ -21,11 +22,13 @@ 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 @@ -37,6 +40,7 @@ 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 @@ -49,6 +53,13 @@ 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("/")