mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 18:50:56 +08:00
refactor(translate): centralize language label handling with getLanguageLabel
Move language label generation from individual TranslateLanguage objects to a centralized getLanguageLabel function in useTranslate hook. This improves maintainability by removing duplicate label logic and makes it easier to update language labels globally. - Remove label() method from TranslateLanguage type and all language objects - Add getLanguageLabel function in useTranslate that handles label generation - Update all components to use getLanguageLabel instead of label() - Add labelMap for common language codes to avoid unnecessary lookups
This commit is contained in:
parent
dbfece3590
commit
6cda7f891d
@ -18,19 +18,22 @@ type Props = {
|
||||
} & Omit<SelectProps, 'labelRender' | 'options'>
|
||||
|
||||
const LanguageSelect = (props: Props) => {
|
||||
const { translateLanguages } = useTranslate()
|
||||
const { translateLanguages, getLanguageLabel } = useTranslate()
|
||||
const { extraOptionsAfter, extraOptionsBefore, languageRenderer, ...restProps } = props
|
||||
|
||||
const defaultLanguageRenderer = useCallback((lang: TranslateLanguage) => {
|
||||
return (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
{lang.label()}
|
||||
</Space.Compact>
|
||||
)
|
||||
}, [])
|
||||
const defaultLanguageRenderer = useCallback(
|
||||
(lang: TranslateLanguage) => {
|
||||
return (
|
||||
<Space.Compact direction="horizontal" block>
|
||||
<span role="img" aria-label={lang.emoji} style={{ marginRight: 8 }}>
|
||||
{lang.emoji}
|
||||
</span>
|
||||
{getLanguageLabel(lang.langCode)}
|
||||
</Space.Compact>
|
||||
)
|
||||
},
|
||||
[getLanguageLabel]
|
||||
)
|
||||
|
||||
const labelRender = (props) => {
|
||||
const { label } = props
|
||||
|
||||
@ -24,7 +24,7 @@ const TranslateButton: FC<Props> = ({ text, onTranslated, disabled, style, isLoa
|
||||
const [isTranslating, setIsTranslating] = useState(false)
|
||||
const [targetLanguage] = usePreference('feature.translate.target_language')
|
||||
const [showTranslateConfirm] = usePreference('chat.input.translate.show_confirm')
|
||||
const { getLanguageByLangcode } = useTranslate()
|
||||
const { getLanguageLabel, getLanguageByLangcode } = useTranslate()
|
||||
|
||||
const translateConfirm = () => {
|
||||
if (!showTranslateConfirm) {
|
||||
@ -64,9 +64,7 @@ const TranslateButton: FC<Props> = ({ text, onTranslated, disabled, style, isLoa
|
||||
}, [isLoading])
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={t('chat.input.translate', { target_language: getLanguageByLangcode(targetLanguage).label() })}
|
||||
closeDelay={0}>
|
||||
<Tooltip content={t('chat.input.translate', { target_language: getLanguageLabel(targetLanguage) })} closeDelay={0}>
|
||||
<Button
|
||||
onPress={handleTranslate}
|
||||
isDisabled={disabled || isTranslating}
|
||||
|
||||
@ -1,150 +1,128 @@
|
||||
import i18n from '@renderer/i18n'
|
||||
import type { TranslateLanguage } from '@renderer/types'
|
||||
|
||||
export const UNKNOWN: TranslateLanguage = {
|
||||
value: 'Unknown',
|
||||
langCode: 'unknown',
|
||||
label: () => i18n.t('languages.unknown'),
|
||||
emoji: '🏳️'
|
||||
}
|
||||
|
||||
export const ENGLISH: TranslateLanguage = {
|
||||
value: 'English',
|
||||
langCode: 'en-us',
|
||||
label: () => i18n.t('languages.english'),
|
||||
emoji: '🇬🇧'
|
||||
}
|
||||
|
||||
export const CHINESE_SIMPLIFIED: TranslateLanguage = {
|
||||
value: 'Chinese (Simplified)',
|
||||
langCode: 'zh-cn',
|
||||
label: () => i18n.t('languages.chinese'),
|
||||
emoji: '🇨🇳'
|
||||
}
|
||||
|
||||
export const CHINESE_TRADITIONAL: TranslateLanguage = {
|
||||
value: 'Chinese (Traditional)',
|
||||
langCode: 'zh-tw',
|
||||
label: () => i18n.t('languages.chinese-traditional'),
|
||||
emoji: '🇭🇰'
|
||||
}
|
||||
|
||||
export const JAPANESE: TranslateLanguage = {
|
||||
value: 'Japanese',
|
||||
langCode: 'ja-jp',
|
||||
label: () => i18n.t('languages.japanese'),
|
||||
emoji: '🇯🇵'
|
||||
}
|
||||
|
||||
export const KOREAN: TranslateLanguage = {
|
||||
value: 'Korean',
|
||||
langCode: 'ko-kr',
|
||||
label: () => i18n.t('languages.korean'),
|
||||
emoji: '🇰🇷'
|
||||
}
|
||||
|
||||
export const FRENCH: TranslateLanguage = {
|
||||
value: 'French',
|
||||
langCode: 'fr-fr',
|
||||
label: () => i18n.t('languages.french'),
|
||||
emoji: '🇫🇷'
|
||||
}
|
||||
|
||||
export const GERMAN: TranslateLanguage = {
|
||||
value: 'German',
|
||||
langCode: 'de-de',
|
||||
label: () => i18n.t('languages.german'),
|
||||
emoji: '🇩🇪'
|
||||
}
|
||||
|
||||
export const ITALIAN: TranslateLanguage = {
|
||||
value: 'Italian',
|
||||
langCode: 'it-it',
|
||||
label: () => i18n.t('languages.italian'),
|
||||
emoji: '🇮🇹'
|
||||
}
|
||||
|
||||
export const SPANISH: TranslateLanguage = {
|
||||
value: 'Spanish',
|
||||
langCode: 'es-es',
|
||||
label: () => i18n.t('languages.spanish'),
|
||||
emoji: '🇪🇸'
|
||||
}
|
||||
|
||||
export const PORTUGUESE: TranslateLanguage = {
|
||||
value: 'Portuguese',
|
||||
langCode: 'pt-pt',
|
||||
label: () => i18n.t('languages.portuguese'),
|
||||
emoji: '🇵🇹'
|
||||
}
|
||||
|
||||
export const RUSSIAN: TranslateLanguage = {
|
||||
value: 'Russian',
|
||||
langCode: 'ru-ru',
|
||||
label: () => i18n.t('languages.russian'),
|
||||
emoji: '🇷🇺'
|
||||
}
|
||||
|
||||
export const POLISH: TranslateLanguage = {
|
||||
value: 'Polish',
|
||||
langCode: 'pl-pl',
|
||||
label: () => i18n.t('languages.polish'),
|
||||
emoji: '🇵🇱'
|
||||
}
|
||||
|
||||
export const ARABIC: TranslateLanguage = {
|
||||
value: 'Arabic',
|
||||
langCode: 'ar-ar',
|
||||
label: () => i18n.t('languages.arabic'),
|
||||
emoji: '🇸🇦'
|
||||
}
|
||||
|
||||
export const TURKISH: TranslateLanguage = {
|
||||
value: 'Turkish',
|
||||
langCode: 'tr-tr',
|
||||
label: () => i18n.t('languages.turkish'),
|
||||
emoji: '🇹🇷'
|
||||
}
|
||||
|
||||
export const THAI: TranslateLanguage = {
|
||||
value: 'Thai',
|
||||
langCode: 'th-th',
|
||||
label: () => i18n.t('languages.thai'),
|
||||
emoji: '🇹🇭'
|
||||
}
|
||||
|
||||
export const VIETNAMESE: TranslateLanguage = {
|
||||
value: 'Vietnamese',
|
||||
langCode: 'vi-vn',
|
||||
label: () => i18n.t('languages.vietnamese'),
|
||||
emoji: '🇻🇳'
|
||||
}
|
||||
|
||||
export const INDONESIAN: TranslateLanguage = {
|
||||
value: 'Indonesian',
|
||||
langCode: 'id-id',
|
||||
label: () => i18n.t('languages.indonesian'),
|
||||
emoji: '🇮🇩'
|
||||
}
|
||||
|
||||
export const URDU: TranslateLanguage = {
|
||||
value: 'Urdu',
|
||||
langCode: 'ur-pk',
|
||||
label: () => i18n.t('languages.urdu'),
|
||||
emoji: '🇵🇰'
|
||||
}
|
||||
|
||||
export const MALAY: TranslateLanguage = {
|
||||
value: 'Malay',
|
||||
langCode: 'ms-my',
|
||||
label: () => i18n.t('languages.malay'),
|
||||
emoji: '🇲🇾'
|
||||
}
|
||||
|
||||
export const UKRAINIAN: TranslateLanguage = {
|
||||
value: 'Ukrainian',
|
||||
langCode: 'uk-ua',
|
||||
label: () => i18n.t('languages.ukrainian'),
|
||||
emoji: '🇺🇦'
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { usePreference } from '@data/hooks/usePreference'
|
||||
import { loggerService } from '@logger'
|
||||
import { builtinLanguages, UNKNOWN } from '@renderer/config/translate'
|
||||
import type { TranslateLanguage } from '@renderer/types'
|
||||
import type { TranslateLanguageCode } from '@renderer/types'
|
||||
import { type TranslateLanguage } from '@renderer/types'
|
||||
import { runAsyncFunction } from '@renderer/utils'
|
||||
import { getTranslateOptions } from '@renderer/utils/translate'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const logger = loggerService.withContext('useTranslate')
|
||||
|
||||
@ -16,6 +18,7 @@ const logger = loggerService.withContext('useTranslate')
|
||||
* - getLanguageByLangcode: 通过语言代码获取语言对象
|
||||
*/
|
||||
export default function useTranslate() {
|
||||
const { t } = useTranslation()
|
||||
const [prompt] = usePreference('feature.translate.model_prompt')
|
||||
const [translateLanguages, setTranslateLanguages] = useState<TranslateLanguage[]>(builtinLanguages)
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
@ -46,9 +49,53 @@ export default function useTranslate() {
|
||||
[isLoaded, translateLanguages]
|
||||
)
|
||||
|
||||
const labelMap: Record<string, string> = useMemo(
|
||||
() => ({
|
||||
'zh-cn': t('languages.chinese'),
|
||||
'zh-tw': t('languages.chinese-traditional'),
|
||||
'ja-jp': t('languages.japanese'),
|
||||
'ko-kr': t('languages.korean'),
|
||||
'en-us': t('languages.english'),
|
||||
'fr-fr': t('languages.french'),
|
||||
'de-de': t('languages.german'),
|
||||
'it-it': t('languages.italian'),
|
||||
'es-es': t('languages.spanish'),
|
||||
'pt-pt': t('languages.portuguese'),
|
||||
'ru-ru': t('languages.russian'),
|
||||
'pl-pl': t('languages.polish'),
|
||||
'ar-ar': t('languages.arabic'),
|
||||
'tr-tr': t('languages.turkish'),
|
||||
'th-th': t('languages.thai'),
|
||||
'vi-vn': t('languages.vietnamese'),
|
||||
'id-id': t('languages.indonesian'),
|
||||
'ur-pk': t('languages.urdu'),
|
||||
'ms-my': t('languages.malay'),
|
||||
'uk-ua': t('languages.ukrainian'),
|
||||
unknown: t('common.unknown')
|
||||
}),
|
||||
[t]
|
||||
)
|
||||
|
||||
const getLanguageLabel = useCallback(
|
||||
(code: TranslateLanguageCode) => {
|
||||
const label = labelMap[code]
|
||||
if (label) {
|
||||
return label
|
||||
} else if (isLoaded) {
|
||||
const language = getLanguageByLangcode(code)
|
||||
return language.value
|
||||
} else {
|
||||
return t('common.unknown')
|
||||
}
|
||||
},
|
||||
[getLanguageByLangcode, isLoaded, labelMap, t]
|
||||
)
|
||||
|
||||
return {
|
||||
prompt,
|
||||
isLoaded,
|
||||
translateLanguages,
|
||||
getLanguageByLangcode
|
||||
getLanguageByLangcode,
|
||||
getLanguageLabel
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import store, { useAppDispatch } from '@renderer/store'
|
||||
import { messageBlocksSelectors, removeOneBlock } from '@renderer/store/messageBlock'
|
||||
import { selectMessagesForTopic } from '@renderer/store/newMessage'
|
||||
import { TraceIcon } from '@renderer/trace/pages/Component'
|
||||
import type { Assistant, Model, Topic, TranslateLanguage } from '@renderer/types'
|
||||
import type { Assistant, Model, Topic, TranslateLanguage, TranslateLanguageCode } from '@renderer/types'
|
||||
import { type Message, MessageBlockType } from '@renderer/types/newMessage'
|
||||
import { captureScrollableAsBlob, captureScrollableAsDataURL, classNames } from '@renderer/utils'
|
||||
import { copyMessageAsPlainText } from '@renderer/utils/copy'
|
||||
@ -118,6 +118,7 @@ type MessageMenubarButtonContext = {
|
||||
softHoverBg: boolean
|
||||
t: TFunction
|
||||
translateLanguages: TranslateLanguage[]
|
||||
getLanguageLabel: (lang: TranslateLanguageCode) => string
|
||||
}
|
||||
|
||||
type MessageMenubarButtonRenderer = (ctx: MessageMenubarButtonContext) => ReactNode | null
|
||||
@ -142,7 +143,7 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
const [isTranslating, setIsTranslating] = useState(false)
|
||||
// remove confirm for regenerate; tooltip stays simple
|
||||
const [showDeleteTooltip, setShowDeleteTooltip] = useState(false)
|
||||
const { translateLanguages } = useTranslate()
|
||||
const { translateLanguages, getLanguageLabel } = useTranslate()
|
||||
// const assistantModel = assistant?.model
|
||||
const {
|
||||
deleteMessage,
|
||||
@ -571,7 +572,8 @@ const MessageMenubar: FC<Props> = (props) => {
|
||||
showDeleteTooltip,
|
||||
softHoverBg,
|
||||
t,
|
||||
translateLanguages
|
||||
translateLanguages,
|
||||
getLanguageLabel
|
||||
}
|
||||
|
||||
return (
|
||||
@ -757,6 +759,7 @@ const buttonRenderers: Record<MessageMenubarButtonId, MessageMenubarButtonRender
|
||||
translate: ({
|
||||
isUserMessage,
|
||||
translateLanguages,
|
||||
getLanguageLabel,
|
||||
handleTranslate,
|
||||
hasTranslationBlocks,
|
||||
message,
|
||||
@ -771,7 +774,7 @@ const buttonRenderers: Record<MessageMenubarButtonId, MessageMenubarButtonRender
|
||||
|
||||
const items: MenuProps['items'] = [
|
||||
...translateLanguages.map((item) => ({
|
||||
label: item.emoji + ' ' + item.label(),
|
||||
label: item.emoji + ' ' + getLanguageLabel(item.langCode),
|
||||
key: item.langCode,
|
||||
onClick: () => handleTranslate(item)
|
||||
})),
|
||||
|
||||
@ -96,7 +96,7 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0)
|
||||
const [fontSizeValue, setFontSizeValue] = useState(fontSize)
|
||||
const [streamOutput, setStreamOutput] = useState(assistant?.settings?.streamOutput)
|
||||
const { translateLanguages } = useTranslate()
|
||||
const { translateLanguages, getLanguageLabel } = useTranslate()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -142,8 +142,12 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
)
|
||||
|
||||
const targetLanguageItems = useMemo<SelectorItem<string>[]>(
|
||||
() => translateLanguages.map((item) => ({ value: item.langCode, label: item.emoji + ' ' + item.label() })),
|
||||
[translateLanguages]
|
||||
() =>
|
||||
translateLanguages.map((item) => ({
|
||||
value: item.langCode,
|
||||
label: item.emoji + ' ' + getLanguageLabel(item.langCode)
|
||||
})),
|
||||
[getLanguageLabel, translateLanguages]
|
||||
)
|
||||
|
||||
const sendMessageShortcutItems = useMemo<SelectorItem<SendMessageShortcut>[]>(
|
||||
@ -727,7 +731,7 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
selectionMode="single"
|
||||
selectedKeys={targetLanguage}
|
||||
onSelectionChange={(value) => setTargetLanguage(value)}
|
||||
placeholder={UNKNOWN.emoji + ' ' + UNKNOWN.label()}
|
||||
placeholder={UNKNOWN.emoji + ' ' + getLanguageLabel(UNKNOWN.langCode)}
|
||||
items={targetLanguageItems}
|
||||
/>
|
||||
</SettingRow>
|
||||
|
||||
@ -18,7 +18,7 @@ import { SettingRow, SettingRowTitle } from '..'
|
||||
export const OcrSystemSettings = () => {
|
||||
const { t } = useTranslation()
|
||||
// 和翻译自定义语言耦合了,应该还ok
|
||||
const { translateLanguages } = useTranslate()
|
||||
const { translateLanguages, getLanguageLabel } = useTranslate()
|
||||
const { provider, updateConfig } = useOcrProvider(BuiltinOcrProviderIds.system)
|
||||
|
||||
if (!isOcrSystemProvider(provider)) {
|
||||
@ -36,9 +36,9 @@ export const OcrSystemSettings = () => {
|
||||
() =>
|
||||
translateLanguages.map((lang) => ({
|
||||
value: lang.langCode,
|
||||
label: lang.emoji + ' ' + lang.label()
|
||||
label: lang.emoji + ' ' + getLanguageLabel(lang.langCode)
|
||||
})),
|
||||
[translateLanguages]
|
||||
[getLanguageLabel, translateLanguages]
|
||||
)
|
||||
|
||||
const onChange = useCallback((value: TranslateLanguageCode[]) => {
|
||||
|
||||
@ -24,17 +24,17 @@ export const OcrTesseractSettings = () => {
|
||||
}
|
||||
|
||||
const [langs, setLangs] = useState<Partial<Record<TesseractLangCode, boolean>>>(provider.config?.langs ?? {})
|
||||
const { translateLanguages } = useTranslate()
|
||||
const { translateLanguages, getLanguageLabel } = useTranslate()
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
translateLanguages
|
||||
.map((lang) => ({
|
||||
value: TESSERACT_LANG_MAP[lang.langCode],
|
||||
label: lang.emoji + ' ' + lang.label()
|
||||
label: lang.emoji + ' ' + getLanguageLabel(lang.langCode)
|
||||
}))
|
||||
.filter((option) => option.value),
|
||||
[translateLanguages]
|
||||
[getLanguageLabel, translateLanguages]
|
||||
)
|
||||
|
||||
// TODO: type safe objectKeys
|
||||
|
||||
@ -6,7 +6,7 @@ import { DynamicVirtualList } from '@renderer/components/VirtualList'
|
||||
import db from '@renderer/databases'
|
||||
import useTranslate from '@renderer/hooks/useTranslate'
|
||||
import { clearHistory, deleteHistory, updateTranslateHistory } from '@renderer/services/TranslateService'
|
||||
import type { TranslateHistory, TranslateLanguage } from '@renderer/types'
|
||||
import type { TranslateHistory } from '@renderer/types'
|
||||
import { Drawer, Empty, Input, Popconfirm } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { useLiveQuery } from 'dexie-react-hooks'
|
||||
@ -17,14 +17,9 @@ import { useCallback, useDeferredValue, useEffect, useMemo, useState } from 'rea
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
type DisplayedTranslateHistoryItem = TranslateHistory & {
|
||||
_sourceLanguage: TranslateLanguage
|
||||
_targetLanguage: TranslateLanguage
|
||||
}
|
||||
|
||||
type TranslateHistoryProps = {
|
||||
isOpen: boolean
|
||||
onHistoryItemClick: (history: DisplayedTranslateHistoryItem) => void
|
||||
onHistoryItemClick: (history: TranslateHistory) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
@ -35,39 +30,34 @@ const ITEM_HEIGHT = 160
|
||||
|
||||
const TranslateHistoryList: FC<TranslateHistoryProps> = ({ isOpen, onHistoryItemClick, onClose }) => {
|
||||
const { t } = useTranslation()
|
||||
const { getLanguageByLangcode } = useTranslate()
|
||||
const { getLanguageByLangcode, getLanguageLabel } = useTranslate()
|
||||
const _translateHistory = useLiveQuery(() => db.translate_history.orderBy('createdAt').reverse().toArray(), [])
|
||||
const [search, setSearch] = useState('')
|
||||
const [displayedHistory, setDisplayedHistory] = useState<DisplayedTranslateHistoryItem[]>([])
|
||||
const [displayedHistory, setDisplayedHistory] = useState<TranslateHistory[]>([])
|
||||
const [showStared, setShowStared] = useState<boolean>(false)
|
||||
|
||||
const translateHistory: DisplayedTranslateHistoryItem[] = useMemo(() => {
|
||||
const translateHistory: TranslateHistory[] = useMemo(() => {
|
||||
if (!_translateHistory) return []
|
||||
|
||||
return _translateHistory.map((item) => ({
|
||||
...item,
|
||||
_sourceLanguage: getLanguageByLangcode(item.sourceLanguage),
|
||||
_targetLanguage: getLanguageByLangcode(item.targetLanguage),
|
||||
createdAt: dayjs(item.createdAt).format('MM/DD HH:mm')
|
||||
}))
|
||||
}, [_translateHistory, getLanguageByLangcode])
|
||||
}, [_translateHistory])
|
||||
|
||||
const searchFilter = useCallback(
|
||||
(item: DisplayedTranslateHistoryItem) => {
|
||||
(item: TranslateHistory) => {
|
||||
if (isEmpty(search)) return true
|
||||
const content = `${item._sourceLanguage.label()} ${item._targetLanguage.label()} ${item.sourceText} ${item.targetText} ${item.createdAt}`
|
||||
const content = `${getLanguageLabel(item.sourceLanguage)} ${getLanguageLabel(item.targetLanguage)} ${item.sourceText} ${item.targetText} ${item.createdAt}`
|
||||
return content.includes(search)
|
||||
},
|
||||
[search]
|
||||
[getLanguageLabel, search]
|
||||
)
|
||||
|
||||
const starFilter = useMemo(
|
||||
() => (showStared ? (item: DisplayedTranslateHistoryItem) => !!item.star : () => true),
|
||||
[showStared]
|
||||
)
|
||||
const starFilter = useMemo(() => (showStared ? (item: TranslateHistory) => !!item.star : () => true), [showStared])
|
||||
|
||||
const finalFilter = useCallback(
|
||||
(item: DisplayedTranslateHistoryItem) => searchFilter(item) && starFilter(item),
|
||||
(item: TranslateHistory) => searchFilter(item) && starFilter(item),
|
||||
[searchFilter, starFilter]
|
||||
)
|
||||
|
||||
@ -179,8 +169,8 @@ const TranslateHistoryList: FC<TranslateHistoryProps> = ({ isOpen, onHistoryItem
|
||||
<ColFlex className="h-full w-full flex-1 justify-between gap-1">
|
||||
<Flex className="h-[30px] items-center justify-between">
|
||||
<Flex className="items-center gap-1.5">
|
||||
<HistoryListItemLanguage>{item._sourceLanguage.label()} →</HistoryListItemLanguage>
|
||||
<HistoryListItemLanguage>{item._targetLanguage.label()}</HistoryListItemLanguage>
|
||||
<HistoryListItemLanguage>{getLanguageLabel(item.sourceLanguage)} →</HistoryListItemLanguage>
|
||||
<HistoryListItemLanguage>{getLanguageLabel(item.targetLanguage)}</HistoryListItemLanguage>
|
||||
</Flex>
|
||||
{/* tool bar */}
|
||||
<Flex className="mt-2 items-center justify-end">
|
||||
|
||||
@ -58,7 +58,7 @@ const TranslatePage: FC = () => {
|
||||
// hooks
|
||||
const { t } = useTranslation()
|
||||
const { translateModel, setTranslateModel } = useDefaultModel()
|
||||
const { prompt, getLanguageByLangcode } = useTranslate()
|
||||
const { prompt, getLanguageByLangcode, getLanguageLabel } = useTranslate()
|
||||
const [autoCopy] = usePreference('translate.settings.auto_copy')
|
||||
const { shikiMarkdownIt } = useCodeStyle()
|
||||
const { onSelectFile, selecting, clearFiles } = useFiles({ extensions: [...imageExts, ...textExts] })
|
||||
@ -261,17 +261,15 @@ const TranslatePage: FC = () => {
|
||||
}
|
||||
|
||||
// 控制历史记录点击
|
||||
const onHistoryItemClick = (
|
||||
history: TranslateHistory & { _sourceLanguage: TranslateLanguage; _targetLanguage: TranslateLanguage }
|
||||
) => {
|
||||
const onHistoryItemClick = (history: TranslateHistory) => {
|
||||
setText(history.sourceText)
|
||||
setOutput(history.targetText)
|
||||
if (history._sourceLanguage === UNKNOWN) {
|
||||
if (history.sourceLanguage === UNKNOWN.langCode) {
|
||||
setSourceLanguage('auto')
|
||||
} else {
|
||||
setSourceLanguage(history._sourceLanguage)
|
||||
setSourceLanguage(getLanguageByLangcode(history.sourceLanguage))
|
||||
}
|
||||
setTargetLanguage(history._targetLanguage)
|
||||
setTargetLanguage(getLanguageByLangcode(history.targetLanguage))
|
||||
setHistoryDrawerVisible(false)
|
||||
}
|
||||
|
||||
@ -390,7 +388,7 @@ const TranslatePage: FC = () => {
|
||||
return (
|
||||
<Flex className="min-w-40 items-center">
|
||||
<BidirectionalLanguageDisplay>
|
||||
{`${bidirectionalPair[0].label()} ⇆ ${bidirectionalPair[1].label()}`}
|
||||
{`${getLanguageLabel(bidirectional.origin)} ⇆ ${getLanguageLabel(bidirectional.target)}`}
|
||||
</BidirectionalLanguageDisplay>
|
||||
</Flex>
|
||||
)
|
||||
@ -656,7 +654,7 @@ const TranslatePage: FC = () => {
|
||||
{
|
||||
value: 'auto',
|
||||
label: detectedLanguage
|
||||
? `${t('translate.detected.language')} (${detectedLanguage.label()})`
|
||||
? `${t('translate.detected.language')} (${getLanguageLabel(detectedLanguage.langCode)})`
|
||||
: t('translate.detected.language')
|
||||
}
|
||||
]}
|
||||
|
||||
@ -485,7 +485,6 @@ export type TranslateLanguageCode = string
|
||||
export type TranslateLanguage = {
|
||||
value: string
|
||||
langCode: TranslateLanguageCode
|
||||
label: () => string
|
||||
emoji: string
|
||||
}
|
||||
|
||||
|
||||
@ -251,7 +251,6 @@ export const getTranslateOptions = async () => {
|
||||
// 转换为Language类型
|
||||
const transformedCustomLangs: TranslateLanguage[] = customLanguages.map((item) => ({
|
||||
value: item.value,
|
||||
label: () => item.value,
|
||||
emoji: item.emoji,
|
||||
langCode: item.langCode
|
||||
}))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user