Skip to content

Commit 43e2803

Browse files
authored
FEATURE: Show an indicator for posts not originally written in the user's language (#274)
This commit adds a little indicator in each post that shares the original post language. It only shows up when experimental_inline_translation is enabled, and if the post's language is not matching the user's locale. We're currently using the LocaleSiteSetting values to determine the name of the language, so we will get values like "Español" instead of "Spanish". In the future we will consider localized language names (internal /151409)
1 parent 2c05a85 commit 43e2803

File tree

8 files changed

+87
-0
lines changed

8 files changed

+87
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Component from "@glimmer/component";
2+
import { i18n } from "discourse-i18n";
3+
import DTooltip from "float-kit/components/d-tooltip";
4+
5+
export default class TranslatedPostIndicator extends Component {
6+
get tooltip() {
7+
return i18n("translator.originally_written_in", {
8+
language: this.args.data.detectedLanguage,
9+
});
10+
}
11+
12+
<template>
13+
<DTooltip
14+
@identifier="discourse-translator_translated-post-indicator"
15+
@icon="language"
16+
@content={{this.tooltip}}
17+
/>
18+
</template>
19+
}

assets/javascripts/discourse/initializers/extend-for-translate-button.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { withPluginApi } from "discourse/lib/plugin-api";
2+
import RenderGlimmer from "discourse/widgets/render-glimmer";
23
import LanguageSwitcher from "../components/language-switcher";
34
import ToggleTranslationButton from "../components/post-menu/toggle-translation-button";
45
import ShowOriginalContent from "../components/show-original-content";
56
import TranslatedPost from "../components/translated-post";
7+
import TranslatedPostIndicator from "../components/translated-post-indicator";
68

79
function initializeTranslation(api) {
810
const siteSettings = api.container.lookup("service:site-settings");
@@ -46,6 +48,18 @@ function initializeTranslation(api) {
4648
});
4749
}
4850
);
51+
52+
api.includePostAttributes("is_translated", "detected_language");
53+
api.decorateWidget("post-date:before", (dec) => {
54+
if (dec.attrs.is_translated && dec.attrs.detected_language) {
55+
return new RenderGlimmer(
56+
dec.widget,
57+
"div.post-info.post-translated-indicator",
58+
TranslatedPostIndicator,
59+
{ detectedLanguage: dec.attrs.detected_language }
60+
);
61+
}
62+
});
4963
}
5064

5165
customizePostMenu(api);

assets/stylesheets/common/common.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,11 @@
2222
color: var(--tertiary);
2323
}
2424
}
25+
26+
.post-info .post-info.post-translated-indicator {
27+
display: inline;
28+
29+
svg {
30+
color: var(--primary-med-or-secondary-med);
31+
}
32+
}

config/locales/client.en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ en:
1212
content_translated: "Page is machine-translated. Click to view original"
1313
translated_from: "Translated from %{language} by %{translator}"
1414
translating: "Translating"
15+
originally_written_in: "This post was originally written in %{language}"

lib/discourse_translator/inline_translation.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,16 @@ def inject(plugin)
6363
plugin.add_to_serializer(:basic_post, :is_translated) do
6464
SiteSetting.experimental_inline_translation &&
6565
!object.locale_matches?(InlineTranslation.effective_locale) &&
66+
!scope&.request&.cookies&.key?(SHOW_ORIGINAL_COOKIE) &&
6667
object.translation_for(InlineTranslation.effective_locale).present?
6768
end
6869

70+
plugin.add_to_serializer(:basic_post, :detected_language) do
71+
if SiteSetting.experimental_inline_translation && object.detected_locale.present?
72+
LocaleToLanguage.get_language(object.detected_locale)
73+
end
74+
end
75+
6976
plugin.add_to_serializer(:topic_view, :is_translated) do
7077
SiteSetting.experimental_inline_translation &&
7178
!object.topic.locale_matches?(InlineTranslation.effective_locale) &&
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseTranslator
4+
class LocaleToLanguage
5+
def self.get_language(locale)
6+
LocaleSiteSetting.values.find { |v| v[:value] == locale.to_s.sub("-", "_") }&.[](:name)
7+
end
8+
end
9+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
describe DiscourseTranslator::LocaleToLanguage do
4+
describe ".get_language" do
5+
it "returns the language name for a valid locale" do
6+
expect(DiscourseTranslator::LocaleToLanguage.get_language("en")).to eq("English (US)")
7+
expect(DiscourseTranslator::LocaleToLanguage.get_language("es")).to eq("Español")
8+
end
9+
10+
it "returns nil for a locale that doesn't exist" do
11+
expect(DiscourseTranslator::LocaleToLanguage.get_language("xx")).to be_nil
12+
end
13+
14+
it "handles symbol locales" do
15+
expect(DiscourseTranslator::LocaleToLanguage.get_language(:en_GB)).to eq("English (UK)")
16+
end
17+
end
18+
end

spec/system/full_page_translation_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
fab!(:post_2) do
1313
Fabricate(:post, topic: topic, raw: "The greatest victory is that which requires no battle")
1414
end
15+
fab!(:post_3) { Fabricate(:post, topic: topic, raw: "将とは、智・信・仁・勇・厳なり。") }
1516

1617
let(:topic_page) { PageObjects::Pages::Topic.new }
1718
let(:topic_list) { PageObjects::Components::TopicList.new }
@@ -21,11 +22,13 @@
2122
topic.set_detected_locale("en")
2223
post_1.set_detected_locale("en")
2324
post_2.set_detected_locale("en")
25+
post_3.set_detected_locale("ja")
2426

2527
topic.set_translation("ja", "孫子兵法からの人生戦略")
2628
topic.set_translation("es", "Estrategias de vida de El arte de la guerra")
2729
post_1.set_translation("ja", "傑作は単なる軍事戦略についてではありません")
2830
post_2.set_translation("ja", "最大の勝利は戦いを必要としないものです")
31+
post_3.set_translation("en", "A general is one who possesses wisdom, sincerity...")
2932
end
3033

3134
context "when the feature is enabled" do
@@ -37,6 +40,7 @@
3740
SiteSetting.experimental_inline_translation = true
3841
SiteSetting.automatic_translation_backfill_rate = 1
3942
SiteSetting.automatic_translation_target_languages = "ja"
43+
SiteSetting.experimental_anon_language_switcher = true
4044
end
4145

4246
it "shows the correct language based on the selected language and login status" do
@@ -49,6 +53,13 @@
4953
expect(find(topic_page.post_by_number_selector(1))).to have_content(
5054
"The masterpiece isn’t just about military strategy",
5155
)
56+
expect(find(topic_page.post_by_number_selector(3))).to have_css(
57+
"div.post-translated-indicator",
58+
)
59+
find("#{topic_page.post_by_number_selector(3)} .post-translated-indicator").hover
60+
expect(
61+
PageObjects::Components::Tooltips.new("discourse-translator_translated-post-indicator"),
62+
).to be_present(text: "This post was originally written in 日本語")
5263

5364
sign_in(japanese_user)
5465
visit("/")

0 commit comments

Comments
 (0)