diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index daa34de4b8..aac7cf8b55 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -65,14 +65,13 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE - Feat: 리버시 대전 중에 상대방에게 리액션을 보낼 수 있음 (misskey-dev/misskey#13119) - Feat: 자동 번역 기능 - 자동 번역은 번역 서비스의 API 제한을 방지하기 위해 자동으로 활성화되지 않으며, 기본적으로 비활성화되어 있습니다. - - `역할`에서 `자동 번역 기능 이용 가능 여부`를 활성화 하면 자동 번역을 사용할 수 있는 상태가 됩니다. - - 역할별로 기능 사용 가능 유무를 설정할 수 있습니다. + - `역할`에서 `자동 번역 기능 이용`를 활성화 하면 자동 번역을 사용할 수 있는 상태가 됩니다. + - 자동 번역 사용 권한을 역할별로 설정할 수 있습니다. - 이후, 각 사용자별로 `설정` - `일반`에서 `자동 번역`을 활성화한 사용자는 자동 번역을 사용할 수 있습니다. - 노트가 아래와 같이 설정된 경우에는 자동 번역을 사용하지 않습니다. - 노트가 `내용 가리기`로 설정되어 있음 - 노트의 내용이 긺 - 노트에 5개 이상의 파일이 포함되어 있음 - - `자동 번역 기능 이용 가능 여부` 역할의 권한을 상실하게 되면 모든 사용자의 `자동 번역` 설정도 자동으로 비활성화 됩니다. ### Client - Enhance: CherryPick 업데이트 페이지를 제어판 목록에 추가함 diff --git a/locales/en-US.yml b/locales/en-US.yml index c9b495531d..f6838d9a6c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2,7 +2,8 @@ _lang_: "English" useAutoTranslate: "Automatic translation" useAutoTranslateDescription: "Enabling the automatic translation feature automatically translates all notes in the timeline, which could potentially result in the API restriction policy set by the translation service provider temporarily disabling the translation feature.\n\nDo you still want to activate it?" -cantUseAutoTranslateDescription: "The server administrator has disabled this feature.\nContact your server administrator to use the feature." +cantUseAutoTranslateDescription: "The server administrator has disabled automatic translation.\nPlease contact your server administrator to enable automatic translation." +cantUseAutoTranslateCaption: "When enabled, automatic translation will be applied when available again." widgets: "Widgets" postNote: "Post note" bottomNavbar: "Bottom navigation bar" diff --git a/locales/index.d.ts b/locales/index.d.ts index 82d0872271..b889a5d166 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -24,10 +24,14 @@ export interface Locale extends ILocale { */ "useAutoTranslateDescription": string; /** - * サーバー管理者がこの機能を無効にしました。 - * 機能を使用するには、サーバー管理者にお問い合わせください。 + * サーバー管理者が自動翻訳を使用できないように設定しました。 + * 自動翻訳を使用するには、サーバー管理者にお問い合わせください。 */ "cantUseAutoTranslateDescription": string; + /** + * 有効にすると、再利用できるときに自動翻訳を適用します。 + */ + "cantUseAutoTranslateCaption": string; /** * ウィジェット */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e1d80ec4a0..0585e62ec6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2,7 +2,8 @@ _lang_: "日本語" useAutoTranslate: "自動翻訳" useAutoTranslateDescription: "自動翻訳機能を有効にすると、タイムラインのすべてのノートが自動的に翻訳され、これにより翻訳サービス提供者が設定したAPI制限ポリシーにより、翻訳機能を一時的に使用できなくなる可能性があります。\n\nそれでも続けましょうか?" -cantUseAutoTranslateDescription: "サーバー管理者がこの機能を無効にしました。\n機能を使用するには、サーバー管理者にお問い合わせください。" +cantUseAutoTranslateDescription: "サーバー管理者が自動翻訳を使用できないように設定しました。\n自動翻訳を使用するには、サーバー管理者にお問い合わせください。" +cantUseAutoTranslateCaption: "有効にすると、再利用できるときに自動翻訳を適用します。" widgets: "ウィジェット" postNote: "ノートを作成" bottomNavbar: "下のナビゲーションバー" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 8dc9336323..ae8ce65664 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -2,7 +2,8 @@ _lang_: "한국어" useAutoTranslate: "자동 번역" useAutoTranslateDescription: "자동 번역 기능을 활성화하면 타임라인의 모든 노트가 자동으로 번역되며, 이로 인해 번역 서비스 제공자가 설정한 API 제한 정책에 의해 번역 기능을 일시적으로 사용하지 못하게 될 가능성이 있어요.\n\n그래도 활성화 하시겠어요?" -cantUseAutoTranslateDescription: "서버 관리자가 이 기능을 사용할 수 없도록 설정했어요.\n기능을 사용하려면 서버 관리자에게 문의해 주세요." +cantUseAutoTranslateDescription: "서버 관리자가 자동 번역을 사용할 수 없도록 설정했어요.\n자동 번역을 사용하려면 서버 관리자에게 문의해 주세요." +cantUseAutoTranslateCaption: "활성화하면 다시 사용할 수 있을 때 자동 번역을 적용해요." widgets: "위젯" postNote: "노트 작성" bottomNavbar: "하단 내비게이션 바" @@ -1992,9 +1993,9 @@ _role: rateLimitFactor: "요청 빈도 제한" descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화돼요." canHideAds: "광고 숨기기" - canSearchNotes: "노트 검색 이용 가능 여부" - canUseTranslator: "번역 기능 이용 가능 여부" - canUseAutoTranslate: "자동 번역 기능 이용 가능 여부" + canSearchNotes: "노트 검색 이용" + canUseTranslator: "번역 기능 이용" + canUseAutoTranslate: "자동 번역 기능 이용" canUseAutoTranslateDescription: "자동 번역 기능을 활성화한 사용자는 타임라인의 모든 노트가 자동으로 번역되며, 이로 인해 번역 서비스 제공자가 설정한 API 제한에 매우 빠르게 도달해 번역 기능을 일시적으로 사용하지 못하게 될 가능성이 있어요.\n이는 서버 내 모든 사용자가 API를 일시적으로 사용하지 못하게 될 수도 있다는 것을 의미해요.\n또한 번역 서비스 제공자에 따라 API 사용으로 인한 요금이 과도하게 발생할 수 있어요.\n\n그래도 활성화 하시겠어요?" avatarDecorationLimit: "최대로 붙일 수 있는 아바타 장식 개수" canImportAntennas: "안테나 가져오기 허용" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 345b0650c1..ada3c73be9 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" /> -
+
@@ -713,7 +713,7 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, collapsed, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -723,7 +723,7 @@ function showMenu(): void { return; } - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, collapsed, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } @@ -741,8 +741,7 @@ const isForeignLanguage: boolean = appearNote.value.text != null && (() => { return postLang !== '' && postLang !== targetLang; })(); -if (defaultStore.state.useAutoTranslate && !$i.policies.canUseAutoTranslate) defaultStore.set('useAutoTranslate', false); -if ($i.policies.canUseTranslator && defaultStore.state.useAutoTranslate && !isLong && (appearNote.value.cw == null || showContent.value) && appearNote.value.text && isForeignLanguage) translate(); +if (defaultStore.state.useAutoTranslate && instance.translatorAvailable && $i.policies.canUseTranslator && $i.policies.canUseAutoTranslate && !isLong && (appearNote.value.cw == null || showContent.value) && appearNote.value.text && isForeignLanguage) translate(); async function translate(): Promise { if (translation.value != null) return; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 70fbb5d1f1..6cfd440009 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -115,7 +115,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenuReaction="true" /> RN: -
+
@@ -681,8 +681,7 @@ const isForeignLanguage: boolean = appearNote.value.text != null && (() => { return postLang !== '' && postLang !== targetLang; })(); -if (defaultStore.state.useAutoTranslate && !$i.policies.canUseAutoTranslate) defaultStore.set('useAutoTranslate', false); -if ($i.policies.canUseTranslator && defaultStore.state.useAutoTranslate && (appearNote.value.cw == null || showContent.value) && appearNote.value.text && isForeignLanguage) translate(); +if (defaultStore.state.useAutoTranslate && instance.translatorAvailable && $i.policies.canUseTranslator && $i.policies.canUseAutoTranslate && (appearNote.value.cw == null || showContent.value) && appearNote.value.text && isForeignLanguage) translate(); async function translate(): Promise { if (translation.value != null) return; diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 44fe4f34eb..1427915b20 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenuReaction="true" /> RN: ... -
+
@@ -423,7 +423,7 @@ function showMenu(): void { return; } - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, collapsed, translating, translation, viewTextSource, noNyaize, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } @@ -441,8 +441,7 @@ const isForeignLanguage: boolean = note.value.text != null && (() => { return postLang !== '' && postLang !== targetLang; })(); -if (defaultStore.state.useAutoTranslate && !$i.policies.canUseAutoTranslate) defaultStore.set('useAutoTranslate', false); -if ($i.policies.canUseTranslator && defaultStore.state.useAutoTranslate && !isLong && (note.value.cw == null || showContent.value) && note.value.text && isForeignLanguage) translate(); +if (defaultStore.state.useAutoTranslate && instance.translatorAvailable && $i.policies.canUseTranslator && $i.policies.canUseAutoTranslate && !isLong && (note.value.cw == null || showContent.value) && note.value.text && isForeignLanguage) translate(); async function translate(): Promise { if (translation.value != null) return; diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 5f0f399667..607fd52370 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -231,6 +231,7 @@ export function alert(props: { type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; title?: string; text?: string; + caption?: string | null; }): Promise { return new Promise(resolve => { const { dispose } = popup(MkDialog, props, { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 4313482f13..2f52d83f36 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -51,9 +51,9 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.enableHorizontalSwipe }} {{ i18n.ts.alwaysConfirmFollow }} {{ i18n.ts.confirmWhenRevealingSensitiveMedia }} - + {{ i18n.ts.useAutoTranslate }} CherryPick - +
@@ -303,6 +303,15 @@ async function learnMoreAutoTranslate() { if (confirm.canceled) useAutoTranslate.value = false; } +function learnMoreCantUseAutoTranslate() { + os.alert({ + type: 'info', + title: i18n.ts.useAutoTranslate, + text: i18n.ts.cantUseAutoTranslateDescription, + caption: i18n.ts.cantUseAutoTranslateCaption, + }); +} + watch(dataSaver, (to) => { defaultStore.set('dataSaver', to); }, { diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 975768809a..5fce5c0c85 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -178,6 +178,7 @@ function getNoteEmbedCodeMenu(note: Misskey.entities.Note, text: string): MenuIt export function getNoteMenu(props: { note: Misskey.entities.Note; + collapsed?: Ref; translation: Ref; translating: Ref; viewTextSource: Ref; @@ -358,6 +359,7 @@ export function getNoteMenu(props: { async function translate(): Promise { if (props.translation.value != null) return; + if (props.collapsed?.value != null) props.collapsed.value = false; props.translating.value = true; const res = await misskeyApi('notes/translate', { noteId: appearNote.id, @@ -424,7 +426,7 @@ export function getNoteMenu(props: { }); const isLong = shouldCollapsed(appearNote, []); - if ($i.policies.canUseTranslator && instance.translatorAvailable && (!defaultStore.state.useAutoTranslate || (defaultStore.state.useAutoTranslate && isLong))) { + if ($i.policies.canUseTranslator && instance.translatorAvailable && (!defaultStore.state.useAutoTranslate || (defaultStore.state.useAutoTranslate && !$i.policies.canUseAutoTranslate && (isLong || appearNote.cw != null)))) { menuItems.push({ icon: 'ti ti-language-hiragana', text: i18n.ts.translate,