diff --git a/locales/index.d.ts b/locales/index.d.ts index 7eebd65a0d..b516e742b9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -383,6 +383,10 @@ export interface Locale extends ILocale { * ノートを翻訳する */ "translateNote": string; + /** + * 投票を翻訳する + */ + "translatePoll": string; /** * ノート本文に翻訳ボタンを表示 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index cb27e3dfd4..2eaf8afa0e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -90,6 +90,7 @@ inviteRevokeConfirm: "本当に全ての招待コードを失効させますか enableAbsoluteTime: "絶対時刻表記を使用する" posted: "ノートを公開しました。" translateNote: "ノートを翻訳する" +translatePoll: "投票を翻訳する" showTranslateButtonInNote: "ノート本文に翻訳ボタンを表示" editNickName: "ニックネームを編集" hideAvatarsInNote: "ノートでアイコンを隠す" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index a94bfb1c29..15146dc5d6 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -110,7 +110,6 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenuReaction="!!$i" @click.stop /> -
@@ -128,6 +127,21 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ + +
+
+ +
+ {{ i18n.tsx.translatedFrom({ x: pollTranslation.sourceLang }) }}:
+ +
+ + +
+
+
@@ -355,7 +369,9 @@ const isDeleted = ref(false); const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true)); const translation = ref(null); +const pollTranslation = ref(null); const translating = ref(false); +const pollTranslating = ref(false); const viewTextSource = ref(false); const noNyaize = ref(false); const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); @@ -757,9 +773,15 @@ async function clip(): Promise { const isForeignLanguage: boolean = appearNote.value.text != null && (() => { const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); const postLang = detectLanguage(appearNote.value.text); - const choicesLang = appearNote.value.poll?.choices.map((choice) => choice.text).join(' ') ?? ''; - const pollLang = detectLanguage(choicesLang); - return postLang !== '' && (postLang !== targetLang || pollLang !== targetLang); + return postLang !== '' && postLang !== targetLang; +})(); + +const pollIsForeignLanguage: boolean = appearNote.value.poll != null && (() => { + const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); + const langs = appearNote.value.poll.choices + .map((choice) => detectLanguage(choice.text)) + .filter((lang) => lang != targetLang).length; + return langs != 0; })(); if (defaultStore.state.useAutoTranslate && instance.translatorAvailable && $i.policies.canUseTranslator && $i.policies.canUseAutoTranslate && !isLong && (appearNote.value.cw == null || showContent.value) && appearNote.value.text && isForeignLanguage) translate(); @@ -793,6 +815,34 @@ async function translate(): Promise { vibrate(defaultStore.state.vibrateSystem ? [5, 5, 10] : []); } +async function pollTranslate(): Promise { + if (pollTranslation.value != null) return; + collapsed.value = false; + pollTranslating.value = true; + + vibrate(defaultStore.state.vibrateSystem ? 5 : []); + + if (props.mock) { + return; + } + const res = await misskeyApi('notes/polls/translate', { + noteId: appearNote.value.id, + targetLang: miLocalStorage.getItem('lang') ?? navigator.language, + }).catch((err) => { + translating.value = false; + os.alert( + { + type: 'error', + title: err.message, + text: err.id, + }); + }); + pollTranslating.value = false; + pollTranslation.value = res; + + vibrate(defaultStore.state.vibrateSystem ? [5, 5, 10] : []); +} + function showRenoteMenu(): void { if (props.mock) { return;