1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2025-01-22 17:54:05 +09:00
This commit is contained in:
NoriDev 2024-10-07 19:44:36 +09:00
parent 4e00de6e83
commit b553610712
11 changed files with 41 additions and 26 deletions

View File

@ -65,14 +65,13 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE
- Feat: 리버시 대전 중에 상대방에게 리액션을 보낼 수 있음 (misskey-dev/misskey#13119)
- Feat: 자동 번역 기능
- 자동 번역은 번역 서비스의 API 제한을 방지하기 위해 자동으로 활성화되지 않으며, 기본적으로 비활성화되어 있습니다.
- `역할`에서 `자동 번역 기능 이용 가능 여부`를 활성화 하면 자동 번역을 사용할 수 있는 상태가 됩니다.
- 역할별로 기능 사용 가능 유무를 설정할 수 있습니다.
- `역할`에서 `자동 번역 기능 이용`를 활성화 하면 자동 번역을 사용할 수 있는 상태가 됩니다.
- 자동 번역 사용 권한을 역할별로 설정할 수 있습니다.
- 이후, 각 사용자별로 `설정` - `일반`에서 `자동 번역`을 활성화한 사용자는 자동 번역을 사용할 수 있습니다.
- 노트가 아래와 같이 설정된 경우에는 자동 번역을 사용하지 않습니다.
- 노트가 `내용 가리기`로 설정되어 있음
- 노트의 내용이 긺
- 노트에 5개 이상의 파일이 포함되어 있음
- `자동 번역 기능 이용 가능 여부` 역할의 권한을 상실하게 되면 모든 사용자의 `자동 번역` 설정도 자동으로 비활성화 됩니다.
### Client
- Enhance: CherryPick 업데이트 페이지를 제어판 목록에 추가함

View File

@ -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\n<b>Do you still want to activate it?</b>"
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"

8
locales/index.d.ts vendored
View File

@ -24,10 +24,14 @@ export interface Locale extends ILocale {
*/
"useAutoTranslateDescription": string;
/**
*
* 使
* 使
* 使
*/
"cantUseAutoTranslateDescription": string;
/**
*
*/
"cantUseAutoTranslateCaption": string;
/**
*
*/

View File

@ -2,7 +2,8 @@ _lang_: "日本語"
useAutoTranslate: "自動翻訳"
useAutoTranslateDescription: "自動翻訳機能を有効にすると、タイムラインのすべてのートが自動的に翻訳され、これにより翻訳サービス提供者が設定したAPI制限ポリシーにより、翻訳機能を一時的に使用できなくなる可能性があります。\n\n<b>それでも続けましょうか?</b>"
cantUseAutoTranslateDescription: "サーバー管理者がこの機能を無効にしました。\n機能を使用するには、サーバー管理者にお問い合わせください。"
cantUseAutoTranslateDescription: "サーバー管理者が自動翻訳を使用できないように設定しました。\n自動翻訳を使用するには、サーバー管理者にお問い合わせください。"
cantUseAutoTranslateCaption: "有効にすると、再利用できるときに自動翻訳を適用します。"
widgets: "ウィジェット"
postNote: "ノートを作成"
bottomNavbar: "下のナビゲーションバー"

View File

@ -2,7 +2,8 @@
_lang_: "한국어"
useAutoTranslate: "자동 번역"
useAutoTranslateDescription: "자동 번역 기능을 활성화하면 타임라인의 모든 노트가 자동으로 번역되며, 이로 인해 번역 서비스 제공자가 설정한 API 제한 정책에 의해 번역 기능을 일시적으로 사용하지 못하게 될 가능성이 있어요.\n\n<b>그래도 활성화 하시겠어요?</b>"
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<b>그래도 활성화 하시겠어요?</b>"
avatarDecorationLimit: "최대로 붙일 수 있는 아바타 장식 개수"
canImportAntennas: "안테나 가져오기 허용"

View File

@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
/>
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (defaultStore.state.useAutoTranslate && (isLong || appearNote.cw != null || !showContent))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && appearNote.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (!$i.policies.canUseAutoTranslate || (defaultStore.state.useAutoTranslate && (isLong || appearNote.cw != null || !showContent)))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && appearNote.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<button v-if="!(translating || translation)" ref="translateButton" class="_button" @click.stop="translate()">{{ i18n.ts.translateNote }}</button>
<button v-else class="_button" @click.stop="translation = null">{{ i18n.ts.close }}</button>
</div>
@ -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<void> {
if (translation.value != null) return;

View File

@ -115,7 +115,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:enableEmojiMenuReaction="true"
/>
<a v-if="appearNote.renote != null" :class="$style.rn">RN:</a>
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (defaultStore.state.useAutoTranslate && (appearNote.cw != null || !showContent))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && appearNote.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (!$i.policies.canUseAutoTranslate || (defaultStore.state.useAutoTranslate && (appearNote.cw != null || !showContent)))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && appearNote.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<button v-if="!(translating || translation)" ref="translateButton" class="_button" @click="translate()">{{ i18n.ts.translateNote }}</button>
<button v-else class="_button" @click="translation = null">{{ i18n.ts.close }}</button>
</div>
@ -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<void> {
if (translation.value != null) return;

View File

@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:enableEmojiMenuReaction="true"
/>
<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (defaultStore.state.useAutoTranslate && (isLong || note.cw != null || !showContent))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && note.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<div v-if="defaultStore.state.showTranslateButtonInNote && (!defaultStore.state.useAutoTranslate || (!$i.policies.canUseAutoTranslate || (defaultStore.state.useAutoTranslate && (isLong || note.cw != null || !showContent)))) && instance.translatorAvailable && $i && $i.policies.canUseTranslator && note.text && isForeignLanguage" style="padding-top: 5px; color: var(--accent);">
<button v-if="!(translating || translation)" ref="translateButton" class="_button" @click.stop="translate()">{{ i18n.ts.translateNote }}</button>
<button v-else class="_button" @click.stop="translation = null">{{ i18n.ts.close }}</button>
</div>
@ -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<void> {
if (translation.value != null) return;

View File

@ -231,6 +231,7 @@ export function alert(props: {
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string;
text?: string;
caption?: string | null;
}): Promise<void> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, props, {

View File

@ -51,9 +51,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
<MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
<MkSwitch v-model="useAutoTranslate" :disabled="!$i.policies.canUseTranslator || !$i.policies.canUseAutoTranslate" @update:modelValue="learnMoreAutoTranslate">
<MkSwitch v-model="useAutoTranslate" @update:modelValue="learnMoreAutoTranslate">
{{ i18n.ts.useAutoTranslate }} <span class="_beta">CherryPick</span>
<template v-if="!$i.policies.canUseAutoTranslate" #caption>{{ i18n.ts.cannotBeUsedFunc }} <a class="_link" @click="learnMoreAutoTranslate">{{ i18n.ts.learnMore }}</a></template>
<template v-if="!$i.policies.canUseAutoTranslate" #caption>{{ i18n.ts.cannotBeUsedFunc }} <a class="_link" @click="learnMoreCantUseAutoTranslate">{{ i18n.ts.learnMore }}</a></template>
</MkSwitch>
</div>
<MkSelect v-model="serverDisconnectedBehavior">
@ -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);
}, {

View File

@ -178,6 +178,7 @@ function getNoteEmbedCodeMenu(note: Misskey.entities.Note, text: string): MenuIt
export function getNoteMenu(props: {
note: Misskey.entities.Note;
collapsed?: Ref<boolean>;
translation: Ref<Misskey.entities.NotesTranslateResponse | null>;
translating: Ref<boolean>;
viewTextSource: Ref<boolean>;
@ -358,6 +359,7 @@ export function getNoteMenu(props: {
async function translate(): Promise<void> {
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,