-
-
{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}:
+
+ {{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}:
+
{{ i18n.ts.translationFailed }}
@@ -151,7 +152,7 @@ const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null;
const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null;
const showContent = ref(false);
-const translation = ref(null);
+const translation = ref(null);
const translating = ref(false);
const urls = appearNote.value.text ? extractUrlFromMfm(mfm.parse(appearNote.value.text)).filter(u => u !== renoteUrl && u !== renoteUri) : null;
const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && appearNote.value.user.instance);
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index ff366d699f..a537b6e837 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -271,6 +271,14 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
+
+ {{ i18n.ts.showTranslationButtonInNoteFooter }}
+
+
+
+
@@ -964,6 +972,7 @@ const serverDisconnectedBehavior = prefer.model('serverDisconnectedBehavior');
const hemisphere = prefer.model('hemisphere');
const showNoteActionsOnlyHover = prefer.model('showNoteActionsOnlyHover');
const showClipButtonInNoteFooter = prefer.model('showClipButtonInNoteFooter');
+const showTranslationButtonInNoteFooter = prefer.model('showTranslationButtonInNoteFooter');
const collapseRenotes = prefer.model('collapseRenotes');
const advancedMfm = prefer.model('advancedMfm');
const showReactionsCount = prefer.model('showReactionsCount');
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 277508d79d..a4d52c8acb 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -248,6 +248,9 @@ export const PREF_DEF = {
showClipButtonInNoteFooter: {
default: false,
},
+ showTranslationButtonInNoteFooter: {
+ default: false,
+ },
reactionsDisplaySize: {
default: 'medium' as 'small' | 'medium' | 'large',
},
diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts
index 0326ebdac8..f773149fac 100644
--- a/packages/frontend/src/utility/get-note-menu.ts
+++ b/packages/frontend/src/utility/get-note-menu.ts
@@ -176,7 +176,7 @@ function getNoteEmbedCodeMenu(note: Misskey.entities.Note, text: string): MenuIt
export function getNoteMenu(props: {
note: Misskey.entities.Note;
- translation: Ref;
+ translation: Ref;
translating: Ref;
isDeleted: Ref;
currentClip?: Misskey.entities.Clip;
@@ -290,17 +290,6 @@ export function getNoteMenu(props: {
os.pageWindow(`/notes/${appearNote.id}`);
}
- async function translate(): Promise {
- if (props.translation.value != null) return;
- props.translating.value = true;
- props.translation.value = await misskeyApi('notes/translate', {
- noteId: appearNote.id,
- targetLang: miLocalStorage.getItem('lang') ?? navigator.language,
- }).finally(() => {
- props.translating.value = false;
- });
- }
-
const menuItems: MenuItem[] = [];
if ($i) {
@@ -357,7 +346,7 @@ export function getNoteMenu(props: {
menuItems.push({
icon: 'ti ti-language-hiragana',
text: i18n.ts.translate,
- action: translate,
+ action: () => translateNote(appearNote.id, props.translation, props.translating),
});
}
@@ -697,3 +686,20 @@ export function getRenoteMenu(props: {
menu: renoteItems,
};
}
+
+export async function translateNote(noteId: string, translation: Ref, translating: Ref): Promise {
+ if (translating.value || translation.value) return;
+ translating.value = true;
+ try {
+ const targetLang = miLocalStorage.getItem('lang') ?? navigator.language;
+ translation.value = await misskeyApi('notes/translate', {
+ noteId,
+ targetLang,
+ });
+ } catch (err) {
+ console.error(`Translation failed for ${noteId}: `, err);
+ translation.value = false;
+ } finally {
+ translating.value = false;
+ }
+}
diff --git a/sharkey-locales/en-US.yml b/sharkey-locales/en-US.yml
index bf746533af..d2e28b9ddd 100644
--- a/sharkey-locales/en-US.yml
+++ b/sharkey-locales/en-US.yml
@@ -523,6 +523,8 @@ id: "ID"
mandatoryCW: "Force content warning"
mandatoryCWDescription: "Applies a content warning to all posts created by this user. If the post already has a CW, then this is appended to the end."
fetchLinkedNote: "Fetch linked note"
+showTranslationButtonInNoteFooter: "Add \"Translate\" to note action menu"
+translationFailed: "Failed to translate note. Please try again later or contact an administrator for assistance."
_processErrors:
quoteUnavailable: "Unable to process quote. This post may be missing context."