Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { popupAjaxError } from "discourse/lib/ajax-error";

export default class ToggleTranslationButton extends Component {
static shouldRender(args) {
return args.post.can_translate;
}

@service modal;
@service translator;

get isTranslating() {
return this.args.post.isTranslating;
}

get isTranslated() {
return this.args.post.isTranslated;
}

get title() {
if (this.isTranslating) {
return "translator.translating";
}

return this.isTranslated
? "translator.hide_translation"
: "translator.view_translation";
}

@action
hideTranslation() {
this.args.post.isTranslated = false;
this.args.post.isTranslating = false;
this.translator.clearPostTranslation(this.args.post);
}

@action
toggleTranslation() {
return this.args.post.isTranslated
? this.hideTranslation()
: this.translate();
}

@action
async translate() {
const post = this.args.post;
post.isTranslating = true;

try {
await this.translator.translatePost(post);
post.isTranslated = true;
} catch (error) {
this.translator.clearPostTranslation(this.args.post);
post.isTranslated = false;
popupAjaxError(error);
} finally {
post.isTranslating = false;
}
}

<template>
<DButton
class={{concatClass
"post-action-menu__translate"
(if this.isTranslated "translated")
}}
...attributes
@action={{this.toggleTranslation}}
@disabled={{this.isTranslating}}
@icon="globe"
@label={{if @showLabel this.title}}
@title={{this.title}}
/>
</template>
}
60 changes: 60 additions & 0 deletions assets/javascripts/discourse/components/translated-post.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import i18n from "discourse-common/helpers/i18n";

export default class TranslatedPost extends Component {
static shouldRender(args) {
return args.post.isTranslated || args.post.isTranslating;
}

@service siteSettings;

get loading() {
return this.post.isTranslating;
}

get isTranslated() {
return this.post.isTranslated;
}

get post() {
return this.args.outletArgs.post;
}

get translatedText() {
return this.post.translatedText;
}

get translatedTitle() {
return this.post.translatedTitle;
}

<template>
<div class="post-translation">
<ConditionalLoadingSpinner
class="post-translation"
@condition={{this.loading}}
@size="small"
>
<hr />
{{#if this.translatedTitle}}
<div class="topic-attribution">
{{this.translatedTitle}}
</div>
{{/if}}
<div class="post-attribution">
{{i18n
"translator.translated_from"
language=this.post.detectedLang
translator=this.siteSettings.translator
}}
</div>
<div class="cooked">
{{htmlSafe this.post.translatedText}}
</div>
</ConditionalLoadingSpinner>
</div>
</template>
}
9 changes: 0 additions & 9 deletions assets/javascripts/discourse/components/translated-post.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,39 +1,66 @@
import { tracked } from "@glimmer/tracking";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { withPluginApi } from "discourse/lib/plugin-api";
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
import I18n from "I18n";

function translatePost(post) {
return ajax("/translator/translate", {
type: "POST",
data: { post_id: post.get("id") },
}).then(function (res) {
post.setProperties({
translated_text: res.translation,
detected_lang: res.detected_lang,
translated_title: res.title_translation,
});
});
}
import ToggleTranslationButton from "../components/post-menu/toggle-translation-button";
import TranslatedPost from "../components/translated-post";

function initializeTranslation(api) {
const siteSettings = api.container.lookup("service:site-settings");
const currentUser = api.getCurrentUser();

if (!currentUser) {
if (!currentUser || !siteSettings.translator_enabled) {
return;
}
if (!siteSettings.translator_enabled) {
return;

customizePostMenu(api);
}

function customizePostMenu(api, container) {
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { firstButtonKey } }) => {
dag.add("translate", ToggleTranslationButton, { before: firstButtonKey });
}
);

if (transformerRegistered) {
// the plugin outlet is not updated when the post instance is modified unless we extend it to add the tracking to
// the new properties
api.modifyClass(
"model:post",
(Superclass) =>
class extends Superclass {
@tracked detectedLang;
@tracked isTranslating;
@tracked isTranslated;
@tracked translatedText;
@tracked translatedTitle;
}
);

api.renderBeforeWrapperOutlet("post-menu", TranslatedPost);
}

const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";

withSilencedDeprecations(silencedKey, () =>
customizeWidgetPostMenu(api, container)
);
}

function customizeWidgetPostMenu(api) {
api.includePostAttributes(
"can_translate",
"translated_text",
"detected_lang",
"translated_title"
);

const siteSettings = api.container.lookup("service:site-settings");
api.decorateWidget("post-menu:before", (dec) => {
if (!dec.state.isTranslated) {
return;
Expand Down Expand Up @@ -74,7 +101,17 @@ function initializeTranslation(api) {
const post = this.findAncestorModel();

if (post) {
return translatePost(post)
return ajax("/translator/translate", {
type: "POST",
data: { post_id: post.get("id") },
})
.then(function (res) {
post.setProperties({
translated_text: res.translation,
detected_lang: res.detected_lang,
translated_title: res.title_translation,
});
})
.catch((error) => {
popupAjaxError(error);
state.isTranslating = false;
Expand Down Expand Up @@ -114,6 +151,6 @@ function initializeTranslation(api) {
export default {
name: "extend-for-translate-button",
initialize() {
withPluginApi("0.1", (api) => initializeTranslation(api));
withPluginApi("1.34.0", (api) => initializeTranslation(api));
},
};
21 changes: 21 additions & 0 deletions assets/javascripts/discourse/services/translator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Service from "@ember/service";
import { ajax } from "discourse/lib/ajax";

export default class TranslatorService extends Service {
async translatePost(post) {
const response = await ajax("/translator/translate", {
type: "POST",
data: { post_id: post.id },
});

post.detectedLang = response.detected_lang;
post.translatedText = response.translation;
post.translatedTitle = response.title_translation;
}

clearPostTranslation(post) {
post.detectedLang = null;
post.translatedText = null;
post.translatedTitle = null;
}
}

This file was deleted.

This file was deleted.

1 change: 1 addition & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ en:
view_translation: "View translation"
hide_translation: "Hide translation"
translated_from: "Translated from %{language} by %{translator}"
translating: "Translating"