diff --git a/package.json b/package.json index 36fe62337c..ceb0cbf3ac 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@napi-rs/system-ocr": "patch:@napi-rs/system-ocr@npm%3A1.0.2#~/.yarn/patches/@napi-rs-system-ocr-npm-1.0.2-59e7a78e8b.patch", "@paymoapp/electron-shutdown-handler": "^1.1.2", "@strongtz/win32-arm64-msvc": "^0.4.7", + "emoji-picker-element-data": "^1", "express": "^5.1.0", "font-list": "^2.0.0", "graceful-fs": "^4.2.11", diff --git a/src/renderer/src/components/EmojiPicker/index.tsx b/src/renderer/src/components/EmojiPicker/index.tsx index 8ba9d3e967..9a4158d469 100644 --- a/src/renderer/src/components/EmojiPicker/index.tsx +++ b/src/renderer/src/components/EmojiPicker/index.tsx @@ -1,35 +1,120 @@ +import 'emoji-picker-element' + import TwemojiCountryFlagsWoff2 from '@renderer/assets/fonts/country-flag-fonts/TwemojiCountryFlags.woff2?url' import { useTheme } from '@renderer/context/ThemeProvider' +import type { LanguageVarious } from '@renderer/types' import { polyfillCountryFlagEmojis } from 'country-flag-emoji-polyfill' +// i18n translations from emoji-picker-element +import de from 'emoji-picker-element/i18n/de' +import en from 'emoji-picker-element/i18n/en' +import es from 'emoji-picker-element/i18n/es' +import fr from 'emoji-picker-element/i18n/fr' +import ja from 'emoji-picker-element/i18n/ja' +import pt_PT from 'emoji-picker-element/i18n/pt_PT' +import ru_RU from 'emoji-picker-element/i18n/ru_RU' +import zh_CN from 'emoji-picker-element/i18n/zh_CN' +import type Picker from 'emoji-picker-element/picker' +import type { EmojiClickEvent, NativeEmoji } from 'emoji-picker-element/shared' +// Emoji data from emoji-picker-element-data (local, no CDN) +// Using CLDR format for full multi-language search support (28 languages) +import dataDE from 'emoji-picker-element-data/de/cldr/data.json?url' +import dataEN from 'emoji-picker-element-data/en/cldr/data.json?url' +import dataES from 'emoji-picker-element-data/es/cldr/data.json?url' +import dataFR from 'emoji-picker-element-data/fr/cldr/data.json?url' +import dataJA from 'emoji-picker-element-data/ja/cldr/data.json?url' +import dataPT from 'emoji-picker-element-data/pt/cldr/data.json?url' +import dataRU from 'emoji-picker-element-data/ru/cldr/data.json?url' +import dataZH from 'emoji-picker-element-data/zh/cldr/data.json?url' +import dataZH_HANT from 'emoji-picker-element-data/zh-hant/cldr/data.json?url' import type { FC } from 'react' import { useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' interface Props { onEmojiClick: (emoji: string) => void } +// Mapping from app locale to emoji-picker-element i18n +const i18nMap: Record = { + 'en-US': en, + 'zh-CN': zh_CN, + 'zh-TW': zh_CN, // Closest available + 'de-DE': de, + 'el-GR': en, // No Greek available, fallback to English + 'es-ES': es, + 'fr-FR': fr, + 'ja-JP': ja, + 'pt-PT': pt_PT, + 'ru-RU': ru_RU +} + +// Mapping from app locale to emoji data URL +// Using CLDR format provides native language search support for all locales +const dataSourceMap: Record = { + 'en-US': dataEN, + 'zh-CN': dataZH, + 'zh-TW': dataZH_HANT, + 'de-DE': dataDE, + 'el-GR': dataEN, // No Greek CLDR available, fallback to English + 'es-ES': dataES, + 'fr-FR': dataFR, + 'ja-JP': dataJA, + 'pt-PT': dataPT, + 'ru-RU': dataRU +} + +// Mapping from app locale to emoji-picker-element locale string +// Must match the data source locale for proper IndexedDB caching +const localeMap: Record = { + 'en-US': 'en', + 'zh-CN': 'zh', + 'zh-TW': 'zh-hant', + 'de-DE': 'de', + 'el-GR': 'en', + 'es-ES': 'es', + 'fr-FR': 'fr', + 'ja-JP': 'ja', + 'pt-PT': 'pt', + 'ru-RU': 'ru' +} + const EmojiPicker: FC = ({ onEmojiClick }) => { const { theme } = useTheme() - const ref = useRef(null) + const { i18n } = useTranslation() + const ref = useRef(null) + const currentLocale = i18n.language as LanguageVarious useEffect(() => { polyfillCountryFlagEmojis('Twemoji Mozilla', TwemojiCountryFlagsWoff2) }, []) + // Configure picker with i18n and dataSource useEffect(() => { - const refValue = ref.current + const picker = ref.current + if (picker) { + picker.i18n = i18nMap[currentLocale] || en + picker.dataSource = dataSourceMap[currentLocale] || dataEN + picker.locale = localeMap[currentLocale] || 'en' + } + }, [currentLocale]) - if (refValue) { - const handleEmojiClick = (event: any) => { + useEffect(() => { + const picker = ref.current + + if (picker) { + const handleEmojiClick = (event: EmojiClickEvent) => { event.stopPropagation() - onEmojiClick(event.detail.unicode || event.detail.emoji.unicode) + const { detail } = event + // Use detail.unicode (processed with skin tone) or fallback to emoji's unicode for native emoji + const unicode = detail.unicode || ('unicode' in detail.emoji ? (detail.emoji as NativeEmoji).unicode : '') + onEmojiClick(unicode) } // 添加事件监听器 - refValue.addEventListener('emoji-click', handleEmojiClick) + picker.addEventListener('emoji-click', handleEmojiClick) // 清理事件监听器 return () => { - refValue.removeEventListener('emoji-click', handleEmojiClick) + picker.removeEventListener('emoji-click', handleEmojiClick) } } return diff --git a/src/renderer/src/pages/store/assistants/presets/components/AddAssistantPresetPopup.tsx b/src/renderer/src/pages/store/assistants/presets/components/AddAssistantPresetPopup.tsx index d5558b8e88..a6f733dfef 100644 --- a/src/renderer/src/pages/store/assistants/presets/components/AddAssistantPresetPopup.tsx +++ b/src/renderer/src/pages/store/assistants/presets/components/AddAssistantPresetPopup.tsx @@ -1,5 +1,3 @@ -import 'emoji-picker-element' - import { CheckOutlined, LoadingOutlined, RollbackOutlined, ThunderboltOutlined } from '@ant-design/icons' import { loggerService } from '@logger' import EmojiPicker from '@renderer/components/EmojiPicker' diff --git a/yarn.lock b/yarn.lock index 18bde2062e..240532dfb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10074,6 +10074,7 @@ __metadata: electron-window-state: "npm:^5.0.3" emittery: "npm:^1.0.3" emoji-picker-element: "npm:^1.22.1" + emoji-picker-element-data: "npm:^1" epub: "patch:epub@npm%3A1.3.0#~/.yarn/patches/epub-npm-1.3.0-8325494ffe.patch" eslint: "npm:^9.22.0" eslint-plugin-import-zod: "npm:^1.2.0" @@ -13655,6 +13656,13 @@ __metadata: languageName: node linkType: hard +"emoji-picker-element-data@npm:^1": + version: 1.8.0 + resolution: "emoji-picker-element-data@npm:1.8.0" + checksum: 10c0/c8976b636205a0cc90d2690859a1193add71a948dadf743962b47c338a4c3715768404d0ccbc02156608b44abf41f3e1d51756e06f1bbed9d164dd4cb1752103 + languageName: node + linkType: hard + "emoji-picker-element@npm:^1.22.1": version: 1.26.3 resolution: "emoji-picker-element@npm:1.26.3"