Skip to content

Commit 4ea54e1

Browse files
committed
FEATURE: Show full topic translations
1 parent e8614bd commit 4ea54e1

File tree

9 files changed

+180
-2
lines changed

9 files changed

+180
-2
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Component from "@glimmer/component";
2+
import { service } from "@ember/service";
3+
import { action } from "@ember/object";
4+
import DButton from "discourse/components/d-button";
5+
import concatClass from "discourse/helpers/concat-class";
6+
import { tracked } from "@glimmer/tracking";
7+
8+
export default class ShowOriginalContent extends Component {
9+
@service router;
10+
@tracked active = true;
11+
12+
constructor() {
13+
super(...arguments);
14+
this.active = !new URLSearchParams(window.location.search).has("show");
15+
}
16+
17+
@action
18+
async showOriginal() {
19+
this.active = !this.active;
20+
window.location.search = this.active ? "" : `show=original`;
21+
}
22+
23+
get title() {
24+
return this.active
25+
? "translator.hide_translation"
26+
: "translator.show_translation";
27+
}
28+
29+
<template>
30+
<div class="discourse-translator_toggle-original">
31+
<DButton
32+
@icon="language"
33+
@title={{this.title}}
34+
class={{concatClass "btn btn-default" (if this.active "active")}}
35+
@action={{this.showOriginal}}
36+
/>
37+
</div>
38+
</template>
39+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { i18n } from "discourse-i18n";
66
import LanguageSwitcher from "../components/language-switcher";
77
import ToggleTranslationButton from "../components/post-menu/toggle-translation-button";
88
import TranslatedPost from "../components/translated-post";
9+
import ShowOriginalContent from "../components/show-original-content";
910

1011
function initializeTranslation(api) {
1112
const siteSettings = api.container.lookup("service:site-settings");
@@ -22,8 +23,9 @@ function initializeTranslation(api) {
2223
{ before: ["search"] }
2324
);
2425
}
25-
26-
if (currentUser) {
26+
if (siteSettings.experimental_topic_translation) {
27+
api.renderInOutlet("topic-navigation", ShowOriginalContent);
28+
} else {
2729
customizePostMenu(api);
2830
}
2931
}

assets/stylesheets/common/common.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,19 @@
22
.fk-d-menu__inner-content {
33
max-height: 50vh;
44
}
5+
6+
.topic-navigation.with-timeline .discourse-translator_toggle-original {
7+
margin-bottom: 0.5em;
8+
}
9+
10+
.topic-navigation.with-topic-progress
11+
.discourse-translator_toggle-original
12+
button {
13+
height: 100%;
14+
}
15+
16+
.discourse-translator_toggle-original {
17+
button.active svg {
18+
color: var(--tertiary);
19+
}
20+
}

config/locales/server.en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ en:
2020
experimental_anon_language_switcher: "Enable experimental language switcher for anonymous users. This will allow anonymous users to switch between translated versions of Discourse and user-contributed content in topics."
2121
errors:
2222
set_locale_cookie_requirements: "The experimental language switcher for anonymous users requires the `set locale from cookie` site setting to be enabled."
23+
experimental_topic_translation: "Enable experimental topic translation feature. This replaces existing post in-line translation with a button that allows users to translate the entire topic."
2324
translator:
2425
failed: "The translator is unable to translate this content (%{source_locale}) to the default language of this site (%{target_locale})."
2526
not_supported: "This language is not supported by the translator."

config/settings.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,6 @@ discourse_translator:
106106
default: false
107107
client: true
108108
validator: "LanguageSwitcherSettingValidator"
109+
experimental_topic_translation:
110+
default: false
111+
client: true
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseTranslator
4+
module TranslatorHelper
5+
def self.translated_value(original_value, model, scope)
6+
return original_value if !SiteSetting.experimental_topic_translation
7+
return original_value if scope.request.params["show"] == "original"
8+
9+
translated = model.custom_fields[TRANSLATED_CUSTOM_FIELD]
10+
return original_value if (translated.blank? || translated[I18n.locale].blank?)
11+
12+
translated[I18n.locale]
13+
end
14+
end
15+
end

plugin.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,13 @@ module ::DiscourseTranslator
4747
add_to_serializer :post, :can_translate do
4848
scope.can_translate?(object)
4949
end
50+
51+
add_to_serializer :post, :cooked, respect_plugin_enabled: false do
52+
return super() if cooked_hidden
53+
DiscourseTranslator::TranslatorHelper.translated_value(super(), object, scope)
54+
end
55+
56+
add_to_serializer :basic_topic, :fancy_title do
57+
DiscourseTranslator::TranslatorHelper.translated_value(object.fancy_title, object, scope)
58+
end
5059
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
describe BasicTopicSerializer do
6+
let!(:guardian) { Guardian.new(user) }
7+
let!(:original_title) { "FUS ROH DAAHHH" }
8+
let!(:jap_title) { "フス・ロ・ダ・ア" }
9+
10+
describe "#fancy_title" do
11+
fab!(:user) { Fabricate(:user, locale: "ja") }
12+
fab!(:topic)
13+
14+
before do
15+
topic.title = original_title
16+
SiteSetting.experimental_topic_translation = true
17+
I18n.locale = "ja"
18+
end
19+
20+
def serialize_topic(guardian_user: user, params: {})
21+
env = { "action_dispatch.request.parameters" => params, "REQUEST_METHOD" => "GET" }
22+
request = ActionDispatch::Request.new(env)
23+
guardian = Guardian.new(guardian_user, request)
24+
BasicTopicSerializer.new(topic, scope: guardian)
25+
end
26+
27+
it "returns original fancy_title when experimental_topic_translation is disabled" do
28+
SiteSetting.experimental_topic_translation = false
29+
topic.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = { "ja" => jap_title }
30+
31+
expect(serialize_topic.fancy_title).to eq(original_title)
32+
end
33+
34+
it "returns original fancy_title when show_original param is present" do
35+
topic.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = { "ja" => jap_title }
36+
expect(serialize_topic(params: { "show" => "original" }).fancy_title).to eq(original_title)
37+
end
38+
39+
it "returns original fancy_title when no translation exists" do
40+
expect(serialize_topic.fancy_title).to eq(original_title)
41+
end
42+
43+
it "returns original fancy_title when translation is blank for current locale" do
44+
topic.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = { "ja" => "" }
45+
expect(serialize_topic.fancy_title).to eq(original_title)
46+
end
47+
48+
it "returns translated title when translation exists for current locale" do
49+
topic.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = { "ja" => jap_title }
50+
expect(serialize_topic.fancy_title).to eq(jap_title)
51+
end
52+
end
53+
end

spec/serializers/post_serializer_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,44 @@
8080
end
8181
end
8282
end
83+
84+
describe "#cooked" do
85+
def serialize_post(guardian_user: user, params: {})
86+
env = { "action_dispatch.request.parameters" => params, "REQUEST_METHOD" => "GET" }
87+
request = ActionDispatch::Request.new(env)
88+
guardian = Guardian.new(guardian_user, request)
89+
PostSerializer.new(post, scope: guardian)
90+
end
91+
92+
before { SiteSetting.experimental_topic_translation = true }
93+
94+
it "returns original cooked when experimental_topic_translation is disabled" do
95+
SiteSetting.experimental_topic_translation = false
96+
original_cooked = post.cooked
97+
expect(serialize_post.cooked).to eq(original_cooked)
98+
end
99+
100+
it "returns original cooked when show=original param is present" do
101+
original_cooked = post.cooked
102+
I18n.locale = "ja"
103+
post.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = { "ja" => "こんにちは" }
104+
expect(serialize_post(params: { "show" => "original" }).cooked).to eq(original_cooked)
105+
expect(serialize_post(params: { "show" => "derp" }).cooked).to eq("こんにちは")
106+
end
107+
108+
it "returns translated content based on locale" do
109+
I18n.locale = "ja"
110+
post.custom_fields[DiscourseTranslator::TRANSLATED_CUSTOM_FIELD] = {
111+
"ja" => "こんにちは",
112+
"es" => "Hola",
113+
}
114+
expect(serialize_post.cooked).to eq("こんにちは")
115+
end
116+
117+
it "returns original cooked when plugin is disabled" do
118+
SiteSetting.translator_enabled = false
119+
original_cooked = post.cooked
120+
expect(serialize_post.cooked).to eq(original_cooked)
121+
end
122+
end
83123
end

0 commit comments

Comments
 (0)