From e54651acfd1e35b9187f455b24b23e8ab9e0de10 Mon Sep 17 00:00:00 2001 From: NoriDev Date: Wed, 29 Nov 2023 17:25:45 +0900 Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=96=B8=EC=96=B4=EC=99=80=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EB=B3=B8=EB=AC=B8=EC=9D=98=20=EC=96=B8?= =?UTF-8?q?=EC=96=B4=EA=B0=80=20=EA=B0=99=EC=9C=BC=EB=A9=B4=20=EB=B2=88?= =?UTF-8?q?=EC=97=AD=20=EB=B2=84=ED=8A=BC=EC=9D=84=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG_CHERRYPICK.md | 1 + packages/frontend/package.json | 1 + packages/frontend/src/components/MkNote.vue | 9 ++++- .../src/components/MkNoteDetailed.vue | 9 ++++- .../src/components/MkSubNoteContent.vue | 9 ++++- packages/frontend/src/pages/user/home.vue | 9 ++++- .../frontend/src/scripts/detect-language.ts | 13 +++++++ pnpm-lock.yaml | 36 ++++++++++--------- 8 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 packages/frontend/src/scripts/detect-language.ts diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index 3232219817..391e00b54d 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -46,6 +46,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE - Enhance: 사운드 설정을 기본값으로 복원하거나 저장할 때 확실하게 표시함 - Enhance: 리모트 서버와 동일한 이모지가 존재하지 않는 경우 '이모지 복사'를 비활성화함 - Enhance: 아이콘 장식을 바로 업로드 하거나 드라이브에서 불러올 수 있음 ([Secineralyr/misskey.dream@e358212d](https://github.com/Secineralyr/misskey.dream/commit/e358212da93256749e31d9e0ca9dd2ed37fd548e), [Secineralyr/misskey.dream@52592fea](https://github.com/Secineralyr/misskey.dream/commit/52592fea52684497ba7e07f173aac2b1083afcb1)) +- Enhance: 클라이언트 언어와 노트 본문의 언어가 같으면 번역 버튼을 표시하지 않음 - Fix: '모달 배경색 제거' 옵션이 이모지 피커에 반영되지 않음 - Fix: 열람 주의로 설정된 노트의 반응이 더 보기를 눌러야 표시됨 diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 7c8fea7f22..e44ad2744d 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -71,6 +71,7 @@ "three": "0.158.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", + "tinyld": "^1.3.4", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "twemoji-parser": "14.0.0", diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 5481cced9d..c35ab7fa73 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenu="true" :enableEmojiMenuReaction="true" /> -
+
@@ -239,6 +239,7 @@ import { miLocalStorage } from '@/local-storage.js'; import { instance } from '@/instance.js'; import { concat } from '@/scripts/array.js'; import { vibrate } from '@/scripts/vibrate.js'; +import detectLanguage from '@/scripts/detect-language.js'; let showEl = $ref(false); @@ -592,6 +593,12 @@ async function clip() { os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); } +const isForeignLanguage: boolean = appearNote.text != null && (() => { + const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); + const postLang = detectLanguage(appearNote.text); + return postLang !== '' && postLang !== targetLang; +})(); + async function translate(): Promise { if (translation.value != null) return; translating.value = true; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 049355ceda..f956a84353 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -105,7 +105,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenuReaction="true" /> RN: -
+
@@ -315,6 +315,7 @@ import { infoImageUrl, instance } from '@/instance.js'; import MkPostForm from '@/components/MkPostFormSimple.vue'; import { deviceKind } from '@/scripts/device-kind.js'; import { vibrate } from '@/scripts/vibrate.js'; +import detectLanguage from '@/scripts/detect-language.js'; const MOBILE_THRESHOLD = 500; const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); @@ -601,6 +602,12 @@ function menu(viaKeyboard = false): void { }).then(focus).finally(cleanup); } +const isForeignLanguage: boolean = appearNote.text != null && (() => { + const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); + const postLang = detectLanguage(appearNote.text); + return postLang !== '' && postLang !== targetLang; +})(); + async function translate(): Promise { if (translation.value != null) return; translating.value = true; diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 8e7ec2e728..0d68917c69 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: ... -
+
@@ -143,6 +143,7 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { useNoteCapture } from '@/scripts/use-note-capture.js'; import { concat } from '@/scripts/array.js'; import { vibrate } from '@/scripts/vibrate.js'; +import detectLanguage from '@/scripts/detect-language.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -403,6 +404,12 @@ async function clip() { os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); } +const isForeignLanguage: boolean = note.text != null && (() => { + const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); + const postLang = detectLanguage(note.text); + return postLang !== '' && postLang !== targetLang; +})(); + async function translate(): Promise { if (translation.value != null) return; translating.value = true; diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index ef269c83af..998c21c32e 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -80,7 +80,7 @@ SPDX-License-Identifier: AGPL-3.0-only

{{ i18n.ts.noAccountDescription }}

-
+
{{ i18n.ts.translateProfile }} {{ i18n.ts.close }}
@@ -187,6 +187,7 @@ import { defaultStore } from '@/store.js'; import { miLocalStorage } from '@/local-storage.js'; import { editNickname } from '@/scripts/edit-nickname.js'; import { vibrate } from '@/scripts/vibrate.js'; +import detectLanguage from '@/scripts/detect-language.js'; function calcAge(birthdate: string): number { const date = new Date(birthdate); @@ -290,6 +291,12 @@ async function updateMemo() { isEditingMemo = false; } +const isForeignLanguage: boolean = user.description != null && (() => { + const targetLang = (miLocalStorage.getItem('lang') ?? navigator.language).slice(0, 2); + const postLang = detectLanguage(user.description); + return postLang !== '' && postLang !== targetLang; +})(); + async function translate(): Promise { if (translation.value != null) return; translating.value = true; diff --git a/packages/frontend/src/scripts/detect-language.ts b/packages/frontend/src/scripts/detect-language.ts new file mode 100644 index 0000000000..d0e46c955a --- /dev/null +++ b/packages/frontend/src/scripts/detect-language.ts @@ -0,0 +1,13 @@ +import { detect } from 'tinyld'; +import * as mfm from 'cherrypick-mfm-js'; + +export default function detectLanguage(text: string): string { + const nodes = mfm.parse(text); + const filtered = mfm.extract(nodes, (node) => { + return node.type === 'text' || node.type === 'quote'; + }); + const purified = mfm.toString(filtered); + + if (detect(purified) === '') return 'en'; + return detect(purified); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 569f7fc5ff..f4999f4db0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -878,6 +878,9 @@ importers: tinycolor2: specifier: 1.6.0 version: 1.6.0 + tinyld: + specifier: ^1.3.4 + version: 1.3.4 tsc-alias: specifier: 1.8.8 version: 1.8.8 @@ -962,10 +965,10 @@ importers: version: 7.5.3 '@storybook/vue3': specifier: 7.5.3 - version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9) + version: 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9) '@storybook/vue3-vite': specifier: 7.5.3 - version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9) + version: 7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9) '@testing-library/vue': specifier: 8.0.1 version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9) @@ -7158,7 +7161,7 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9): + /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9): resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==} engines: {node: ^14.18 || >=16} peerDependencies: @@ -7168,7 +7171,7 @@ packages: dependencies: '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2) '@storybook/core-server': 7.5.3 - '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9) + '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9) '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.9) magic-string: 0.30.5 react: 18.2.0 @@ -7187,7 +7190,7 @@ packages: - vue dev: true - /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9): + /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9): resolution: {integrity: sha512-JaxtOl3UD9YhPrOqHuKtpqHMnFril3sBUxx/no2yM/mZYmNpAVd/C6PFM839WCay1mAywPuUoebJvmwWxWijkw==} engines: {node: '>=16.0.0'} peerDependencies: @@ -7199,7 +7202,7 @@ packages: '@storybook/global': 5.0.0 '@storybook/preview-api': 7.5.3 '@storybook/types': 7.5.3 - '@vue/compiler-core': 3.3.8 + '@vue/compiler-core': 3.3.9 lodash: 4.17.21 ts-dedent: 2.2.0 type-fest: 2.19.0 @@ -10134,7 +10137,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -12454,13 +12457,6 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - optional: true - /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -14104,7 +14100,7 @@ packages: micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /jest-leak-detector@29.7.0: @@ -18066,7 +18062,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup@4.6.0: @@ -18086,7 +18082,7 @@ packages: '@rollup/rollup-win32-arm64-msvc': 4.6.0 '@rollup/rollup-win32-ia32-msvc': 4.6.0 '@rollup/rollup-win32-x64-msvc': 4.6.0 - fsevents: 2.3.2 + fsevents: 2.3.3 /rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} @@ -19262,6 +19258,12 @@ packages: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} dev: false + /tinyld@1.3.4: + resolution: {integrity: sha512-u26CNoaInA4XpDU+8s/6Cq8xHc2T5M4fXB3ICfXPokUQoLzmPgSZU02TAkFwFMJCWTjk53gtkS8pETTreZwCqw==} + engines: {node: '>= 12.10.0', npm: '>= 6.12.0', yarn: '>= 1.20.0'} + hasBin: true + dev: false + /tinypool@0.7.0: resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} engines: {node: '>=14.0.0'}