refactor(i18n): use JSON with flattened key and namespace (#30114)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Stephen Zhou
2025-12-29 14:52:32 +08:00
committed by GitHub
parent 09be869f58
commit 6d0e36479b
2552 changed files with 111159 additions and 142972 deletions

View File

@ -47,7 +47,7 @@ const AnnotationCtrlButton: FC<Props> = ({
answer,
})
Toast.notify({
message: t('common.api.actionSuccess') as string,
message: t('api.actionSuccess', { ns: 'common' }) as string,
type: 'success',
})
onAdded(res.id, res.account?.name ?? '')
@ -57,7 +57,7 @@ const AnnotationCtrlButton: FC<Props> = ({
<>
{cached && (
<Tooltip
popupContent={t('appDebug.feature.annotation.edit')}
popupContent={t('feature.annotation.edit', { ns: 'appDebug' })}
>
<ActionButton onClick={onEdit}>
<RiEditLine className="h-4 w-4" />
@ -66,7 +66,7 @@ const AnnotationCtrlButton: FC<Props> = ({
)}
{!cached && answer && (
<Tooltip
popupContent={t('appDebug.feature.annotation.add')}
popupContent={t('feature.annotation.add', { ns: 'appDebug' })}
>
<ActionButton onClick={handleAdd}>
<RiFileEditLine className="h-4 w-4" />

View File

@ -61,7 +61,7 @@ const ConfigParamModal: FC<Props> = ({
const handleSave = async () => {
if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model && !isEmbeddingsDefaultModelValid)) {
Toast.notify({
message: t('common.modelProvider.embeddingModel.required'),
message: t('modelProvider.embeddingModel.required', { ns: 'common' }),
type: 'error',
})
return
@ -81,13 +81,13 @@ const ConfigParamModal: FC<Props> = ({
className="!mt-14 !w-[640px] !max-w-none !p-6"
>
<div className="title-2xl-semi-bold mb-2 text-text-primary">
{t(`appAnnotation.initSetup.${isInit ? 'title' : 'configTitle'}`)}
{t(`initSetup.${isInit ? 'title' : 'configTitle'}`, { ns: 'appAnnotation' })}
</div>
<div className="mt-6 space-y-3">
<Item
title={t('appDebug.feature.annotation.scoreThreshold.title')}
tooltip={t('appDebug.feature.annotation.scoreThreshold.description')}
title={t('feature.annotation.scoreThreshold.title', { ns: 'appDebug' })}
tooltip={t('feature.annotation.scoreThreshold.description', { ns: 'appDebug' })}
>
<ScoreSlider
className="mt-1"
@ -102,8 +102,8 @@ const ConfigParamModal: FC<Props> = ({
</Item>
<Item
title={t('common.modelProvider.embeddingModel.key')}
tooltip={t('appAnnotation.embeddingModelSwitchTip')}
title={t('modelProvider.embeddingModel.key', { ns: 'common' })}
tooltip={t('embeddingModelSwitchTip', { ns: 'appAnnotation' })}
>
<div className="pt-1">
<ModelSelector
@ -124,14 +124,14 @@ const ConfigParamModal: FC<Props> = ({
</div>
<div className="mt-6 flex justify-end gap-2">
<Button onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button onClick={onHide}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button
variant="primary"
onClick={handleSave}
loading={isLoading}
>
<div></div>
<div>{t(`appAnnotation.initSetup.${isInit ? 'confirmBtn' : 'configConfirmBtn'}`)}</div>
<div>{t(`initSetup.${isInit ? 'confirmBtn' : 'configConfirmBtn'}`, { ns: 'appAnnotation' })}</div>
</Button>
</div>
</Modal>

View File

@ -83,7 +83,7 @@ const AnnotationReply = ({
<MessageFast className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.annotation.title')}
title={t('feature.annotation.title', { ns: 'appDebug' })}
value={!!annotationReply?.enabled}
onChange={state => handleSwitch(state)}
onMouseEnter={() => setIsHovering(true)}
@ -92,19 +92,19 @@ const AnnotationReply = ({
>
<>
{!annotationReply?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.annotation.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.annotation.description', { ns: 'appDebug' })}</div>
)}
{!!annotationReply?.enabled && (
<>
{!isHovering && (
<div className="flex items-center gap-4 pt-0.5">
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.annotation.scoreThreshold.title')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.annotation.scoreThreshold.title', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{annotationReply.score_threshold || '-'}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('common.modelProvider.embeddingModel.key')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('modelProvider.embeddingModel.key', { ns: 'common' })}</div>
<div className="system-xs-regular text-text-secondary">{annotationReply.embedding_model?.embedding_model_name}</div>
</div>
</div>
@ -113,7 +113,7 @@ const AnnotationReply = ({
<div className="flex items-center justify-between">
<Button className="w-[178px]" onClick={() => setIsShowAnnotationConfigInit(true)} disabled={disabled}>
<RiEqualizer2Line className="mr-1 h-4 w-4" />
{t('common.operation.params')}
{t('operation.params', { ns: 'common' })}
</Button>
<Button
className="w-[178px]"
@ -122,7 +122,7 @@ const AnnotationReply = ({
}}
>
<RiExternalLinkLine className="mr-1 h-4 w-4" />
{t('appDebug.feature.annotation.cacheManagement')}
{t('feature.annotation.cacheManagement', { ns: 'appDebug' })}
</Button>
</div>
)}

View File

@ -32,12 +32,12 @@ const ScoreSlider: FC<Props> = ({
<div className="flex space-x-1 text-util-colors-cyan-cyan-500">
<div>0.8</div>
<div>·</div>
<div>{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}</div>
<div>{t('feature.annotation.scoreThreshold.easyMatch', { ns: 'appDebug' })}</div>
</div>
<div className="flex space-x-1 text-util-colors-blue-blue-500">
<div>1.0</div>
<div>·</div>
<div>{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}</div>
<div>{t('feature.annotation.scoreThreshold.accurateMatch', { ns: 'appDebug' })}</div>
</div>
</div>
</div>

View File

@ -45,9 +45,9 @@ const Citation = ({
<Citations className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.citation.title')}
title={t('feature.citation.title', { ns: 'appDebug' })}
value={!!features.citation?.enabled}
description={t('appDebug.feature.citation.description')!}
description={t('feature.citation.description', { ns: 'appDebug' })!}
onChange={state => handleChange(FeatureEnum.citation, state)}
disabled={disabled}
/>

View File

@ -86,7 +86,7 @@ const ConversationOpener = ({
<LoveMessage className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.conversationOpener.title')}
title={t('feature.conversationOpener.title', { ns: 'appDebug' })}
value={!!opening?.enabled}
onChange={state => handleChange(FeatureEnum.opening, state)}
onMouseEnter={() => setIsHovering(true)}
@ -95,19 +95,19 @@ const ConversationOpener = ({
>
<>
{!opening?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.conversationOpener.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.conversationOpener.description', { ns: 'appDebug' })}</div>
)}
{!!opening?.enabled && (
<>
{!isHovering && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">
{opening.opening_statement || t('appDebug.openingStatement.placeholder')}
{opening.opening_statement || t('openingStatement.placeholder', { ns: 'appDebug' })}
</div>
)}
{isHovering && (
<Button className="w-full" onClick={handleOpenOpeningModal} disabled={disabled}>
<RiEditLine className="mr-1 h-4 w-4" />
{t('appDebug.openingStatement.writeOpener')}
{t('openingStatement.writeOpener', { ns: 'appDebug' })}
</Button>
)}
</>

View File

@ -106,7 +106,7 @@ const OpeningSettingModal = ({
<div>
<div className="flex items-center py-2">
<div className="flex shrink-0 space-x-0.5 text-xs font-medium leading-[18px] text-text-tertiary">
<div className="uppercase">{t('appDebug.openingStatement.openingQuestion')}</div>
<div className="uppercase">{t('openingStatement.openingQuestion', { ns: 'appDebug' })}</div>
<div>·</div>
<div>
{tempSuggestedQuestions.length}
@ -143,7 +143,7 @@ const OpeningSettingModal = ({
<input
type="input"
value={question || ''}
placeholder={t('appDebug.openingStatement.openingQuestionPlaceholder') as string}
placeholder={t('openingStatement.openingQuestionPlaceholder', { ns: 'appDebug' }) as string}
onChange={(e) => {
const value = e.target.value
setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => {
@ -178,7 +178,7 @@ const OpeningSettingModal = ({
className="mt-1 flex h-9 cursor-pointer items-center gap-2 rounded-lg bg-components-button-tertiary-bg px-3 text-components-button-tertiary-text hover:bg-components-button-tertiary-bg-hover"
>
<RiAddLine className="h-4 w-4" />
<div className="system-sm-medium text-[13px]">{t('appDebug.variableConfig.addOption')}</div>
<div className="system-sm-medium text-[13px]">{t('variableConfig.addOption', { ns: 'appDebug' })}</div>
</div>
)}
</div>
@ -192,7 +192,7 @@ const OpeningSettingModal = ({
className="!mt-14 !w-[640px] !max-w-none !bg-components-panel-bg-blur !p-6"
>
<div className="mb-6 flex items-center justify-between">
<div className="title-2xl-semi-bold text-text-primary">{t('appDebug.feature.conversationOpener.title')}</div>
<div className="title-2xl-semi-bold text-text-primary">{t('feature.conversationOpener.title', { ns: 'appDebug' })}</div>
<div className="cursor-pointer p-1" onClick={onCancel}><RiCloseLine className="h-4 w-4 text-text-tertiary" /></div>
</div>
<div className="mb-8 flex gap-2">
@ -203,7 +203,7 @@ const OpeningSettingModal = ({
<PromptEditor
value={tempValue}
onChange={setTempValue}
placeholder={t('appDebug.openingStatement.placeholder') as string}
placeholder={t('openingStatement.placeholder', { ns: 'appDebug' }) as string}
variableBlock={{
show: true,
variables: [
@ -228,14 +228,14 @@ const OpeningSettingModal = ({
onClick={onCancel}
className="mr-2"
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
variant="primary"
onClick={() => handleSave()}
disabled={isSaveDisabled}
>
{t('common.operation.save')}
{t('operation.save', { ns: 'common' })}
</Button>
</div>
{isShowConfirmAddVar && (

View File

@ -41,7 +41,7 @@ const FeatureBar = ({
{noFeatureEnabled && (
<div className="flex cursor-pointer items-end gap-1" onClick={() => onFeatureBarClick?.(true)}>
<RiApps2AddLine className="h-3.5 w-3.5 text-text-accent" />
<div className="body-xs-medium text-text-accent">{t('appDebug.feature.bar.empty')}</div>
<div className="body-xs-medium text-text-accent">{t('feature.bar.empty', { ns: 'appDebug' })}</div>
<RiArrowRightLine className="h-3.5 w-3.5 text-text-accent" />
</div>
)}
@ -50,7 +50,7 @@ const FeatureBar = ({
<div className="flex shrink-0 items-center gap-0.5">
{!!features.moreLikeThis?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.moreLikeThis.title')}
popupContent={t('feature.moreLikeThis.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-light-blue-light-500 p-1 shadow-xs">
<RiSparklingFill className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -59,7 +59,7 @@ const FeatureBar = ({
)}
{!!features.opening?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.conversationOpener.title')}
popupContent={t('feature.conversationOpener.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-light-blue-light-500 p-1 shadow-xs">
<LoveMessage className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -68,7 +68,7 @@ const FeatureBar = ({
)}
{!!features.moderation?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.moderation.title')}
popupContent={t('feature.moderation.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-text-success p-1 shadow-xs">
<ContentModeration className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -77,7 +77,7 @@ const FeatureBar = ({
)}
{!!features.speech2text?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.speechToText.title')}
popupContent={t('feature.speechToText.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-violet-violet-600 p-1 shadow-xs">
<Microphone01 className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -87,7 +87,7 @@ const FeatureBar = ({
{!!features.text2speech?.enabled && (
<VoiceSettings placementLeft={false} open={modalOpen && !disabled} onOpen={setModalOpen}>
<Tooltip
popupContent={t('appDebug.feature.textToSpeech.title')}
popupContent={t('feature.textToSpeech.title', { ns: 'appDebug' })}
>
<div className={cn('shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-violet-violet-600 p-1 shadow-xs', !disabled && 'cursor-pointer')}>
<TextToAudio className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -97,7 +97,7 @@ const FeatureBar = ({
)}
{showFileUpload && !!features.file?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.fileUpload.title')}
popupContent={t('feature.fileUpload.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-blue-600 p-1 shadow-xs">
<FolderUpload className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -106,7 +106,7 @@ const FeatureBar = ({
)}
{!!features.suggested?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
popupContent={t('feature.suggestedQuestionsAfterAnswer.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-light-blue-light-500 p-1 shadow-xs">
<VirtualAssistant className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -115,7 +115,7 @@ const FeatureBar = ({
)}
{isChatMode && !!features.citation?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.citation.title')}
popupContent={t('feature.citation.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-warning-warning-500 p-1 shadow-xs">
<Citations className="h-4 w-4 text-text-primary-on-surface" />
@ -124,7 +124,7 @@ const FeatureBar = ({
)}
{isChatMode && !!features.annotationReply?.enabled && (
<Tooltip
popupContent={t('appDebug.feature.annotation.title')}
popupContent={t('feature.annotation.title', { ns: 'appDebug' })}
>
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-indigo-indigo-600 p-1 shadow-xs">
<MessageFast className="h-3.5 w-3.5 text-text-primary-on-surface" />
@ -132,9 +132,9 @@ const FeatureBar = ({
</Tooltip>
)}
</div>
<div className="body-xs-regular grow text-text-tertiary">{t('appDebug.feature.bar.enableText')}</div>
<div className="body-xs-regular grow text-text-tertiary">{t('feature.bar.enableText', { ns: 'appDebug' })}</div>
<Button className="shrink-0" variant="ghost-accent" size="small" onClick={() => onFeatureBarClick?.(true)}>
<div className="mx-1">{t('appDebug.feature.bar.manage')}</div>
<div className="mx-1">{t('feature.bar.manage', { ns: 'appDebug' })}</div>
<RiArrowRightLine className="h-3.5 w-3.5 text-text-accent" />
</Button>
</div>

View File

@ -55,7 +55,7 @@ const FileUpload = ({
<FolderUpload className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.fileUpload.title')}
title={t('feature.fileUpload.title', { ns: 'appDebug' })}
value={file?.enabled}
onChange={state => handleChange(FeatureEnum.file, state)}
onMouseEnter={() => setIsHovering(true)}
@ -64,19 +64,19 @@ const FileUpload = ({
>
<>
{!file?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.fileUpload.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.fileUpload.description', { ns: 'appDebug' })}</div>
)}
{file?.enabled && (
<>
{!isHovering && !modalOpen && (
<div className="flex items-center gap-4 pt-0.5">
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.fileUpload.supportedTypes')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.fileUpload.supportedTypes', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{supportedTypes}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.fileUpload.numberLimit')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.fileUpload.numberLimit', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{file?.number_limits}</div>
</div>
</div>
@ -92,7 +92,7 @@ const FileUpload = ({
>
<Button className="w-full" disabled={disabled}>
<RiEqualizer2Line className="mr-1 h-4 w-4" />
{t('common.operation.settings')}
{t('operation.settings', { ns: 'common' })}
</Button>
</SettingModal>
)}

View File

@ -58,7 +58,7 @@ const SettingContent = ({
return (
<>
<div className="mb-4 flex items-center justify-between">
<div className="system-xl-semibold text-text-primary">{!imageUpload ? t('appDebug.feature.fileUpload.modalTitle') : t('appDebug.feature.imageUpload.modalTitle')}</div>
<div className="system-xl-semibold text-text-primary">{!imageUpload ? t('feature.fileUpload.modalTitle', { ns: 'appDebug' }) : t('feature.imageUpload.modalTitle', { ns: 'appDebug' })}</div>
<div className="cursor-pointer p-1" onClick={onClose}><RiCloseLine className="h-4 w-4 text-text-tertiary" /></div>
</div>
<FileUploadSetting
@ -73,14 +73,14 @@ const SettingContent = ({
onClick={onClose}
className="mr-2"
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
variant="primary"
onClick={handleChange}
disabled={tempPayload.allowed_file_types.length === 0}
>
{t('common.operation.save')}
{t('operation.save', { ns: 'common' })}
</Button>
</div>
</>

View File

@ -45,9 +45,9 @@ const FollowUp = ({
<VirtualAssistant className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
title={t('feature.suggestedQuestionsAfterAnswer.title', { ns: 'appDebug' })}
value={!!features.suggested?.enabled}
description={t('appDebug.feature.suggestedQuestionsAfterAnswer.description')!}
description={t('feature.suggestedQuestionsAfterAnswer.description', { ns: 'appDebug' })!}
onChange={state => handleChange(FeatureEnum.suggested, state)}
disabled={disabled}
/>

View File

@ -57,7 +57,7 @@ const FileUpload = ({
)}
title={(
<div className="flex items-center">
{t('appDebug.feature.imageUpload.title')}
{t('feature.imageUpload.title', { ns: 'appDebug' })}
<Badge
text="LEGACY"
className="mx-1 shrink-0 border-text-accent-secondary text-text-accent-secondary"
@ -72,19 +72,19 @@ const FileUpload = ({
>
<>
{!file?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.imageUpload.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.imageUpload.description', { ns: 'appDebug' })}</div>
)}
{file?.enabled && (
<>
{!isHovering && !modalOpen && (
<div className="flex items-center gap-4 pt-0.5">
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.imageUpload.supportedTypes')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.imageUpload.supportedTypes', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{supportedTypes}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.imageUpload.numberLimit')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.imageUpload.numberLimit', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{file?.number_limits}</div>
</div>
</div>
@ -101,7 +101,7 @@ const FileUpload = ({
>
<Button className="w-full" disabled={disabled}>
<RiEqualizer2Line className="mr-1 h-4 w-4" />
{t('common.operation.settings')}
{t('operation.settings', { ns: 'common' })}
</Button>
</SettingModal>
)}

View File

@ -60,8 +60,8 @@ const NewFeaturePanel = ({
{/* header */}
<div className="flex shrink-0 justify-between p-4 pb-3">
<div>
<div className="system-xl-semibold text-text-primary">{t('workflow.common.features')}</div>
<div className="body-xs-regular text-text-tertiary">{t('workflow.common.featuresDescription')}</div>
<div className="system-xl-semibold text-text-primary">{t('common.features', { ns: 'workflow' })}</div>
<div className="body-xs-regular text-text-tertiary">{t('common.featuresDescription', { ns: 'workflow' })}</div>
</div>
<div className="h-8 w-8 cursor-pointer p-2" onClick={onClose}><RiCloseLine className="h-4 w-4 text-text-tertiary" /></div>
</div>
@ -75,14 +75,14 @@ const NewFeaturePanel = ({
<RiInformation2Fill className="h-5 w-5 text-text-accent" />
</div>
<div className="system-xs-medium p-1 text-text-primary">
<span>{isChatMode ? t('workflow.common.fileUploadTip') : t('workflow.common.ImageUploadLegacyTip')}</span>
<span>{isChatMode ? t('common.fileUploadTip', { ns: 'workflow' }) : t('common.ImageUploadLegacyTip', { ns: 'workflow' })}</span>
<a
className="text-text-accent"
href={docLink('/guides/workflow/bulletin')}
target="_blank"
rel="noopener noreferrer"
>
{t('workflow.common.featuresDocLink')}
{t('common.featuresDocLink', { ns: 'workflow' })}
</a>
</div>
</div>

View File

@ -106,22 +106,22 @@ const Moderation = ({
const providerContent = useMemo(() => {
if (moderation?.type === 'openai_moderation')
return t('appDebug.feature.moderation.modal.provider.openai')
return t('feature.moderation.modal.provider.openai', { ns: 'appDebug' })
else if (moderation?.type === 'keywords')
return t('appDebug.feature.moderation.modal.provider.keywords')
return t('feature.moderation.modal.provider.keywords', { ns: 'appDebug' })
else if (moderation?.type === 'api')
return t('common.apiBasedExtension.selector.title')
return t('apiBasedExtension.selector.title', { ns: 'common' })
else
return codeBasedExtensionList?.data.find(item => item.name === moderation?.type)?.label[locale] || '-'
}, [codeBasedExtensionList?.data, locale, moderation?.type, t])
const enableContent = useMemo(() => {
if (moderation?.config?.inputs_config?.enabled && moderation.config?.outputs_config?.enabled)
return t('appDebug.feature.moderation.allEnabled')
return t('feature.moderation.allEnabled', { ns: 'appDebug' })
else if (moderation?.config?.inputs_config?.enabled)
return t('appDebug.feature.moderation.inputEnabled')
return t('feature.moderation.inputEnabled', { ns: 'appDebug' })
else if (moderation?.config?.outputs_config?.enabled)
return t('appDebug.feature.moderation.outputEnabled')
return t('feature.moderation.outputEnabled', { ns: 'appDebug' })
}, [moderation?.config?.inputs_config?.enabled, moderation?.config?.outputs_config?.enabled, t])
return (
@ -131,7 +131,7 @@ const Moderation = ({
<ContentModeration className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.moderation.title')}
title={t('feature.moderation.title', { ns: 'appDebug' })}
value={!!moderation?.enabled}
onChange={state => handleChange(FeatureEnum.moderation, state)}
onMouseEnter={() => setIsHovering(true)}
@ -140,19 +140,19 @@ const Moderation = ({
>
<>
{!moderation?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.moderation.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.moderation.description', { ns: 'appDebug' })}</div>
)}
{!!moderation?.enabled && (
<>
{!isHovering && (
<div className="flex items-center gap-4 pt-0.5">
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.moderation.modal.provider.title')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.moderation.modal.provider.title', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{providerContent}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.feature.moderation.contentEnableLabel')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('feature.moderation.contentEnableLabel', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{enableContent}</div>
</div>
</div>
@ -160,7 +160,7 @@ const Moderation = ({
{isHovering && (
<Button className="w-full" onClick={handleOpenModerationSettingModal} disabled={disabled}>
<RiEqualizer2Line className="mr-1 h-4 w-4" />
{t('common.operation.settings')}
{t('operation.settings', { ns: 'common' })}
</Button>
)}
</>

View File

@ -47,14 +47,14 @@ const ModerationContent: FC<ModerationContentProps> = ({
config.enabled && showPreset && (
<div className="rounded-lg bg-components-panel-bg px-3 pb-3 pt-1">
<div className="flex h-8 items-center justify-between text-[13px] font-medium text-text-secondary">
{t('appDebug.feature.moderation.modal.content.preset')}
<span className="text-xs font-normal text-text-tertiary">{t('appDebug.feature.moderation.modal.content.supportMarkdown')}</span>
{t('feature.moderation.modal.content.preset', { ns: 'appDebug' })}
<span className="text-xs font-normal text-text-tertiary">{t('feature.moderation.modal.content.supportMarkdown', { ns: 'appDebug' })}</span>
</div>
<div className="relative h-20 rounded-lg bg-components-input-bg-normal px-3 py-2">
<textarea
value={config.preset_response || ''}
className="block h-full w-full resize-none appearance-none bg-transparent text-sm text-text-secondary outline-none"
placeholder={t('appDebug.feature.moderation.modal.content.placeholder') || ''}
placeholder={t('feature.moderation.modal.content.placeholder', { ns: 'appDebug' }) || ''}
onChange={e => handleConfigChange('preset_response', e.target.value)}
/>
<div className="absolute bottom-2 right-2 flex h-5 items-center rounded-md bg-background-section px-1 text-xs font-medium text-text-quaternary">

View File

@ -67,15 +67,15 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
const providers: Provider[] = [
{
key: 'openai_moderation',
name: t('appDebug.feature.moderation.modal.provider.openai'),
name: t('feature.moderation.modal.provider.openai', { ns: 'appDebug' }),
},
{
key: 'keywords',
name: t('appDebug.feature.moderation.modal.provider.keywords'),
name: t('feature.moderation.modal.provider.keywords', { ns: 'appDebug' }),
},
{
key: 'api',
name: t('common.apiBasedExtension.selector.title'),
name: t('apiBasedExtension.selector.title', { ns: 'common' }),
},
...(
codeBasedExtensionList
@ -193,17 +193,17 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
return
if (!localeData.config?.inputs_config?.enabled && !localeData.config?.outputs_config?.enabled) {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.condition') })
notify({ type: 'error', message: t('feature.moderation.modal.content.condition', { ns: 'appDebug' }) })
return
}
if (localeData.type === 'keywords' && !localeData.config.keywords) {
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }) })
notify({ type: 'error', message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }) })
return
}
if (localeData.type === 'api' && !localeData.config.api_based_extension_id) {
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }) })
notify({ type: 'error', message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }) })
return
}
@ -212,7 +212,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
if (!localeData.config?.[currentProvider.form_schema[i].variable] && currentProvider.form_schema[i].required) {
notify({
type: 'error',
message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }),
message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }),
})
return
}
@ -220,12 +220,12 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
}
if (localeData.config.inputs_config?.enabled && !localeData.config.inputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') })
notify({ type: 'error', message: t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }) })
return
}
if (localeData.config.outputs_config?.enabled && !localeData.config.outputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') })
notify({ type: 'error', message: t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }) })
return
}
@ -239,12 +239,12 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
className="!mt-14 !w-[600px] !max-w-none !p-6"
>
<div className="flex items-center justify-between">
<div className="title-2xl-semi-bold text-text-primary">{t('appDebug.feature.moderation.modal.title')}</div>
<div className="title-2xl-semi-bold text-text-primary">{t('feature.moderation.modal.title', { ns: 'appDebug' })}</div>
<div className="cursor-pointer p-1" onClick={onCancel}><RiCloseLine className="h-4 w-4 text-text-tertiary" /></div>
</div>
<div className="py-2">
<div className="text-sm font-medium leading-9 text-text-primary">
{t('appDebug.feature.moderation.modal.provider.title')}
{t('feature.moderation.modal.provider.title', { ns: 'appDebug' })}
</div>
<div className="grid grid-cols-3 gap-2.5">
{
@ -275,16 +275,16 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
<div className="mt-2 flex items-center rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] px-3 py-2">
<InfoCircle className="mr-1 h-4 w-4 text-[#F79009]" />
<div className="flex items-center text-xs font-medium text-gray-700">
{t('appDebug.feature.moderation.modal.openaiNotConfig.before')}
{t('feature.moderation.modal.openaiNotConfig.before', { ns: 'appDebug' })}
<span
className="cursor-pointer text-primary-600"
onClick={handleOpenSettingsModal}
>
&nbsp;
{t('common.settings.provider')}
{t('settings.provider', { ns: 'common' })}
&nbsp;
</span>
{t('appDebug.feature.moderation.modal.openaiNotConfig.after')}
{t('feature.moderation.modal.openaiNotConfig.after', { ns: 'appDebug' })}
</div>
</div>
)
@ -293,21 +293,21 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
{
localeData.type === 'keywords' && (
<div className="py-2">
<div className="mb-1 text-sm font-medium text-text-primary">{t('appDebug.feature.moderation.modal.provider.keywords')}</div>
<div className="mb-2 text-xs text-text-tertiary">{t('appDebug.feature.moderation.modal.keywords.tip')}</div>
<div className="mb-1 text-sm font-medium text-text-primary">{t('feature.moderation.modal.provider.keywords', { ns: 'appDebug' })}</div>
<div className="mb-2 text-xs text-text-tertiary">{t('feature.moderation.modal.keywords.tip', { ns: 'appDebug' })}</div>
<div className="relative h-[88px] rounded-lg bg-components-input-bg-normal px-3 py-2">
<textarea
value={localeData.config?.keywords || ''}
onChange={handleDataKeywordsChange}
className="block h-full w-full resize-none appearance-none bg-transparent text-sm text-text-secondary outline-none"
placeholder={t('appDebug.feature.moderation.modal.keywords.placeholder') || ''}
placeholder={t('feature.moderation.modal.keywords.placeholder', { ns: 'appDebug' }) || ''}
/>
<div className="absolute bottom-2 right-2 flex h-5 items-center rounded-md bg-background-section px-1 text-xs font-medium text-text-quaternary">
<span>{(localeData.config?.keywords || '').split('\n').filter(Boolean).length}</span>
/
<span className="text-text-tertiary">
100
{t('appDebug.feature.moderation.modal.keywords.line')}
{t('feature.moderation.modal.keywords.line', { ns: 'appDebug' })}
</span>
</div>
</div>
@ -318,7 +318,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
localeData.type === 'api' && (
<div className="py-2">
<div className="flex h-9 items-center justify-between">
<div className="text-sm font-medium text-text-primary">{t('common.apiBasedExtension.selector.title')}</div>
<div className="text-sm font-medium text-text-primary">{t('apiBasedExtension.selector.title', { ns: 'common' })}</div>
<a
href={docLink('/guides/extension/api-based-extension/README')}
target="_blank"
@ -326,7 +326,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
className="group flex items-center text-xs text-text-tertiary hover:text-primary-600"
>
<BookOpen01 className="mr-1 h-3 w-3 text-text-tertiary group-hover:text-primary-600" />
{t('common.apiBasedExtension.link')}
{t('apiBasedExtension.link', { ns: 'common' })}
</a>
</div>
<ApiBasedExtensionSelector
@ -349,33 +349,33 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
}
<Divider bgStyle="gradient" className="my-3 h-px" />
<ModerationContent
title={t('appDebug.feature.moderation.modal.content.input') || ''}
title={t('feature.moderation.modal.content.input', { ns: 'appDebug' }) || ''}
config={localeData.config?.inputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('inputs_config', config)}
info={(localeData.type === 'api' && t('appDebug.feature.moderation.modal.content.fromApi')) || ''}
info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''}
showPreset={localeData.type !== 'api'}
/>
<ModerationContent
title={t('appDebug.feature.moderation.modal.content.output') || ''}
title={t('feature.moderation.modal.content.output', { ns: 'appDebug' }) || ''}
config={localeData.config?.outputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('outputs_config', config)}
info={(localeData.type === 'api' && t('appDebug.feature.moderation.modal.content.fromApi')) || ''}
info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''}
showPreset={localeData.type !== 'api'}
/>
<div className="mb-8 mt-1 text-xs font-medium text-text-tertiary">{t('appDebug.feature.moderation.modal.content.condition')}</div>
<div className="mb-8 mt-1 text-xs font-medium text-text-tertiary">{t('feature.moderation.modal.content.condition', { ns: 'appDebug' })}</div>
<div className="flex items-center justify-end">
<Button
onClick={onCancel}
className="mr-2"
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
variant="primary"
onClick={handleSave}
disabled={localeData.type === 'openai_moderation' && !isOpenAIProviderConfigured}
>
{t('common.operation.save')}
{t('operation.save', { ns: 'common' })}
</Button>
</div>
</Modal>

View File

@ -45,10 +45,10 @@ const MoreLikeThis = ({
<RiSparklingFill className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.moreLikeThis.title')}
tooltip={t('appDebug.feature.moreLikeThis.tip')}
title={t('feature.moreLikeThis.title', { ns: 'appDebug' })}
tooltip={t('feature.moreLikeThis.tip', { ns: 'appDebug' })}
value={!!features.moreLikeThis?.enabled}
description={t('appDebug.feature.moreLikeThis.description')!}
description={t('feature.moreLikeThis.description', { ns: 'appDebug' })!}
onChange={state => handleChange(FeatureEnum.moreLikeThis, state)}
disabled={disabled}
/>

View File

@ -45,9 +45,9 @@ const SpeechToText = ({
<Microphone01 className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.speechToText.title')}
title={t('feature.speechToText.title', { ns: 'appDebug' })}
value={!!features.speech2text?.enabled}
description={t('appDebug.feature.speechToText.description')!}
description={t('feature.speechToText.description', { ns: 'appDebug' })!}
onChange={state => handleChange(FeatureEnum.speech2text, state)}
disabled={disabled}
/>

View File

@ -54,7 +54,7 @@ const TextToSpeech = ({
<TextToAudio className="h-4 w-4 text-text-primary-on-surface" />
</div>
)}
title={t('appDebug.feature.textToSpeech.title')}
title={t('feature.textToSpeech.title', { ns: 'appDebug' })}
value={!!features.text2speech?.enabled}
onChange={state => handleChange(FeatureEnum.text2speech, state)}
onMouseEnter={() => setIsHovering(true)}
@ -63,25 +63,25 @@ const TextToSpeech = ({
>
<>
{!features.text2speech?.enabled && (
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('appDebug.feature.textToSpeech.description')}</div>
<div className="system-xs-regular line-clamp-2 min-h-8 text-text-tertiary">{t('feature.textToSpeech.description', { ns: 'appDebug' })}</div>
)}
{!!features.text2speech?.enabled && (
<>
{!isHovering && !modalOpen && (
<div className="flex items-center gap-4 pt-0.5">
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.voice.voiceSettings.language')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('voice.voiceSettings.language', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{languageInfo?.name || '-'}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.voice.voiceSettings.voice')}</div>
<div className="system-xs-regular text-text-secondary">{features.text2speech?.voice || t('appDebug.voice.defaultDisplay')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('voice.voiceSettings.voice', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{features.text2speech?.voice || t('voice.defaultDisplay', { ns: 'appDebug' })}</div>
</div>
<div className="h-[27px] w-px rotate-12 bg-divider-subtle"></div>
<div className="">
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('appDebug.voice.voiceSettings.autoPlay')}</div>
<div className="system-xs-regular text-text-secondary">{features.text2speech?.autoPlay === TtsAutoPlay.enabled ? t('appDebug.voice.voiceSettings.autoPlayEnabled') : t('appDebug.voice.voiceSettings.autoPlayDisabled')}</div>
<div className="system-2xs-medium-uppercase mb-0.5 text-text-tertiary">{t('voice.voiceSettings.autoPlay', { ns: 'appDebug' })}</div>
<div className="system-xs-regular text-text-secondary">{features.text2speech?.autoPlay === TtsAutoPlay.enabled ? t('voice.voiceSettings.autoPlayEnabled', { ns: 'appDebug' }) : t('voice.voiceSettings.autoPlayDisabled', { ns: 'appDebug' })}</div>
</div>
</div>
)}
@ -89,7 +89,7 @@ const TextToSpeech = ({
<VoiceSettings open={modalOpen && !disabled} onOpen={setModalOpen} onChange={onChange}>
<Button className="w-full" disabled={disabled}>
<RiEqualizer2Line className="mr-1 h-4 w-4" />
{t('appDebug.voice.voiceSettings.title')}
{t('voice.voiceSettings.title', { ns: 'appDebug' })}
</Button>
</VoiceSettings>
)}

View File

@ -1,6 +1,7 @@
'use client'
import type { OnFeaturesChange } from '@/app/components/base/features/types'
import type { Item } from '@/app/components/base/select'
import type { I18nKeysWithPrefix } from '@/types/i18n'
import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
import { RiCloseLine } from '@remixicon/react'
@ -9,6 +10,7 @@ import { usePathname } from 'next/navigation'
import * as React from 'react'
import { Fragment } from 'react'
import { useTranslation } from 'react-i18next'
import { replace } from 'string-ts'
import AudioBtn from '@/app/components/base/audio-btn'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
import Switch from '@/app/components/base/switch'
@ -18,6 +20,8 @@ import { useAppVoices } from '@/service/use-apps'
import { TtsAutoPlay } from '@/types/app'
import { cn } from '@/utils/classnames'
type VoiceLanguageKey = I18nKeysWithPrefix<'common', 'voice.language.'>
type VoiceParamConfigProps = {
onClose: () => void
onChange?: OnFeaturesChange
@ -36,14 +40,14 @@ const VoiceParamConfig = ({
let languageItem = languages.find(item => item.value === text2speech?.language)
if (languages && !languageItem)
languageItem = languages[0]
const localLanguagePlaceholder = languageItem?.name || t('common.placeholder.select')
const localLanguagePlaceholder = languageItem?.name || t('placeholder.select', { ns: 'common' })
const language = languageItem?.value
const { data: voiceItems } = useAppVoices(appId, language)
let voiceItem = voiceItems?.find(item => item.value === text2speech?.voice)
if (voiceItems && !voiceItem)
voiceItem = voiceItems[0]
const localVoicePlaceholder = voiceItem?.name || t('common.placeholder.select')
const localVoicePlaceholder = voiceItem?.name || t('placeholder.select', { ns: 'common' })
const handleChange = (value: Record<string, string>) => {
const {
@ -66,16 +70,16 @@ const VoiceParamConfig = ({
return (
<>
<div className="mb-4 flex items-center justify-between">
<div className="system-xl-semibold text-text-primary">{t('appDebug.voice.voiceSettings.title')}</div>
<div className="system-xl-semibold text-text-primary">{t('voice.voiceSettings.title', { ns: 'appDebug' })}</div>
<div className="cursor-pointer p-1" onClick={onClose}><RiCloseLine className="h-4 w-4 text-text-tertiary" /></div>
</div>
<div className="mb-3">
<div className="system-sm-semibold mb-1 flex items-center py-1 text-text-secondary">
{t('appDebug.voice.voiceSettings.language')}
{t('voice.voiceSettings.language', { ns: 'appDebug' })}
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.voice.voiceSettings.resolutionTooltip').split('\n').map(item => (
{t('voice.voiceSettings.resolutionTooltip', { ns: 'appDebug' }).split('\n').map(item => (
<div key={item}>
{item}
</div>
@ -97,7 +101,7 @@ const VoiceParamConfig = ({
className="h-full w-full cursor-pointer rounded-lg border-0 bg-components-input-bg-normal py-1.5 pl-3 pr-10 focus-visible:bg-state-base-hover focus-visible:outline-none group-hover:bg-state-base-hover sm:text-sm sm:leading-6"
>
<span className={cn('block truncate text-left text-text-secondary', !languageItem?.name && 'text-text-tertiary')}>
{languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}` as any) as string : localLanguagePlaceholder}
{languageItem?.name ? t(`voice.language.${replace(languageItem?.value, '-', '')}`, { ns: 'common' }) : localLanguagePlaceholder}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
@ -116,7 +120,7 @@ const VoiceParamConfig = ({
<ListboxOptions
className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg px-1 py-1 text-base shadow-lg focus:outline-none sm:text-sm"
>
{languages.map((item: Item) => (
{languages.map(item => (
<ListboxOption
key={item.value}
className="relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover data-[active]:bg-state-base-active"
@ -128,7 +132,7 @@ const VoiceParamConfig = ({
<span
className={cn('block', selected && 'font-normal')}
>
{t(`common.voice.language.${(item.value).toString().replace('-', '')}` as any) as string}
{t(`voice.language.${replace((item.value), '-', '')}`, { ns: 'common' })}
</span>
{(selected || item.value === text2speech?.language) && (
<span
@ -148,7 +152,7 @@ const VoiceParamConfig = ({
</div>
<div className="mb-3">
<div className="system-sm-semibold mb-1 py-1 text-text-secondary">
{t('appDebug.voice.voiceSettings.voice')}
{t('voice.voiceSettings.voice', { ns: 'appDebug' })}
</div>
<div className="flex items-center gap-1">
<Listbox
@ -225,7 +229,7 @@ const VoiceParamConfig = ({
</div>
<div>
<div className="system-sm-semibold mb-1 py-1 text-text-secondary">
{t('appDebug.voice.voiceSettings.autoPlay')}
{t('voice.voiceSettings.autoPlay', { ns: 'appDebug' })}
</div>
<Switch
className="shrink-0"