test(JinaReader): add tests for handling null and undefined limits

- Introduced new test cases to verify that the JinaReader component correctly handles scenarios where the crawl limit is null or undefined, ensuring that the task is not initiated in these cases.
- Refactored existing tests to remove unnecessary promise resolutions, improving clarity and maintainability.
This commit is contained in:
CodingOnStar 2025-12-29 15:37:01 +08:00
commit fe0b221354
2552 changed files with 111159 additions and 142972 deletions

View File

@ -4,7 +4,7 @@ on:
push:
branches: [main]
paths:
- 'web/i18n/en-US/*.ts'
- 'web/i18n/en-US/*.json'
permissions:
contents: write
@ -28,13 +28,13 @@ jobs:
run: |
git fetch origin "${{ github.event.before }}" || true
git fetch origin "${{ github.sha }}" || true
changed_files=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'i18n/en-US/*.ts')
changed_files=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'i18n/en-US/*.json')
echo "Changed files: $changed_files"
if [ -n "$changed_files" ]; then
echo "FILES_CHANGED=true" >> $GITHUB_ENV
file_args=""
for file in $changed_files; do
filename=$(basename "$file" .ts)
filename=$(basename "$file" .json)
file_args="$file_args --file $filename"
done
echo "FILE_ARGS=$file_args" >> $GITHUB_ENV

View File

@ -16,7 +16,7 @@ const getSupportedLocales = (): string[] => {
// Helper function to load translation file content
const loadTranslationContent = (locale: string): string => {
const filePath = path.join(I18N_DIR, locale, 'app-debug.ts')
const filePath = path.join(I18N_DIR, locale, 'app-debug.json')
if (!fs.existsSync(filePath))
throw new Error(`Translation file not found: ${filePath}`)
@ -24,14 +24,14 @@ const loadTranslationContent = (locale: string): string => {
return fs.readFileSync(filePath, 'utf-8')
}
// Helper function to check if upload features exist
// Helper function to check if upload features exist (supports flattened JSON)
const hasUploadFeatures = (content: string): { [key: string]: boolean } => {
return {
fileUpload: /fileUpload\s*:\s*\{/.test(content),
imageUpload: /imageUpload\s*:\s*\{/.test(content),
documentUpload: /documentUpload\s*:\s*\{/.test(content),
audioUpload: /audioUpload\s*:\s*\{/.test(content),
featureBar: /bar\s*:\s*\{/.test(content),
fileUpload: /"feature\.fileUpload\.title"/.test(content),
imageUpload: /"feature\.imageUpload\.title"/.test(content),
documentUpload: /"feature\.documentUpload\.title"/.test(content),
audioUpload: /"feature\.audioUpload\.title"/.test(content),
featureBar: /"feature\.bar\.empty"/.test(content),
}
}
@ -45,7 +45,7 @@ describe('Upload Features i18n Translations - Issue #23062', () => {
it('all locales should have translation files', () => {
supportedLocales.forEach((locale) => {
const filePath = path.join(I18N_DIR, locale, 'app-debug.ts')
const filePath = path.join(I18N_DIR, locale, 'app-debug.json')
expect(fs.existsSync(filePath)).toBe(true)
})
})
@ -76,12 +76,9 @@ describe('Upload Features i18n Translations - Issue #23062', () => {
previouslyMissingLocales.forEach((locale) => {
const content = loadTranslationContent(locale)
// Verify audioUpload exists
expect(/audioUpload\s*:\s*\{/.test(content)).toBe(true)
// Verify it has title and description
expect(/audioUpload[^}]*title\s*:/.test(content)).toBe(true)
expect(/audioUpload[^}]*description\s*:/.test(content)).toBe(true)
// Verify audioUpload exists with title and description (flattened JSON format)
expect(/"feature\.audioUpload\.title"/.test(content)).toBe(true)
expect(/"feature\.audioUpload\.description"/.test(content)).toBe(true)
console.log(`${locale} - Issue #23062 resolved: audioUpload feature present`)
})
@ -91,28 +88,28 @@ describe('Upload Features i18n Translations - Issue #23062', () => {
supportedLocales.forEach((locale) => {
const content = loadTranslationContent(locale)
// Check fileUpload has required properties
if (/fileUpload\s*:\s*\{/.test(content)) {
expect(/fileUpload[^}]*title\s*:/.test(content)).toBe(true)
expect(/fileUpload[^}]*description\s*:/.test(content)).toBe(true)
// Check fileUpload has required properties (flattened JSON format)
if (/"feature\.fileUpload\.title"/.test(content)) {
expect(/"feature\.fileUpload\.title"/.test(content)).toBe(true)
expect(/"feature\.fileUpload\.description"/.test(content)).toBe(true)
}
// Check imageUpload has required properties
if (/imageUpload\s*:\s*\{/.test(content)) {
expect(/imageUpload[^}]*title\s*:/.test(content)).toBe(true)
expect(/imageUpload[^}]*description\s*:/.test(content)).toBe(true)
if (/"feature\.imageUpload\.title"/.test(content)) {
expect(/"feature\.imageUpload\.title"/.test(content)).toBe(true)
expect(/"feature\.imageUpload\.description"/.test(content)).toBe(true)
}
// Check documentUpload has required properties
if (/documentUpload\s*:\s*\{/.test(content)) {
expect(/documentUpload[^}]*title\s*:/.test(content)).toBe(true)
expect(/documentUpload[^}]*description\s*:/.test(content)).toBe(true)
if (/"feature\.documentUpload\.title"/.test(content)) {
expect(/"feature\.documentUpload\.title"/.test(content)).toBe(true)
expect(/"feature\.documentUpload\.description"/.test(content)).toBe(true)
}
// Check audioUpload has required properties
if (/audioUpload\s*:\s*\{/.test(content)) {
expect(/audioUpload[^}]*title\s*:/.test(content)).toBe(true)
expect(/audioUpload[^}]*description\s*:/.test(content)).toBe(true)
if (/"feature\.audioUpload\.title"/.test(content)) {
expect(/"feature\.audioUpload\.title"/.test(content)).toBe(true)
expect(/"feature\.audioUpload\.description"/.test(content)).toBe(true)
}
})
})

View File

@ -70,7 +70,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const navConfig = [
...(isCurrentWorkspaceEditor
? [{
name: t('common.appMenus.promptEng'),
name: t('appMenus.promptEng', { ns: 'common' }),
href: `/app/${appId}/${(mode === AppModeEnum.WORKFLOW || mode === AppModeEnum.ADVANCED_CHAT) ? 'workflow' : 'configuration'}`,
icon: RiTerminalWindowLine,
selectedIcon: RiTerminalWindowFill,
@ -78,7 +78,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
: []
),
{
name: t('common.appMenus.apiAccess'),
name: t('appMenus.apiAccess', { ns: 'common' }),
href: `/app/${appId}/develop`,
icon: RiTerminalBoxLine,
selectedIcon: RiTerminalBoxFill,
@ -86,8 +86,8 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
...(isCurrentWorkspaceEditor
? [{
name: mode !== AppModeEnum.WORKFLOW
? t('common.appMenus.logAndAnn')
: t('common.appMenus.logs'),
? t('appMenus.logAndAnn', { ns: 'common' })
: t('appMenus.logs', { ns: 'common' }),
href: `/app/${appId}/logs`,
icon: RiFileList3Line,
selectedIcon: RiFileList3Fill,
@ -95,7 +95,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
: []
),
{
name: t('common.appMenus.overview'),
name: t('appMenus.overview', { ns: 'common' }),
href: `/app/${appId}/overview`,
icon: RiDashboard2Line,
selectedIcon: RiDashboard2Fill,
@ -104,7 +104,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return navConfig
}, [t])
useDocumentTitle(appDetail?.name || t('common.menus.appDetail'))
useDocumentTitle(appDetail?.name || t('menus.appDetail', { ns: 'common' }))
useEffect(() => {
if (appDetail) {

View File

@ -4,6 +4,7 @@ import type { IAppCardProps } from '@/app/components/app/overview/app-card'
import type { BlockEnum } from '@/app/components/workflow/types'
import type { UpdateAppSiteCodeResponse } from '@/models/app'
import type { App } from '@/types/app'
import type { I18nKeysByPrefix } from '@/types/i18n'
import * as React from 'react'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -62,7 +63,7 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
const buildTriggerModeMessage = useCallback((featureName: string) => (
<div className="flex flex-col gap-1">
<div className="text-xs text-text-secondary">
{t('appOverview.overview.disableTooltip.triggerMode', { feature: featureName })}
{t('overview.disableTooltip.triggerMode', { ns: 'appOverview', feature: featureName })}
</div>
<div
className="cursor-pointer text-xs font-medium text-text-accent hover:underline"
@ -71,19 +72,19 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
window.open(triggerDocUrl, '_blank')
}}
>
{t('appOverview.overview.appInfo.enableTooltip.learnMore')}
{t('overview.appInfo.enableTooltip.learnMore', { ns: 'appOverview' })}
</div>
</div>
), [t, triggerDocUrl])
const disableWebAppTooltip = disableAppCards
? buildTriggerModeMessage(t('appOverview.overview.appInfo.title'))
? buildTriggerModeMessage(t('overview.appInfo.title', { ns: 'appOverview' }))
: null
const disableApiTooltip = disableAppCards
? buildTriggerModeMessage(t('appOverview.overview.apiInfo.title'))
? buildTriggerModeMessage(t('overview.apiInfo.title', { ns: 'appOverview' }))
: null
const disableMcpTooltip = disableAppCards
? buildTriggerModeMessage(t('tools.mcp.server.title'))
? buildTriggerModeMessage(t('mcp.server.title', { ns: 'tools' }))
: null
const updateAppDetail = async () => {
@ -94,7 +95,7 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
catch (error) { console.error(error) }
}
const handleCallbackResult = (err: Error | null, message?: string) => {
const handleCallbackResult = (err: Error | null, message?: I18nKeysByPrefix<'common', 'actionMsg.'>) => {
const type = err ? 'error' : 'success'
message ||= (type === 'success' ? 'modifiedSuccessfully' : 'modifiedUnsuccessfully')
@ -104,7 +105,7 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
notify({
type,
message: t(`common.actionMsg.${message}` as any) as string,
message: t(`actionMsg.${message}`, { ns: 'common' }) as string,
})
}

View File

@ -1,5 +1,6 @@
'use client'
import type { PeriodParams } from '@/app/components/app/overview/app-chart'
import type { I18nKeysByPrefix } from '@/types/i18n'
import dayjs from 'dayjs'
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import * as React from 'react'
@ -16,7 +17,9 @@ dayjs.extend(quarterOfYear)
const today = dayjs()
const TIME_PERIOD_MAPPING = [
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
const TIME_PERIOD_MAPPING: { value: number, name: TimePeriodName }[] = [
{ value: 0, name: 'today' },
{ value: 7, name: 'last7days' },
{ value: 30, name: 'last30days' },
@ -35,8 +38,8 @@ export default function ChartView({ appId, headerRight }: IChartViewProps) {
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
const isWorkflow = appDetail?.mode === 'workflow'
const [period, setPeriod] = useState<PeriodParams>(IS_CLOUD_EDITION
? { name: t('appLog.filter.period.today'), query: { start: today.startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } }
: { name: t('appLog.filter.period.last7days'), query: { start: today.subtract(7, 'day').startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } },
? { name: t('filter.period.today', { ns: 'appLog' }), query: { start: today.startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } }
: { name: t('filter.period.last7days', { ns: 'appLog' }), query: { start: today.subtract(7, 'day').startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } },
)
if (!appDetail)
@ -45,7 +48,7 @@ export default function ChartView({ appId, headerRight }: IChartViewProps) {
return (
<div>
<div className="mb-4">
<div className="system-xl-semibold mb-2 text-text-primary">{t('common.appMenus.overview')}</div>
<div className="system-xl-semibold mb-2 text-text-primary">{t('appMenus.overview', { ns: 'common' })}</div>
<div className="flex items-center justify-between">
{IS_CLOUD_EDITION
? (

View File

@ -2,13 +2,16 @@
import type { FC } from 'react'
import type { PeriodParams } from '@/app/components/app/overview/app-chart'
import type { Item } from '@/app/components/base/select'
import type { I18nKeysByPrefix } from '@/types/i18n'
import dayjs from 'dayjs'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { SimpleSelect } from '@/app/components/base/select'
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
type Props = {
periodMapping: { [key: string]: { value: number, name: string } }
periodMapping: { [key: string]: { value: number, name: TimePeriodName } }
onSelect: (payload: PeriodParams) => void
queryDateFormat: string
}
@ -25,9 +28,9 @@ const LongTimeRangePicker: FC<Props> = ({
const handleSelect = React.useCallback((item: Item) => {
const id = item.value
const value = periodMapping[id]?.value ?? '-1'
const name = item.name || t('appLog.filter.period.allTime')
const name = item.name || t('filter.period.allTime', { ns: 'appLog' })
if (value === -1) {
onSelect({ name: t('appLog.filter.period.allTime'), query: undefined })
onSelect({ name: t('filter.period.allTime', { ns: 'appLog' }), query: undefined })
}
else if (value === 0) {
const startOfToday = today.startOf('day').format(queryDateFormat)
@ -53,7 +56,7 @@ const LongTimeRangePicker: FC<Props> = ({
return (
<SimpleSelect
items={Object.entries(periodMapping).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}` as any) as string }))}
items={Object.entries(periodMapping).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}`, { ns: 'appLog' }) }))}
className="mt-0 !w-40"
notClearable={true}
onSelect={handleSelect}

View File

@ -2,6 +2,7 @@
import type { Dayjs } from 'dayjs'
import type { FC } from 'react'
import type { PeriodParams, PeriodParamsWithTimeRange } from '@/app/components/app/overview/app-chart'
import type { I18nKeysByPrefix } from '@/types/i18n'
import dayjs from 'dayjs'
import * as React from 'react'
import { useCallback, useState } from 'react'
@ -13,8 +14,10 @@ import RangeSelector from './range-selector'
const today = dayjs()
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
type Props = {
ranges: { value: number, name: string }[]
ranges: { value: number, name: TimePeriodName }[]
onSelect: (payload: PeriodParams) => void
queryDateFormat: string
}

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { PeriodParamsWithTimeRange, TimeRange } from '@/app/components/app/overview/app-chart'
import type { Item } from '@/app/components/base/select'
import type { I18nKeysByPrefix } from '@/types/i18n'
import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react'
import dayjs from 'dayjs'
import * as React from 'react'
@ -12,9 +13,11 @@ import { cn } from '@/utils/classnames'
const today = dayjs()
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
type Props = {
isCustomRange: boolean
ranges: { value: number, name: string }[]
ranges: { value: number, name: TimePeriodName }[]
onSelect: (payload: PeriodParamsWithTimeRange) => void
}
@ -42,7 +45,7 @@ const RangeSelector: FC<Props> = ({
const renderTrigger = useCallback((item: Item | null, isOpen: boolean) => {
return (
<div className={cn('flex h-8 cursor-pointer items-center space-x-1.5 rounded-lg bg-components-input-bg-normal pl-3 pr-2', isOpen && 'bg-state-base-hover-alt')}>
<div className="system-sm-regular text-components-input-text-filled">{isCustomRange ? t('appLog.filter.period.custom') : item?.name}</div>
<div className="system-sm-regular text-components-input-text-filled">{isCustomRange ? t('filter.period.custom', { ns: 'appLog' }) : item?.name}</div>
<RiArrowDownSLine className={cn('size-4 text-text-quaternary', isOpen && 'text-text-secondary')} />
</div>
)
@ -66,7 +69,7 @@ const RangeSelector: FC<Props> = ({
}, [])
return (
<SimpleSelect
items={ranges.map(v => ({ ...v, name: t(`appLog.filter.period.${v.name}` as any) as string }))}
items={ranges.map(v => ({ ...v, name: t(`filter.period.${v.name}`, { ns: 'appLog' }) }))}
className="mt-0 !w-40"
notClearable={true}
onSelect={handleSelectRange}

View File

@ -15,7 +15,7 @@ import ProviderPanel from './provider-panel'
import TracingIcon from './tracing-icon'
import { TracingProvider } from './type'
const I18N_PREFIX = 'app.tracing'
const I18N_PREFIX = 'tracing'
export type PopupProps = {
appId: string
@ -327,19 +327,19 @@ const ConfigPopup: FC<PopupProps> = ({
<div className="flex items-center justify-between">
<div className="flex items-center">
<TracingIcon size="md" className="mr-2" />
<div className="title-2xl-semi-bold text-text-primary">{t(`${I18N_PREFIX}.tracing`)}</div>
<div className="title-2xl-semi-bold text-text-primary">{t(`${I18N_PREFIX}.tracing`, { ns: 'app' })}</div>
</div>
<div className="flex items-center">
<Indicator color={enabled ? 'green' : 'gray'} />
<div className={cn('system-xs-semibold-uppercase ml-1 text-text-tertiary', enabled && 'text-util-colors-green-green-600')}>
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`, { ns: 'app' })}
</div>
{!readOnly && (
<>
{providerAllNotConfigured
? (
<Tooltip
popupContent={t(`${I18N_PREFIX}.disabledTip`)}
popupContent={t(`${I18N_PREFIX}.disabledTip`, { ns: 'app' })}
>
{switchContent}
</Tooltip>
@ -351,14 +351,14 @@ const ConfigPopup: FC<PopupProps> = ({
</div>
<div className="system-xs-regular mt-2 text-text-tertiary">
{t(`${I18N_PREFIX}.tracingDescription`)}
{t(`${I18N_PREFIX}.tracingDescription`, { ns: 'app' })}
</div>
<Divider className="my-3" />
<div className="relative">
{(providerAllConfigured || providerAllNotConfigured)
? (
<>
<div className="system-xs-medium-uppercase text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}</div>
<div className="system-xs-medium-uppercase text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`, { ns: 'app' })}</div>
<div className="mt-2 max-h-96 space-y-2 overflow-y-auto">
{langfusePanel}
{langSmithPanel}
@ -375,11 +375,11 @@ const ConfigPopup: FC<PopupProps> = ({
)
: (
<>
<div className="system-xs-medium-uppercase text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.configured`)}</div>
<div className="system-xs-medium-uppercase text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.configured`, { ns: 'app' })}</div>
<div className="mt-2 max-h-40 space-y-2 overflow-y-auto">
{configuredProviderPanel()}
</div>
<div className="system-xs-medium-uppercase mt-3 text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}</div>
<div className="system-xs-medium-uppercase mt-3 text-text-tertiary">{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`, { ns: 'app' })}</div>
<div className="mt-2 max-h-40 space-y-2 overflow-y-auto">
{moreProviderPanel()}
</div>

View File

@ -23,7 +23,7 @@ import ConfigButton from './config-button'
import TracingIcon from './tracing-icon'
import { TracingProvider } from './type'
const I18N_PREFIX = 'app.tracing'
const I18N_PREFIX = 'tracing'
const Panel: FC = () => {
const { t } = useTranslation()
@ -45,7 +45,7 @@ const Panel: FC = () => {
if (!noToast) {
Toast.notify({
type: 'success',
message: t('common.api.success'),
message: t('api.success', { ns: 'common' }),
})
}
}
@ -254,7 +254,7 @@ const Panel: FC = () => {
)}
>
<TracingIcon size="md" />
<div className="system-sm-semibold mx-2 text-text-secondary">{t(`${I18N_PREFIX}.title`)}</div>
<div className="system-sm-semibold mx-2 text-text-secondary">{t(`${I18N_PREFIX}.title`, { ns: 'app' })}</div>
<div className="rounded-md p-1">
<RiEqualizer2Line className="h-4 w-4 text-text-tertiary" />
</div>
@ -295,7 +295,7 @@ const Panel: FC = () => {
<div className="ml-4 mr-1 flex items-center">
<Indicator color={enabled ? 'green' : 'gray'} />
<div className="system-xs-semibold-uppercase ml-1.5 text-text-tertiary">
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`, { ns: 'app' })}
</div>
</div>
{InUseProviderIcon && <InUseProviderIcon className="ml-1 h-4" />}

View File

@ -30,7 +30,7 @@ type Props = {
onChosen: (provider: TracingProvider) => void
}
const I18N_PREFIX = 'app.tracing.configProvider'
const I18N_PREFIX = 'tracing.configProvider'
const arizeConfigTemplate = {
api_key: '',
@ -157,7 +157,7 @@ const ProviderConfigModal: FC<Props> = ({
})
Toast.notify({
type: 'success',
message: t('common.api.remove'),
message: t('api.remove', { ns: 'common' }),
})
onRemoved()
hideRemoveConfirm()
@ -177,37 +177,37 @@ const ProviderConfigModal: FC<Props> = ({
if (type === TracingProvider.arize) {
const postData = config as ArizeConfig
if (!postData.api_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'API Key' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'API Key' })
if (!postData.space_id)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Space ID' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Space ID' })
if (!errorMessage && !postData.project)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })
}
if (type === TracingProvider.phoenix) {
const postData = config as PhoenixConfig
if (!postData.api_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'API Key' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'API Key' })
if (!errorMessage && !postData.project)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })
}
if (type === TracingProvider.langSmith) {
const postData = config as LangSmithConfig
if (!postData.api_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'API Key' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'API Key' })
if (!errorMessage && !postData.project)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })
}
if (type === TracingProvider.langfuse) {
const postData = config as LangFuseConfig
if (!errorMessage && !postData.secret_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.secretKey`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.secretKey`, { ns: 'app' }) })
if (!errorMessage && !postData.public_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.publicKey`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.publicKey`, { ns: 'app' }) })
if (!errorMessage && !postData.host)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Host' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Host' })
}
if (type === TracingProvider.opik) {
@ -218,43 +218,43 @@ const ProviderConfigModal: FC<Props> = ({
if (type === TracingProvider.weave) {
const postData = config as WeaveConfig
if (!errorMessage && !postData.api_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'API Key' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'API Key' })
if (!errorMessage && !postData.project)
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })
}
if (type === TracingProvider.aliyun) {
const postData = config as AliyunConfig
if (!errorMessage && !postData.app_name)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'App Name' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'App Name' })
if (!errorMessage && !postData.license_key)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'License Key' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'License Key' })
if (!errorMessage && !postData.endpoint)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Endpoint' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Endpoint' })
}
if (type === TracingProvider.mlflow) {
const postData = config as MLflowConfig
if (!errorMessage && !postData.tracking_uri)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Tracking URI' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Tracking URI' })
}
if (type === TracingProvider.databricks) {
const postData = config as DatabricksConfig
if (!errorMessage && !postData.experiment_id)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Experiment ID' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Experiment ID' })
if (!errorMessage && !postData.host)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Host' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Host' })
}
if (type === TracingProvider.tencent) {
const postData = config as TencentConfig
if (!errorMessage && !postData.token)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Token' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Token' })
if (!errorMessage && !postData.endpoint)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Endpoint' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Endpoint' })
if (!errorMessage && !postData.service_name)
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Service Name' })
errorMessage = t('errorMsg.fieldRequired', { ns: 'common', field: 'Service Name' })
}
return errorMessage
@ -281,7 +281,7 @@ const ProviderConfigModal: FC<Props> = ({
})
Toast.notify({
type: 'success',
message: t('common.api.success'),
message: t('api.success', { ns: 'common' }),
})
onSaved(config)
if (isAdd)
@ -303,8 +303,8 @@ const ProviderConfigModal: FC<Props> = ({
<div className="px-8 pt-8">
<div className="mb-4 flex items-center justify-between">
<div className="title-2xl-semi-bold text-text-primary">
{t(`${I18N_PREFIX}.title`)}
{t(`app.tracing.${type}.title`)}
{t(`${I18N_PREFIX}.title`, { ns: 'app' })}
{t(`tracing.${type}.title`, { ns: 'app' })}
</div>
</div>
@ -317,7 +317,7 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as ArizeConfig).api_key}
onChange={handleConfigChange('api_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'API Key' })!}
/>
<Field
label="Space ID"
@ -325,15 +325,15 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as ArizeConfig).space_id}
onChange={handleConfigChange('space_id')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'Space ID' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'Space ID' })!}
/>
<Field
label={t(`${I18N_PREFIX}.project`)!}
label={t(`${I18N_PREFIX}.project`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as ArizeConfig).project}
onChange={handleConfigChange('project')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })!}
/>
<Field
label="Endpoint"
@ -352,15 +352,15 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as PhoenixConfig).api_key}
onChange={handleConfigChange('api_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'API Key' })!}
/>
<Field
label={t(`${I18N_PREFIX}.project`)!}
label={t(`${I18N_PREFIX}.project`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as PhoenixConfig).project}
onChange={handleConfigChange('project')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })!}
/>
<Field
label="Endpoint"
@ -379,7 +379,7 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as AliyunConfig).license_key}
onChange={handleConfigChange('license_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'License Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'License Key' })!}
/>
<Field
label="Endpoint"
@ -404,7 +404,7 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as TencentConfig).token}
onChange={handleConfigChange('token')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'Token' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'Token' })!}
/>
<Field
label="Endpoint"
@ -432,22 +432,22 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as WeaveConfig).api_key}
onChange={handleConfigChange('api_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'API Key' })!}
/>
<Field
label={t(`${I18N_PREFIX}.project`)!}
label={t(`${I18N_PREFIX}.project`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as WeaveConfig).project}
onChange={handleConfigChange('project')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })!}
/>
<Field
label="Entity"
labelClassName="!text-sm"
value={(config as WeaveConfig).entity}
onChange={handleConfigChange('entity')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'Entity' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'Entity' })!}
/>
<Field
label="Endpoint"
@ -473,15 +473,15 @@ const ProviderConfigModal: FC<Props> = ({
isRequired
value={(config as LangSmithConfig).api_key}
onChange={handleConfigChange('api_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'API Key' })!}
/>
<Field
label={t(`${I18N_PREFIX}.project`)!}
label={t(`${I18N_PREFIX}.project`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as LangSmithConfig).project}
onChange={handleConfigChange('project')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })!}
/>
<Field
label="Endpoint"
@ -495,20 +495,20 @@ const ProviderConfigModal: FC<Props> = ({
{type === TracingProvider.langfuse && (
<>
<Field
label={t(`${I18N_PREFIX}.secretKey`)!}
label={t(`${I18N_PREFIX}.secretKey`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as LangFuseConfig).secret_key}
isRequired
onChange={handleConfigChange('secret_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.secretKey`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.secretKey`, { ns: 'app' }) })!}
/>
<Field
label={t(`${I18N_PREFIX}.publicKey`)!}
label={t(`${I18N_PREFIX}.publicKey`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as LangFuseConfig).public_key}
onChange={handleConfigChange('public_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.publicKey`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.publicKey`, { ns: 'app' }) })!}
/>
<Field
label="Host"
@ -527,14 +527,14 @@ const ProviderConfigModal: FC<Props> = ({
labelClassName="!text-sm"
value={(config as OpikConfig).api_key}
onChange={handleConfigChange('api_key')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: 'API Key' })!}
/>
<Field
label={t(`${I18N_PREFIX}.project`)!}
label={t(`${I18N_PREFIX}.project`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as OpikConfig).project}
onChange={handleConfigChange('project')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.project`, { ns: 'app' }) })!}
/>
<Field
label="Workspace"
@ -555,7 +555,7 @@ const ProviderConfigModal: FC<Props> = ({
{type === TracingProvider.mlflow && (
<>
<Field
label={t(`${I18N_PREFIX}.trackingUri`)!}
label={t(`${I18N_PREFIX}.trackingUri`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as MLflowConfig).tracking_uri}
isRequired
@ -563,67 +563,67 @@ const ProviderConfigModal: FC<Props> = ({
placeholder="http://localhost:5000"
/>
<Field
label={t(`${I18N_PREFIX}.experimentId`)!}
label={t(`${I18N_PREFIX}.experimentId`, { ns: 'app' })!}
labelClassName="!text-sm"
isRequired
value={(config as MLflowConfig).experiment_id}
onChange={handleConfigChange('experiment_id')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.experimentId`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.experimentId`, { ns: 'app' }) })!}
/>
<Field
label={t(`${I18N_PREFIX}.username`)!}
label={t(`${I18N_PREFIX}.username`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as MLflowConfig).username}
onChange={handleConfigChange('username')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.username`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.username`, { ns: 'app' }) })!}
/>
<Field
label={t(`${I18N_PREFIX}.password`)!}
label={t(`${I18N_PREFIX}.password`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as MLflowConfig).password}
onChange={handleConfigChange('password')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.password`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.password`, { ns: 'app' }) })!}
/>
</>
)}
{type === TracingProvider.databricks && (
<>
<Field
label={t(`${I18N_PREFIX}.experimentId`)!}
label={t(`${I18N_PREFIX}.experimentId`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as DatabricksConfig).experiment_id}
onChange={handleConfigChange('experiment_id')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.experimentId`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.experimentId`, { ns: 'app' }) })!}
isRequired
/>
<Field
label={t(`${I18N_PREFIX}.databricksHost`)!}
label={t(`${I18N_PREFIX}.databricksHost`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as DatabricksConfig).host}
onChange={handleConfigChange('host')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.databricksHost`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.databricksHost`, { ns: 'app' }) })!}
isRequired
/>
<Field
label={t(`${I18N_PREFIX}.clientId`)!}
label={t(`${I18N_PREFIX}.clientId`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as DatabricksConfig).client_id}
onChange={handleConfigChange('client_id')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.clientId`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.clientId`, { ns: 'app' }) })!}
/>
<Field
label={t(`${I18N_PREFIX}.clientSecret`)!}
label={t(`${I18N_PREFIX}.clientSecret`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as DatabricksConfig).client_secret}
onChange={handleConfigChange('client_secret')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.clientSecret`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.clientSecret`, { ns: 'app' }) })!}
/>
<Field
label={t(`${I18N_PREFIX}.personalAccessToken`)!}
label={t(`${I18N_PREFIX}.personalAccessToken`, { ns: 'app' })!}
labelClassName="!text-sm"
value={(config as DatabricksConfig).personal_access_token}
onChange={handleConfigChange('personal_access_token')}
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.personalAccessToken`) })!}
placeholder={t(`${I18N_PREFIX}.placeholder`, { ns: 'app', key: t(`${I18N_PREFIX}.personalAccessToken`, { ns: 'app' }) })!}
/>
</>
)}
@ -634,7 +634,7 @@ const ProviderConfigModal: FC<Props> = ({
target="_blank"
href={docURL[type]}
>
<span>{t(`${I18N_PREFIX}.viewDocsLink`, { key: t(`app.tracing.${type}.title`) })}</span>
<span>{t(`${I18N_PREFIX}.viewDocsLink`, { ns: 'app', key: t(`tracing.${type}.title`, { ns: 'app' }) })}</span>
<LinkExternal02 className="h-3 w-3" />
</a>
<div className="flex items-center">
@ -644,7 +644,7 @@ const ProviderConfigModal: FC<Props> = ({
className="h-9 text-sm font-medium text-text-secondary"
onClick={showRemoveConfirm}
>
<span className="text-[#D92D20]">{t('common.operation.remove')}</span>
<span className="text-[#D92D20]">{t('operation.remove', { ns: 'common' })}</span>
</Button>
<Divider type="vertical" className="mx-3 h-[18px]" />
</>
@ -653,7 +653,7 @@ const ProviderConfigModal: FC<Props> = ({
className="mr-2 h-9 text-sm font-medium text-text-secondary"
onClick={onCancel}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
className="h-9 text-sm font-medium"
@ -661,7 +661,7 @@ const ProviderConfigModal: FC<Props> = ({
onClick={handleSave}
loading={isSaving}
>
{t(`common.operation.${isAdd ? 'saveAndEnable' : 'save'}`)}
{t(`operation.${isAdd ? 'saveAndEnable' : 'save'}`, { ns: 'common' })}
</Button>
</div>
@ -670,7 +670,7 @@ const ProviderConfigModal: FC<Props> = ({
<div className="border-t-[0.5px] border-divider-regular">
<div className="flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary">
<Lock01 className="mr-1 h-3 w-3 text-text-tertiary" />
{t('common.modelProvider.encrypted.front')}
{t('modelProvider.encrypted.front', { ns: 'common' })}
<a
className="mx-1 text-primary-600"
target="_blank"
@ -679,7 +679,7 @@ const ProviderConfigModal: FC<Props> = ({
>
PKCS1_OAEP
</a>
{t('common.modelProvider.encrypted.back')}
{t('modelProvider.encrypted.back', { ns: 'common' })}
</div>
</div>
</div>
@ -691,8 +691,8 @@ const ProviderConfigModal: FC<Props> = ({
<Confirm
isShow
type="warning"
title={t(`${I18N_PREFIX}.removeConfirmTitle`, { key: t(`app.tracing.${type}.title`) })!}
content={t(`${I18N_PREFIX}.removeConfirmContent`)}
title={t(`${I18N_PREFIX}.removeConfirmTitle`, { ns: 'app', key: t(`tracing.${type}.title`, { ns: 'app' }) })!}
content={t(`${I18N_PREFIX}.removeConfirmContent`, { ns: 'app' })}
onConfirm={handleRemove}
onCancel={hideRemoveConfirm}
/>

View File

@ -11,7 +11,7 @@ import { Eye as View } from '@/app/components/base/icons/src/vender/solid/genera
import { cn } from '@/utils/classnames'
import { TracingProvider } from './type'
const I18N_PREFIX = 'app.tracing'
const I18N_PREFIX = 'tracing'
type Props = {
type: TracingProvider
@ -82,14 +82,14 @@ const ProviderPanel: FC<Props> = ({
<div className="flex items-center justify-between space-x-1">
<div className="flex items-center">
<Icon className="h-6" />
{isChosen && <div className="system-2xs-medium-uppercase ml-1 flex h-4 items-center rounded-[4px] border border-text-accent-secondary px-1 text-text-accent-secondary">{t(`${I18N_PREFIX}.inUse`)}</div>}
{isChosen && <div className="system-2xs-medium-uppercase ml-1 flex h-4 items-center rounded-[4px] border border-text-accent-secondary px-1 text-text-accent-secondary">{t(`${I18N_PREFIX}.inUse`, { ns: 'app' })}</div>}
</div>
{!readOnly && (
<div className="flex items-center justify-between space-x-1">
{hasConfigured && (
<div className="flex h-6 cursor-pointer items-center space-x-1 rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-text-secondary shadow-xs" onClick={viewBtnClick}>
<View className="h-3 w-3" />
<div className="text-xs font-medium">{t(`${I18N_PREFIX}.view`)}</div>
<div className="text-xs font-medium">{t(`${I18N_PREFIX}.view`, { ns: 'app' })}</div>
</div>
)}
<div
@ -97,13 +97,13 @@ const ProviderPanel: FC<Props> = ({
onClick={handleConfigBtnClick}
>
<RiEqualizer2Line className="h-3 w-3" />
<div className="text-xs font-medium">{t(`${I18N_PREFIX}.config`)}</div>
<div className="text-xs font-medium">{t(`${I18N_PREFIX}.config`, { ns: 'app' })}</div>
</div>
</div>
)}
</div>
<div className="system-xs-regular mt-2 text-text-tertiary">
{t(`${I18N_PREFIX}.${type}.description`)}
{t(`${I18N_PREFIX}.${type}.description`, { ns: 'app' })}
</div>
</div>
)

View File

@ -15,7 +15,7 @@ const AppDetail: FC<IAppDetail> = ({ children }) => {
const router = useRouter()
const { isCurrentWorkspaceDatasetOperator } = useAppContext()
const { t } = useTranslation()
useDocumentTitle(t('common.menus.appDetail'))
useDocumentTitle(t('menus.appDetail', { ns: 'common' }))
useEffect(() => {
if (isCurrentWorkspaceDatasetOperator)

View File

@ -70,14 +70,14 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const navigation = useMemo(() => {
const baseNavigation = [
{
name: t('common.datasetMenus.hitTesting'),
name: t('datasetMenus.hitTesting', { ns: 'common' }),
href: `/datasets/${datasetId}/hitTesting`,
icon: RiFocus2Line,
selectedIcon: RiFocus2Fill,
disabled: isButtonDisabledWithPipeline,
},
{
name: t('common.datasetMenus.settings'),
name: t('datasetMenus.settings', { ns: 'common' }),
href: `/datasets/${datasetId}/settings`,
icon: RiEqualizer2Line,
selectedIcon: RiEqualizer2Fill,
@ -87,14 +87,14 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
if (datasetRes?.provider !== 'external') {
baseNavigation.unshift({
name: t('common.datasetMenus.pipeline'),
name: t('datasetMenus.pipeline', { ns: 'common' }),
href: `/datasets/${datasetId}/pipeline`,
icon: PipelineLine as RemixiconComponentType,
selectedIcon: PipelineFill as RemixiconComponentType,
disabled: false,
})
baseNavigation.unshift({
name: t('common.datasetMenus.documents'),
name: t('datasetMenus.documents', { ns: 'common' }),
href: `/datasets/${datasetId}/documents`,
icon: RiFileTextLine,
selectedIcon: RiFileTextFill,
@ -105,7 +105,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return baseNavigation
}, [t, datasetId, isButtonDisabledWithPipeline, datasetRes?.provider])
useDocumentTitle(datasetRes?.name || t('common.menus.datasets'))
useDocumentTitle(datasetRes?.name || t('menus.datasets', { ns: 'common' }))
const setAppSidebarExpand = useStore(state => state.setAppSidebarExpand)

View File

@ -1,3 +1,4 @@
/* eslint-disable dify-i18n/require-ns-option */
import * as React from 'react'
import Form from '@/app/components/datasets/settings/form'
import { getLocaleOnServer, getTranslation } from '@/i18n-config/server'
@ -9,7 +10,7 @@ const Settings = async () => {
return (
<div className="h-full overflow-y-auto">
<div className="flex flex-col gap-y-0.5 px-6 pb-2 pt-3">
<div className="system-xl-semibold text-text-primary">{t('title') as any}</div>
<div className="system-xl-semibold text-text-primary">{t('title')}</div>
<div className="system-sm-regular text-text-tertiary">{t('desc')}</div>
</div>
<Form />

View File

@ -7,7 +7,7 @@ import useDocumentTitle from '@/hooks/use-document-title'
const ExploreLayout: FC<PropsWithChildren> = ({ children }) => {
const { t } = useTranslation()
useDocumentTitle(t('common.menus.explore'))
useDocumentTitle(t('menus.explore', { ns: 'common' }))
return (
<ExploreClient>
{children}

View File

@ -12,7 +12,7 @@ const ToolsList: FC = () => {
const router = useRouter()
const { isCurrentWorkspaceDatasetOperator } = useAppContext()
const { t } = useTranslation()
useDocumentTitle(t('common.menus.tools'))
useDocumentTitle(t('menus.tools', { ns: 'common' }))
useEffect(() => {
if (isCurrentWorkspaceDatasetOperator)

View File

@ -81,7 +81,7 @@ const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => {
return (
<div className="flex h-full flex-col items-center justify-center gap-y-2">
<AppUnavailable className="h-auto w-auto" code={403} unknownReason="no permission." />
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{t('common.userProfile.logout')}</span>
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{t('userProfile.logout', { ns: 'common' })}</span>
</div>
)
}

View File

@ -94,8 +94,8 @@ const Splash: FC<PropsWithChildren> = ({ children }) => {
if (message) {
return (
<div className="flex h-full flex-col items-center justify-center gap-y-4">
<AppUnavailable className="h-auto w-auto" code={code || t('share.common.appUnavailable')} unknownReason={message} />
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{code === '403' ? t('common.userProfile.logout') : t('share.login.backToHome')}</span>
<AppUnavailable className="h-auto w-auto" code={code || t('common.appUnavailable', { ns: 'share' })} unknownReason={message} />
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{code === '403' ? t('userProfile.logout', { ns: 'common' }) : t('login.backToHome', { ns: 'share' })}</span>
</div>
)
}

View File

@ -26,14 +26,14 @@ export default function CheckCode() {
if (!code.trim()) {
Toast.notify({
type: 'error',
message: t('login.checkCode.emptyCode'),
message: t('checkCode.emptyCode', { ns: 'login' }),
})
return
}
if (!/\d{6}/.test(code)) {
Toast.notify({
type: 'error',
message: t('login.checkCode.invalidCode'),
message: t('checkCode.invalidCode', { ns: 'login' }),
})
return
}
@ -69,22 +69,22 @@ export default function CheckCode() {
<RiMailSendFill className="h-6 w-6 text-2xl" />
</div>
<div className="pb-4 pt-2">
<h2 className="title-4xl-semi-bold text-text-primary">{t('login.checkCode.checkYourEmail')}</h2>
<h2 className="title-4xl-semi-bold text-text-primary">{t('checkCode.checkYourEmail', { ns: 'login' })}</h2>
<p className="body-md-regular mt-2 text-text-secondary">
<span>
{t('login.checkCode.tipsPrefix')}
{t('checkCode.tipsPrefix', { ns: 'login' })}
<strong>{email}</strong>
</span>
<br />
{t('login.checkCode.validTime')}
{t('checkCode.validTime', { ns: 'login' })}
</p>
</div>
<form action="">
<input type="text" className="hidden" />
<label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('login.checkCode.verificationCode')}</label>
<Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className="mt-1" placeholder={t('login.checkCode.verificationCodePlaceholder') || ''} />
<Button loading={loading} disabled={loading} className="my-3 w-full" variant="primary" onClick={verify}>{t('login.checkCode.verify')}</Button>
<label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('checkCode.verificationCode', { ns: 'login' })}</label>
<Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className="mt-1" placeholder={t('checkCode.verificationCodePlaceholder', { ns: 'login' }) || ''} />
<Button loading={loading} disabled={loading} className="my-3 w-full" variant="primary" onClick={verify}>{t('checkCode.verify', { ns: 'login' })}</Button>
<Countdown onResend={resendCode} />
</form>
<div className="py-2">
@ -94,7 +94,7 @@ export default function CheckCode() {
<div className="bg-background-default-dimm inline-block rounded-full p-1">
<RiArrowLeftLine size={12} />
</div>
<span className="system-xs-regular ml-2">{t('login.back')}</span>
<span className="system-xs-regular ml-2">{t('back', { ns: 'login' })}</span>
</div>
</div>
)

View File

@ -27,14 +27,14 @@ export default function CheckCode() {
const handleGetEMailVerificationCode = async () => {
try {
if (!email) {
Toast.notify({ type: 'error', message: t('login.error.emailEmpty') })
Toast.notify({ type: 'error', message: t('error.emailEmpty', { ns: 'login' }) })
return
}
if (!emailRegex.test(email)) {
Toast.notify({
type: 'error',
message: t('login.error.emailInValid'),
message: t('error.emailInValid', { ns: 'login' }),
})
return
}
@ -50,7 +50,7 @@ export default function CheckCode() {
else if (res.code === 'account_not_found') {
Toast.notify({
type: 'error',
message: t('login.error.registrationNotAllowed'),
message: t('error.registrationNotAllowed', { ns: 'login' }),
})
}
else {
@ -74,21 +74,21 @@ export default function CheckCode() {
<RiLockPasswordLine className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
</div>
<div className="pb-4 pt-2">
<h2 className="title-4xl-semi-bold text-text-primary">{t('login.resetPassword')}</h2>
<h2 className="title-4xl-semi-bold text-text-primary">{t('resetPassword', { ns: 'login' })}</h2>
<p className="body-md-regular mt-2 text-text-secondary">
{t('login.resetPasswordDesc')}
{t('resetPasswordDesc', { ns: 'login' })}
</p>
</div>
<form onSubmit={noop}>
<input type="text" className="hidden" />
<div className="mb-2">
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('login.email')}</label>
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('email', { ns: 'login' })}</label>
<div className="mt-1">
<Input id="email" type="email" disabled={loading} value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} />
<Input id="email" type="email" disabled={loading} value={email} placeholder={t('emailPlaceholder', { ns: 'login' }) as string} onChange={e => setEmail(e.target.value)} />
</div>
<div className="mt-3">
<Button loading={loading} disabled={loading} variant="primary" className="w-full" onClick={handleGetEMailVerificationCode}>{t('login.sendVerificationCode')}</Button>
<Button loading={loading} disabled={loading} variant="primary" className="w-full" onClick={handleGetEMailVerificationCode}>{t('sendVerificationCode', { ns: 'login' })}</Button>
</div>
</div>
</form>
@ -99,7 +99,7 @@ export default function CheckCode() {
<div className="inline-block rounded-full bg-background-default-dimmed p-1">
<RiArrowLeftLine size={12} />
</div>
<span className="system-xs-regular ml-2">{t('login.backToLogin')}</span>
<span className="system-xs-regular ml-2">{t('backToLogin', { ns: 'login' })}</span>
</Link>
</div>
)

View File

@ -45,15 +45,15 @@ const ChangePasswordForm = () => {
const valid = useCallback(() => {
if (!password.trim()) {
showErrorMessage(t('login.error.passwordEmpty'))
showErrorMessage(t('error.passwordEmpty', { ns: 'login' }))
return false
}
if (!validPassword.test(password)) {
showErrorMessage(t('login.error.passwordInvalid'))
showErrorMessage(t('error.passwordInvalid', { ns: 'login' }))
return false
}
if (password !== confirmPassword) {
showErrorMessage(t('common.account.notEqual'))
showErrorMessage(t('account.notEqual', { ns: 'common' }))
return false
}
return true
@ -92,10 +92,10 @@ const ChangePasswordForm = () => {
<div className="flex flex-col md:w-[400px]">
<div className="mx-auto w-full">
<h2 className="title-4xl-semi-bold text-text-primary">
{t('login.changePassword')}
{t('changePassword', { ns: 'login' })}
</h2>
<p className="body-md-regular mt-2 text-text-secondary">
{t('login.changePasswordTip')}
{t('changePasswordTip', { ns: 'login' })}
</p>
</div>
@ -104,7 +104,7 @@ const ChangePasswordForm = () => {
{/* Password */}
<div className="mb-5">
<label htmlFor="password" className="system-md-semibold my-2 text-text-secondary">
{t('common.account.newPassword')}
{t('account.newPassword', { ns: 'common' })}
</label>
<div className="relative mt-1">
<Input
@ -112,7 +112,7 @@ const ChangePasswordForm = () => {
type={showPassword ? 'text' : 'password'}
value={password}
onChange={e => setPassword(e.target.value)}
placeholder={t('login.passwordPlaceholder') || ''}
placeholder={t('passwordPlaceholder', { ns: 'login' }) || ''}
/>
<div className="absolute inset-y-0 right-0 flex items-center">
@ -125,12 +125,12 @@ const ChangePasswordForm = () => {
</Button>
</div>
</div>
<div className="body-xs-regular mt-1 text-text-secondary">{t('login.error.passwordInvalid')}</div>
<div className="body-xs-regular mt-1 text-text-secondary">{t('error.passwordInvalid', { ns: 'login' })}</div>
</div>
{/* Confirm Password */}
<div className="mb-5">
<label htmlFor="confirmPassword" className="system-md-semibold my-2 text-text-secondary">
{t('common.account.confirmPassword')}
{t('account.confirmPassword', { ns: 'common' })}
</label>
<div className="relative mt-1">
<Input
@ -138,7 +138,7 @@ const ChangePasswordForm = () => {
type={showConfirmPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
placeholder={t('login.confirmPasswordPlaceholder') || ''}
placeholder={t('confirmPasswordPlaceholder', { ns: 'login' }) || ''}
/>
<div className="absolute inset-y-0 right-0 flex items-center">
<Button
@ -157,7 +157,7 @@ const ChangePasswordForm = () => {
className="w-full"
onClick={handleChangePassword}
>
{t('login.changePasswordBtn')}
{t('changePasswordBtn', { ns: 'login' })}
</Button>
</div>
</div>
@ -171,7 +171,7 @@ const ChangePasswordForm = () => {
<RiCheckboxCircleFill className="h-6 w-6 text-text-success" />
</div>
<h2 className="title-4xl-semi-bold text-text-primary">
{t('login.passwordChangedTip')}
{t('passwordChangedTip', { ns: 'login' })}
</h2>
</div>
<div className="mx-auto mt-6 w-full">
@ -183,7 +183,7 @@ const ChangePasswordForm = () => {
router.replace(getSignInUrl())
}}
>
{t('login.passwordChanged')}
{t('passwordChanged', { ns: 'login' })}
{' '}
(
{Math.round(countdown / 1000)}

View File

@ -45,21 +45,21 @@ export default function CheckCode() {
if (!code.trim()) {
Toast.notify({
type: 'error',
message: t('login.checkCode.emptyCode'),
message: t('checkCode.emptyCode', { ns: 'login' }),
})
return
}
if (!/\d{6}/.test(code)) {
Toast.notify({
type: 'error',
message: t('login.checkCode.invalidCode'),
message: t('checkCode.invalidCode', { ns: 'login' }),
})
return
}
if (!redirectUrl || !appCode) {
Toast.notify({
type: 'error',
message: t('login.error.redirectUrlMissing'),
message: t('error.redirectUrlMissing', { ns: 'login' }),
})
return
}
@ -108,19 +108,19 @@ export default function CheckCode() {
<RiMailSendFill className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
</div>
<div className="pb-4 pt-2">
<h2 className="title-4xl-semi-bold text-text-primary">{t('login.checkCode.checkYourEmail')}</h2>
<h2 className="title-4xl-semi-bold text-text-primary">{t('checkCode.checkYourEmail', { ns: 'login' })}</h2>
<p className="body-md-regular mt-2 text-text-secondary">
<span>
{t('login.checkCode.tipsPrefix')}
{t('checkCode.tipsPrefix', { ns: 'login' })}
<strong>{email}</strong>
</span>
<br />
{t('login.checkCode.validTime')}
{t('checkCode.validTime', { ns: 'login' })}
</p>
</div>
<form onSubmit={handleSubmit}>
<label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('login.checkCode.verificationCode')}</label>
<label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('checkCode.verificationCode', { ns: 'login' })}</label>
<Input
ref={codeInputRef}
id="code"
@ -128,9 +128,9 @@ export default function CheckCode() {
onChange={e => setVerifyCode(e.target.value)}
maxLength={6}
className="mt-1"
placeholder={t('login.checkCode.verificationCodePlaceholder') || ''}
placeholder={t('checkCode.verificationCodePlaceholder', { ns: 'login' }) || ''}
/>
<Button type="submit" loading={loading} disabled={loading} className="my-3 w-full" variant="primary">{t('login.checkCode.verify')}</Button>
<Button type="submit" loading={loading} disabled={loading} className="my-3 w-full" variant="primary">{t('checkCode.verify', { ns: 'login' })}</Button>
<Countdown onResend={resendCode} />
</form>
<div className="py-2">
@ -140,7 +140,7 @@ export default function CheckCode() {
<div className="bg-background-default-dimm inline-block rounded-full p-1">
<RiArrowLeftLine size={12} />
</div>
<span className="system-xs-regular ml-2">{t('login.back')}</span>
<span className="system-xs-regular ml-2">{t('back', { ns: 'login' })}</span>
</div>
</div>
)

View File

@ -23,14 +23,14 @@ export default function MailAndCodeAuth() {
const handleGetEMailVerificationCode = async () => {
try {
if (!email) {
Toast.notify({ type: 'error', message: t('login.error.emailEmpty') })
Toast.notify({ type: 'error', message: t('error.emailEmpty', { ns: 'login' }) })
return
}
if (!emailRegex.test(email)) {
Toast.notify({
type: 'error',
message: t('login.error.emailInValid'),
message: t('error.emailInValid', { ns: 'login' }),
})
return
}
@ -56,12 +56,12 @@ export default function MailAndCodeAuth() {
<form onSubmit={noop}>
<input type="text" className="hidden" />
<div className="mb-2">
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('login.email')}</label>
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('email', { ns: 'login' })}</label>
<div className="mt-1">
<Input id="email" type="email" value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} />
<Input id="email" type="email" value={email} placeholder={t('emailPlaceholder', { ns: 'login' }) as string} onChange={e => setEmail(e.target.value)} />
</div>
<div className="mt-3">
<Button loading={loading} disabled={loading || !email} variant="primary" className="w-full" onClick={handleGetEMailVerificationCode}>{t('login.signup.verifyMail')}</Button>
<Button loading={loading} disabled={loading || !email} variant="primary" className="w-full" onClick={handleGetEMailVerificationCode}>{t('signup.verifyMail', { ns: 'login' })}</Button>
</div>
</div>
</form>

View File

@ -46,25 +46,25 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
const appCode = getAppCodeFromRedirectUrl()
const handleEmailPasswordLogin = async () => {
if (!email) {
Toast.notify({ type: 'error', message: t('login.error.emailEmpty') })
Toast.notify({ type: 'error', message: t('error.emailEmpty', { ns: 'login' }) })
return
}
if (!emailRegex.test(email)) {
Toast.notify({
type: 'error',
message: t('login.error.emailInValid'),
message: t('error.emailInValid', { ns: 'login' }),
})
return
}
if (!password?.trim()) {
Toast.notify({ type: 'error', message: t('login.error.passwordEmpty') })
Toast.notify({ type: 'error', message: t('error.passwordEmpty', { ns: 'login' }) })
return
}
if (!redirectUrl || !appCode) {
Toast.notify({
type: 'error',
message: t('login.error.redirectUrlMissing'),
message: t('error.redirectUrlMissing', { ns: 'login' }),
})
return
}
@ -111,7 +111,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
<form onSubmit={noop}>
<div className="mb-3">
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">
{t('login.email')}
{t('email', { ns: 'login' })}
</label>
<div className="mt-1">
<Input
@ -120,7 +120,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
id="email"
type="email"
autoComplete="email"
placeholder={t('login.emailPlaceholder') || ''}
placeholder={t('emailPlaceholder', { ns: 'login' }) || ''}
tabIndex={1}
/>
</div>
@ -128,14 +128,14 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
<div className="mb-3">
<label htmlFor="password" className="my-2 flex items-center justify-between">
<span className="system-md-semibold text-text-secondary">{t('login.password')}</span>
<span className="system-md-semibold text-text-secondary">{t('password', { ns: 'login' })}</span>
<Link
href={`/webapp-reset-password?${searchParams.toString()}`}
className={`system-xs-regular ${isEmailSetup ? 'text-components-button-secondary-accent-text' : 'pointer-events-none text-components-button-secondary-accent-text-disabled'}`}
tabIndex={isEmailSetup ? 0 : -1}
aria-disabled={!isEmailSetup}
>
{t('login.forget')}
{t('forget', { ns: 'login' })}
</Link>
</label>
<div className="relative mt-1">
@ -149,7 +149,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
}}
type={showPassword ? 'text' : 'password'}
autoComplete="current-password"
placeholder={t('login.passwordPlaceholder') || ''}
placeholder={t('passwordPlaceholder', { ns: 'login' }) || ''}
tabIndex={2}
/>
<div className="absolute inset-y-0 right-0 flex items-center">
@ -172,7 +172,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
disabled={isLoading || !email || !password}
className="w-full"
>
{t('login.signBtn')}
{t('signBtn', { ns: 'login' })}
</Button>
</div>
</form>

View File

@ -82,7 +82,7 @@ const SSOAuth: FC<SSOAuthProps> = ({
className="w-full"
>
<Lock01 className="mr-2 h-5 w-5 text-text-accent-light-mode-only" />
<span className="truncate">{t('login.withSSO')}</span>
<span className="truncate">{t('withSSO', { ns: 'login' })}</span>
</Button>
)
}

View File

@ -9,7 +9,7 @@ import { cn } from '@/utils/classnames'
export default function SignInLayout({ children }: PropsWithChildren) {
const { t } = useTranslation()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
useDocumentTitle(t('login.webapp.login'))
useDocumentTitle(t('webapp.login', { ns: 'login' }))
return (
<>
<div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>

View File

@ -60,8 +60,8 @@ const NormalForm = () => {
<RiContractLine className="h-5 w-5" />
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
</div>
<p className="system-sm-medium text-text-primary">{t('login.licenseLost')}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseLostTip')}</p>
<p className="system-sm-medium text-text-primary">{t('licenseLost', { ns: 'login' })}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseLostTip', { ns: 'login' })}</p>
</div>
</div>
</div>
@ -76,8 +76,8 @@ const NormalForm = () => {
<RiContractLine className="h-5 w-5" />
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
</div>
<p className="system-sm-medium text-text-primary">{t('login.licenseExpired')}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseExpiredTip')}</p>
<p className="system-sm-medium text-text-primary">{t('licenseExpired', { ns: 'login' })}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseExpiredTip', { ns: 'login' })}</p>
</div>
</div>
</div>
@ -92,8 +92,8 @@ const NormalForm = () => {
<RiContractLine className="h-5 w-5" />
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
</div>
<p className="system-sm-medium text-text-primary">{t('login.licenseInactive')}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseInactiveTip')}</p>
<p className="system-sm-medium text-text-primary">{t('licenseInactive', { ns: 'login' })}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseInactiveTip', { ns: 'login' })}</p>
</div>
</div>
</div>
@ -104,8 +104,8 @@ const NormalForm = () => {
<>
<div className="mx-auto mt-8 w-full">
<div className="mx-auto w-full">
<h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('login.pageTitleForE') : t('login.pageTitle')}</h2>
<p className="body-md-regular mt-2 text-text-tertiary">{t('login.welcome')}</p>
<h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('pageTitleForE', { ns: 'login' }) : t('pageTitle', { ns: 'login' })}</h2>
<p className="body-md-regular mt-2 text-text-tertiary">{t('welcome', { ns: 'login' })}</p>
</div>
<div className="relative">
<div className="mt-6 flex flex-col gap-3">
@ -122,7 +122,7 @@ const NormalForm = () => {
<div className="h-px w-full bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div>
</div>
<div className="relative flex justify-center">
<span className="system-xs-medium-uppercase px-2 text-text-tertiary">{t('login.or')}</span>
<span className="system-xs-medium-uppercase px-2 text-text-tertiary">{t('or', { ns: 'login' })}</span>
</div>
</div>
)}
@ -134,7 +134,7 @@ const NormalForm = () => {
<MailAndCodeAuth />
{systemFeatures.enable_email_password_login && (
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('password') }}>
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('login.usePassword')}</span>
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('usePassword', { ns: 'login' })}</span>
</div>
)}
</>
@ -144,7 +144,7 @@ const NormalForm = () => {
<MailAndPasswordAuth isEmailSetup={systemFeatures.is_email_setup} />
{systemFeatures.enable_email_code_login && (
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('code') }}>
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('login.useVerificationCode')}</span>
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('useVerificationCode', { ns: 'login' })}</span>
</div>
)}
</>
@ -158,8 +158,8 @@ const NormalForm = () => {
<div className="shadows-shadow-lg mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow">
<RiDoorLockLine className="h-5 w-5" />
</div>
<p className="system-sm-medium text-text-primary">{t('login.noLoginMethod')}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('login.noLoginMethodTip')}</p>
<p className="system-sm-medium text-text-primary">{t('noLoginMethod', { ns: 'login' })}</p>
<p className="system-xs-regular mt-1 text-text-tertiary">{t('noLoginMethodTip', { ns: 'login' })}</p>
</div>
<div className="relative my-2 py-2">
<div className="absolute inset-0 flex items-center" aria-hidden="true">
@ -171,7 +171,7 @@ const NormalForm = () => {
{!systemFeatures.branding.enabled && (
<>
<div className="system-xs-regular mt-2 block w-full text-text-tertiary">
{t('login.tosDesc')}
{t('tosDesc', { ns: 'login' })}
&nbsp;
<Link
className="system-xs-medium text-text-secondary hover:underline"
@ -179,7 +179,7 @@ const NormalForm = () => {
rel="noopener noreferrer"
href="https://dify.ai/terms"
>
{t('login.tos')}
{t('tos', { ns: 'login' })}
</Link>
&nbsp;&&nbsp;
<Link
@ -188,18 +188,18 @@ const NormalForm = () => {
rel="noopener noreferrer"
href="https://dify.ai/privacy"
>
{t('login.pp')}
{t('pp', { ns: 'login' })}
</Link>
</div>
{IS_CE_EDITION && (
<div className="w-hull system-xs-regular mt-2 block text-text-tertiary">
{t('login.goToInit')}
{t('goToInit', { ns: 'login' })}
&nbsp;
<Link
className="system-xs-medium text-text-secondary hover:underline"
href="/install"
>
{t('login.setAdminAccount')}
{t('setAdminAccount', { ns: 'login' })}
</Link>
</div>
)}

View File

@ -37,7 +37,7 @@ const WebSSOForm: FC = () => {
if (!redirectUrl) {
return (
<div className="flex h-full items-center justify-center">
<AppUnavailable code={t('share.common.appUnavailable')} unknownReason="redirect url is invalid." />
<AppUnavailable code={t('common.appUnavailable', { ns: 'share' })} unknownReason="redirect url is invalid." />
</div>
)
}
@ -45,7 +45,7 @@ const WebSSOForm: FC = () => {
if (!systemFeatures.webapp_auth.enabled) {
return (
<div className="flex h-full items-center justify-center">
<p className="system-xs-regular text-text-tertiary">{t('login.webapp.disabled')}</p>
<p className="system-xs-regular text-text-tertiary">{t('webapp.disabled', { ns: 'login' })}</p>
</div>
)
}
@ -63,7 +63,7 @@ const WebSSOForm: FC = () => {
return (
<div className="flex h-full flex-col items-center justify-center gap-y-4">
<AppUnavailable className="h-auto w-auto" isUnknownReason={true} />
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{t('share.login.backToHome')}</span>
<span className="system-sm-regular cursor-pointer text-text-tertiary" onClick={backToHome}>{t('login.backToHome', { ns: 'share' })}</span>
</div>
)
}

View File

@ -48,7 +48,7 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
await updateUserProfile({ url: 'account/avatar', body: { avatar: uploadedFileId } })
setIsShowAvatarPicker(false)
onSave?.()
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
}
catch (e) {
notify({ type: 'error', message: (e as Error).message })
@ -58,7 +58,7 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
const handleDeleteAvatar = useCallback(async () => {
try {
await updateUserProfile({ url: 'account/avatar', body: { avatar: '' } })
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
setIsShowDeleteConfirm(false)
onSave?.()
}
@ -145,11 +145,11 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
<div className="flex w-full items-center justify-center gap-2 p-3">
<Button className="w-full" onClick={() => setIsShowAvatarPicker(false)}>
{t('app.iconPicker.cancel')}
{t('iconPicker.cancel', { ns: 'app' })}
</Button>
<Button variant="primary" className="w-full" disabled={uploading || !inputImageInfo} loading={uploading} onClick={handleSelect}>
{t('app.iconPicker.ok')}
{t('iconPicker.ok', { ns: 'app' })}
</Button>
</div>
</Modal>
@ -160,16 +160,16 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => {
isShow={isShowDeleteConfirm}
onClose={() => setIsShowDeleteConfirm(false)}
>
<div className="title-2xl-semi-bold mb-3 text-text-primary">{t('common.avatar.deleteTitle')}</div>
<p className="mb-8 text-text-secondary">{t('common.avatar.deleteDescription')}</p>
<div className="title-2xl-semi-bold mb-3 text-text-primary">{t('avatar.deleteTitle', { ns: 'common' })}</div>
<p className="mb-8 text-text-secondary">{t('avatar.deleteDescription', { ns: 'common' })}</p>
<div className="flex w-full items-center justify-center gap-2">
<Button className="w-full" onClick={() => setIsShowDeleteConfirm(false)}>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button variant="warning" className="w-full" onClick={handleDeleteAvatar}>
{t('common.operation.delete')}
{t('operation.delete', { ns: 'common' })}
</Button>
</div>
</Modal>

View File

@ -209,9 +209,9 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
</div>
{step === STEP.start && (
<>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('common.account.changeEmail.title')}</div>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('account.changeEmail.title', { ns: 'common' })}</div>
<div className="space-y-0.5 pb-2 pt-1">
<div className="body-md-medium text-text-warning">{t('common.account.changeEmail.authTip')}</div>
<div className="body-md-medium text-text-warning">{t('account.changeEmail.authTip', { ns: 'common' })}</div>
<div className="body-md-regular text-text-secondary">
<Trans
i18nKey="common.account.changeEmail.content1"
@ -227,20 +227,20 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
variant="primary"
onClick={sendCodeToOriginEmail}
>
{t('common.account.changeEmail.sendVerifyCode')}
{t('account.changeEmail.sendVerifyCode', { ns: 'common' })}
</Button>
<Button
className="!w-full"
onClick={onClose}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
</div>
</>
)}
{step === STEP.verifyOrigin && (
<>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('common.account.changeEmail.verifyEmail')}</div>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('account.changeEmail.verifyEmail', { ns: 'common' })}</div>
<div className="space-y-0.5 pb-2 pt-1">
<div className="body-md-regular text-text-secondary">
<Trans
@ -251,10 +251,10 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
</div>
</div>
<div className="pt-3">
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('common.account.changeEmail.codeLabel')}</div>
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('account.changeEmail.codeLabel', { ns: 'common' })}</div>
<Input
className="!w-full"
placeholder={t('common.account.changeEmail.codePlaceholder')}
placeholder={t('account.changeEmail.codePlaceholder', { ns: 'common' })}
value={code}
onChange={e => setCode(e.target.value)}
maxLength={6}
@ -267,46 +267,46 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
variant="primary"
onClick={handleVerifyOriginEmail}
>
{t('common.account.changeEmail.continue')}
{t('account.changeEmail.continue', { ns: 'common' })}
</Button>
<Button
className="!w-full"
onClick={onClose}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
</div>
<div className="system-xs-regular mt-3 flex items-center gap-1 text-text-tertiary">
<span>{t('common.account.changeEmail.resendTip')}</span>
<span>{t('account.changeEmail.resendTip', { ns: 'common' })}</span>
{time > 0 && (
<span>{t('common.account.changeEmail.resendCount', { count: time })}</span>
<span>{t('account.changeEmail.resendCount', { ns: 'common', count: time })}</span>
)}
{!time && (
<span onClick={sendCodeToOriginEmail} className="system-xs-medium cursor-pointer text-text-accent-secondary">{t('common.account.changeEmail.resend')}</span>
<span onClick={sendCodeToOriginEmail} className="system-xs-medium cursor-pointer text-text-accent-secondary">{t('account.changeEmail.resend', { ns: 'common' })}</span>
)}
</div>
</>
)}
{step === STEP.newEmail && (
<>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('common.account.changeEmail.newEmail')}</div>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('account.changeEmail.newEmail', { ns: 'common' })}</div>
<div className="space-y-0.5 pb-2 pt-1">
<div className="body-md-regular text-text-secondary">{t('common.account.changeEmail.content3')}</div>
<div className="body-md-regular text-text-secondary">{t('account.changeEmail.content3', { ns: 'common' })}</div>
</div>
<div className="pt-3">
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('common.account.changeEmail.emailLabel')}</div>
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('account.changeEmail.emailLabel', { ns: 'common' })}</div>
<Input
className="!w-full"
placeholder={t('common.account.changeEmail.emailPlaceholder')}
placeholder={t('account.changeEmail.emailPlaceholder', { ns: 'common' })}
value={mail}
onChange={e => handleNewEmailValueChange(e.target.value)}
destructive={newEmailExited || unAvailableEmail}
/>
{newEmailExited && (
<div className="body-xs-regular mt-1 py-0.5 text-text-destructive">{t('common.account.changeEmail.existingEmail')}</div>
<div className="body-xs-regular mt-1 py-0.5 text-text-destructive">{t('account.changeEmail.existingEmail', { ns: 'common' })}</div>
)}
{unAvailableEmail && (
<div className="body-xs-regular mt-1 py-0.5 text-text-destructive">{t('common.account.changeEmail.unAvailableEmail')}</div>
<div className="body-xs-regular mt-1 py-0.5 text-text-destructive">{t('account.changeEmail.unAvailableEmail', { ns: 'common' })}</div>
)}
</div>
<div className="mt-3 space-y-2">
@ -316,20 +316,20 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
variant="primary"
onClick={sendCodeToNewEmail}
>
{t('common.account.changeEmail.sendVerifyCode')}
{t('account.changeEmail.sendVerifyCode', { ns: 'common' })}
</Button>
<Button
className="!w-full"
onClick={onClose}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
</div>
</>
)}
{step === STEP.verifyNew && (
<>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('common.account.changeEmail.verifyNew')}</div>
<div className="title-2xl-semi-bold pb-3 text-text-primary">{t('account.changeEmail.verifyNew', { ns: 'common' })}</div>
<div className="space-y-0.5 pb-2 pt-1">
<div className="body-md-regular text-text-secondary">
<Trans
@ -340,10 +340,10 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
</div>
</div>
<div className="pt-3">
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('common.account.changeEmail.codeLabel')}</div>
<div className="system-sm-medium mb-1 flex h-6 items-center text-text-secondary">{t('account.changeEmail.codeLabel', { ns: 'common' })}</div>
<Input
className="!w-full"
placeholder={t('common.account.changeEmail.codePlaceholder')}
placeholder={t('account.changeEmail.codePlaceholder', { ns: 'common' })}
value={code}
onChange={e => setCode(e.target.value)}
maxLength={6}
@ -356,22 +356,22 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
variant="primary"
onClick={submitNewEmail}
>
{t('common.account.changeEmail.changeTo', { email: mail })}
{t('account.changeEmail.changeTo', { ns: 'common', email: mail })}
</Button>
<Button
className="!w-full"
onClick={onClose}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
</div>
<div className="system-xs-regular mt-3 flex items-center gap-1 text-text-tertiary">
<span>{t('common.account.changeEmail.resendTip')}</span>
<span>{t('account.changeEmail.resendTip', { ns: 'common' })}</span>
{time > 0 && (
<span>{t('common.account.changeEmail.resendCount', { count: time })}</span>
<span>{t('account.changeEmail.resendCount', { ns: 'common', count: time })}</span>
)}
{!time && (
<span onClick={sendCodeToNewEmail} className="system-xs-medium cursor-pointer text-text-accent-secondary">{t('common.account.changeEmail.resend')}</span>
<span onClick={sendCodeToNewEmail} className="system-xs-medium cursor-pointer text-text-accent-secondary">{t('account.changeEmail.resend', { ns: 'common' })}</span>
)}
</div>
</>

View File

@ -61,7 +61,7 @@ export default function AccountPage() {
try {
setEditing(true)
await updateUserProfile({ url: 'account/name', body: { name: editName } })
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
mutateUserProfile()
setEditNameModalVisible(false)
setEditing(false)
@ -80,15 +80,15 @@ export default function AccountPage() {
}
const valid = () => {
if (!password.trim()) {
showErrorMessage(t('login.error.passwordEmpty'))
showErrorMessage(t('error.passwordEmpty', { ns: 'login' }))
return false
}
if (!validPassword.test(password)) {
showErrorMessage(t('login.error.passwordInvalid'))
showErrorMessage(t('error.passwordInvalid', { ns: 'login' }))
return false
}
if (password !== confirmPassword) {
showErrorMessage(t('common.account.notEqual'))
showErrorMessage(t('account.notEqual', { ns: 'common' }))
return false
}
@ -112,7 +112,7 @@ export default function AccountPage() {
repeat_new_password: confirmPassword,
},
})
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
mutateUserProfile()
setEditPasswordModalVisible(false)
resetPasswordForm()
@ -146,7 +146,7 @@ export default function AccountPage() {
return (
<>
<div className="pb-3 pt-2">
<h4 className="title-2xl-semi-bold text-text-primary">{t('common.account.myAccount')}</h4>
<h4 className="title-2xl-semi-bold text-text-primary">{t('account.myAccount', { ns: 'common' })}</h4>
</div>
<div className="mb-8 flex items-center rounded-xl bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 p-6">
<AvatarWithEdit avatar={userProfile.avatar_url} name={userProfile.name} onSave={mutateUserProfile} size={64} />
@ -164,25 +164,25 @@ export default function AccountPage() {
</div>
</div>
<div className="mb-8">
<div className={titleClassName}>{t('common.account.name')}</div>
<div className={titleClassName}>{t('account.name', { ns: 'common' })}</div>
<div className="mt-2 flex w-full items-center justify-between gap-2">
<div className="system-sm-regular flex-1 rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled ">
<span className="pl-1">{userProfile.name}</span>
</div>
<div className="system-sm-medium cursor-pointer rounded-lg bg-components-button-tertiary-bg px-3 py-2 text-components-button-tertiary-text" onClick={handleEditName}>
{t('common.operation.edit')}
{t('operation.edit', { ns: 'common' })}
</div>
</div>
</div>
<div className="mb-8">
<div className={titleClassName}>{t('common.account.email')}</div>
<div className={titleClassName}>{t('account.email', { ns: 'common' })}</div>
<div className="mt-2 flex w-full items-center justify-between gap-2">
<div className="system-sm-regular flex-1 rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled ">
<span className="pl-1">{userProfile.email}</span>
</div>
{systemFeatures.enable_change_email && (
<div className="system-sm-medium cursor-pointer rounded-lg bg-components-button-tertiary-bg px-3 py-2 text-components-button-tertiary-text" onClick={() => setShowUpdateEmail(true)}>
{t('common.operation.change')}
{t('operation.change', { ns: 'common' })}
</div>
)}
</div>
@ -191,26 +191,26 @@ export default function AccountPage() {
systemFeatures.enable_email_password_login && (
<div className="mb-8 flex justify-between gap-2">
<div>
<div className="system-sm-semibold mb-1 text-text-secondary">{t('common.account.password')}</div>
<div className="body-xs-regular mb-2 text-text-tertiary">{t('common.account.passwordTip')}</div>
<div className="system-sm-semibold mb-1 text-text-secondary">{t('account.password', { ns: 'common' })}</div>
<div className="body-xs-regular mb-2 text-text-tertiary">{t('account.passwordTip', { ns: 'common' })}</div>
</div>
<Button onClick={() => setEditPasswordModalVisible(true)}>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</Button>
<Button onClick={() => setEditPasswordModalVisible(true)}>{userProfile.is_password_set ? t('account.resetPassword', { ns: 'common' }) : t('account.setPassword', { ns: 'common' })}</Button>
</div>
)
}
<div className="mb-6 border-[1px] border-divider-subtle" />
<div className="mb-8">
<div className={titleClassName}>{t('common.account.langGeniusAccount')}</div>
<div className={descriptionClassName}>{t('common.account.langGeniusAccountTip')}</div>
<div className={titleClassName}>{t('account.langGeniusAccount', { ns: 'common' })}</div>
<div className={descriptionClassName}>{t('account.langGeniusAccountTip', { ns: 'common' })}</div>
{!!apps.length && (
<Collapse
title={`${t('common.account.showAppLength', { length: apps.length })}`}
title={`${t('account.showAppLength', { ns: 'common', length: apps.length })}`}
items={apps.map((app: App) => ({ ...app, key: app.id, name: app.name }))}
renderItem={renderAppItem}
wrapperClassName="mt-2"
/>
)}
{!IS_CE_EDITION && <Button className="mt-2 text-components-button-destructive-secondary-text" onClick={() => setShowDeleteAccountModal(true)}>{t('common.account.delete')}</Button>}
{!IS_CE_EDITION && <Button className="mt-2 text-components-button-destructive-secondary-text" onClick={() => setShowDeleteAccountModal(true)}>{t('account.delete', { ns: 'common' })}</Button>}
</div>
{
editNameModalVisible && (
@ -219,21 +219,21 @@ export default function AccountPage() {
onClose={() => setEditNameModalVisible(false)}
className="!w-[420px] !p-6"
>
<div className="title-2xl-semi-bold mb-6 text-text-primary">{t('common.account.editName')}</div>
<div className={titleClassName}>{t('common.account.name')}</div>
<div className="title-2xl-semi-bold mb-6 text-text-primary">{t('account.editName', { ns: 'common' })}</div>
<div className={titleClassName}>{t('account.name', { ns: 'common' })}</div>
<Input
className="mt-2"
value={editName}
onChange={e => setEditName(e.target.value)}
/>
<div className="mt-10 flex justify-end">
<Button className="mr-2" onClick={() => setEditNameModalVisible(false)}>{t('common.operation.cancel')}</Button>
<Button className="mr-2" onClick={() => setEditNameModalVisible(false)}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button
disabled={editing || !editName}
variant="primary"
onClick={handleSaveName}
>
{t('common.operation.save')}
{t('operation.save', { ns: 'common' })}
</Button>
</div>
</Modal>
@ -249,10 +249,10 @@ export default function AccountPage() {
}}
className="!w-[420px] !p-6"
>
<div className="title-2xl-semi-bold mb-6 text-text-primary">{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</div>
<div className="title-2xl-semi-bold mb-6 text-text-primary">{userProfile.is_password_set ? t('account.resetPassword', { ns: 'common' }) : t('account.setPassword', { ns: 'common' })}</div>
{userProfile.is_password_set && (
<>
<div className={titleClassName}>{t('common.account.currentPassword')}</div>
<div className={titleClassName}>{t('account.currentPassword', { ns: 'common' })}</div>
<div className="relative mt-2">
<Input
type={showCurrentPassword ? 'text' : 'password'}
@ -273,7 +273,7 @@ export default function AccountPage() {
</>
)}
<div className="system-sm-semibold mt-8 text-text-secondary">
{userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')}
{userProfile.is_password_set ? t('account.newPassword', { ns: 'common' }) : t('account.password', { ns: 'common' })}
</div>
<div className="relative mt-2">
<Input
@ -291,7 +291,7 @@ export default function AccountPage() {
</Button>
</div>
</div>
<div className="system-sm-semibold mt-8 text-text-secondary">{t('common.account.confirmPassword')}</div>
<div className="system-sm-semibold mt-8 text-text-secondary">{t('account.confirmPassword', { ns: 'common' })}</div>
<div className="relative mt-2">
<Input
type={showConfirmPassword ? 'text' : 'password'}
@ -316,14 +316,14 @@ export default function AccountPage() {
resetPasswordForm()
}}
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
disabled={editing}
variant="primary"
onClick={handleSavePassword}
>
{userProfile.is_password_set ? t('common.operation.reset') : t('common.operation.save')}
{userProfile.is_password_set ? t('operation.reset', { ns: 'common' }) : t('operation.save', { ns: 'common' })}
</Button>
</div>
</Modal>

View File

@ -94,7 +94,7 @@ export default function AppSelector() {
className="group flex h-9 cursor-pointer items-center justify-start rounded-lg px-3 hover:bg-state-base-hover"
>
<LogOut01 className="mr-1 flex h-4 w-4 text-text-tertiary" />
<div className="text-[14px] font-normal text-text-secondary">{t('common.userProfile.logout')}</div>
<div className="text-[14px] font-normal text-text-secondary">{t('userProfile.logout', { ns: 'common' })}</div>
</div>
</div>
</MenuItem>

View File

@ -31,22 +31,22 @@ export default function CheckEmail(props: DeleteAccountProps) {
return (
<>
<div className="body-md-medium py-1 text-text-destructive">
{t('common.account.deleteTip')}
{t('account.deleteTip', { ns: 'common' })}
</div>
<div className="body-md-regular pb-2 pt-1 text-text-secondary">
{t('common.account.deletePrivacyLinkTip')}
<Link href="https://dify.ai/privacy" className="text-text-accent">{t('common.account.deletePrivacyLink')}</Link>
{t('account.deletePrivacyLinkTip', { ns: 'common' })}
<Link href="https://dify.ai/privacy" className="text-text-accent">{t('account.deletePrivacyLink', { ns: 'common' })}</Link>
</div>
<label className="system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary">{t('common.account.deleteLabel')}</label>
<label className="system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary">{t('account.deleteLabel', { ns: 'common' })}</label>
<Input
placeholder={t('common.account.deletePlaceholder') as string}
placeholder={t('account.deletePlaceholder', { ns: 'common' }) as string}
onChange={(e) => {
setUserInputEmail(e.target.value)
}}
/>
<div className="mt-3 flex w-full flex-col gap-2">
<Button className="w-full" disabled={userInputEmail !== userProfile.email || isSendingEmail} loading={isSendingEmail} variant="primary" onClick={handleConfirm}>{t('common.account.sendVerificationButton')}</Button>
<Button className="w-full" onClick={props.onCancel}>{t('common.operation.cancel')}</Button>
<Button className="w-full" disabled={userInputEmail !== userProfile.email || isSendingEmail} loading={isSendingEmail} variant="primary" onClick={handleConfirm}>{t('account.sendVerificationButton', { ns: 'common' })}</Button>
<Button className="w-full" onClick={props.onCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
</div>
</>
)

View File

@ -28,7 +28,7 @@ export default function FeedBack(props: DeleteAccountProps) {
await logout()
// Tokens are now stored in cookies and cleared by backend
router.push('/signin')
Toast.notify({ type: 'info', message: t('common.account.deleteSuccessTip') })
Toast.notify({ type: 'info', message: t('account.deleteSuccessTip', { ns: 'common' }) })
}
catch (error) { console.error(error) }
}, [router, t])
@ -50,22 +50,22 @@ export default function FeedBack(props: DeleteAccountProps) {
<CustomDialog
show={true}
onClose={props.onCancel}
title={t('common.account.feedbackTitle')}
title={t('account.feedbackTitle', { ns: 'common' })}
className="max-w-[480px]"
footer={false}
>
<label className="system-sm-semibold mb-1 mt-3 flex items-center text-text-secondary">{t('common.account.feedbackLabel')}</label>
<label className="system-sm-semibold mb-1 mt-3 flex items-center text-text-secondary">{t('account.feedbackLabel', { ns: 'common' })}</label>
<Textarea
rows={6}
value={userFeedback}
placeholder={t('common.account.feedbackPlaceholder') as string}
placeholder={t('account.feedbackPlaceholder', { ns: 'common' }) as string}
onChange={(e) => {
setUserFeedback(e.target.value)
}}
/>
<div className="mt-3 flex w-full flex-col gap-2">
<Button className="w-full" loading={isPending} variant="primary" onClick={handleSubmit}>{t('common.operation.submit')}</Button>
<Button className="w-full" onClick={handleSkip}>{t('common.operation.skip')}</Button>
<Button className="w-full" loading={isPending} variant="primary" onClick={handleSubmit}>{t('operation.submit', { ns: 'common' })}</Button>
<Button className="w-full" onClick={handleSkip}>{t('operation.skip', { ns: 'common' })}</Button>
</div>
</CustomDialog>
)

View File

@ -37,24 +37,24 @@ export default function VerifyEmail(props: DeleteAccountProps) {
return (
<>
<div className="body-md-medium pt-1 text-text-destructive">
{t('common.account.deleteTip')}
{t('account.deleteTip', { ns: 'common' })}
</div>
<div className="body-md-regular pb-2 pt-1 text-text-secondary">
{t('common.account.deletePrivacyLinkTip')}
<Link href="https://dify.ai/privacy" className="text-text-accent">{t('common.account.deletePrivacyLink')}</Link>
{t('account.deletePrivacyLinkTip', { ns: 'common' })}
<Link href="https://dify.ai/privacy" className="text-text-accent">{t('account.deletePrivacyLink', { ns: 'common' })}</Link>
</div>
<label className="system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary">{t('common.account.verificationLabel')}</label>
<label className="system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary">{t('account.verificationLabel', { ns: 'common' })}</label>
<Input
minLength={6}
maxLength={6}
placeholder={t('common.account.verificationPlaceholder') as string}
placeholder={t('account.verificationPlaceholder', { ns: 'common' }) as string}
onChange={(e) => {
setVerificationCode(e.target.value)
}}
/>
<div className="mt-3 flex w-full flex-col gap-2">
<Button className="w-full" disabled={shouldButtonDisabled} loading={isDeleting} variant="warning" onClick={handleConfirm}>{t('common.account.permanentlyDeleteButton')}</Button>
<Button className="w-full" onClick={props.onCancel}>{t('common.operation.cancel')}</Button>
<Button className="w-full" disabled={shouldButtonDisabled} loading={isDeleting} variant="warning" onClick={handleConfirm}>{t('account.permanentlyDeleteButton', { ns: 'common' })}</Button>
<Button className="w-full" onClick={props.onCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
<Countdown onResend={sendEmail} />
</div>
</>

View File

@ -33,7 +33,7 @@ export default function DeleteAccount(props: DeleteAccountProps) {
<CustomDialog
show={true}
onClose={props.onCancel}
title={t('common.account.delete')}
title={t('account.delete', { ns: 'common' })}
className="max-w-[480px]"
footer={false}
>

View File

@ -32,12 +32,12 @@ const Header = () => {
: <DifyLogo />}
</div>
<div className="h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular" />
<p className="title-3xl-semi-bold relative mt-[-2px] text-text-primary">{t('common.account.account')}</p>
<p className="title-3xl-semi-bold relative mt-[-2px] text-text-primary">{t('account.account', { ns: 'common' })}</p>
</div>
<div className="flex shrink-0 items-center gap-3">
<Button className="system-sm-medium gap-2 px-3 py-2" onClick={goToStudio}>
<RiRobot2Line className="h-4 w-4" />
<p>{t('common.account.studio')}</p>
<p>{t('account.studio', { ns: 'common' })}</p>
<RiArrowRightUpLine className="h-4 w-4" />
</Button>
<div className="h-4 w-[1px] bg-divider-regular" />

View File

@ -5,7 +5,7 @@ import AccountPage from './account-page'
export default function Account() {
const { t } = useTranslation()
useDocumentTitle(t('common.menus.account'))
useDocumentTitle(t('menus.account', { ns: 'common' }))
return (
<div className="mx-auto w-full max-w-[640px] px-6 pt-12">
<AccountPage />

View File

@ -50,23 +50,23 @@ export default function OAuthAuthorize() {
const SCOPE_INFO_MAP: Record<string, { icon: React.ComponentType<{ className?: string }>, label: string }> = {
'read:name': {
icon: RiInfoCardLine,
label: t('oauth.scopes.name'),
label: t('scopes.name', { ns: 'oauth' }),
},
'read:email': {
icon: RiMailLine,
label: t('oauth.scopes.email'),
label: t('scopes.email', { ns: 'oauth' }),
},
'read:avatar': {
icon: RiAccountCircleLine,
label: t('oauth.scopes.avatar'),
label: t('scopes.avatar', { ns: 'oauth' }),
},
'read:interface_language': {
icon: RiTranslate2,
label: t('oauth.scopes.languagePreference'),
label: t('scopes.languagePreference', { ns: 'oauth' }),
},
'read:timezone': {
icon: RiGlobalLine,
label: t('oauth.scopes.timezone'),
label: t('scopes.timezone', { ns: 'oauth' }),
},
}
@ -106,7 +106,7 @@ export default function OAuthAuthorize() {
catch (err: any) {
Toast.notify({
type: 'error',
message: `${t('oauth.error.authorizeFailed')}: ${err.message}`,
message: `${t('error.authorizeFailed', { ns: 'oauth' })}: ${err.message}`,
})
}
}
@ -117,7 +117,7 @@ export default function OAuthAuthorize() {
hasNotifiedRef.current = true
Toast.notify({
type: 'error',
message: invalidParams ? t('oauth.error.invalidParams') : t('oauth.error.authAppInfoFetchFailed'),
message: invalidParams ? t('error.invalidParams', { ns: 'oauth' }) : t('error.authAppInfoFetchFailed', { ns: 'oauth' }),
duration: 0,
})
}
@ -141,11 +141,11 @@ export default function OAuthAuthorize() {
<div className={`mb-4 mt-5 flex flex-col gap-2 ${isLoggedIn ? 'pb-2' : ''}`}>
<div className="title-4xl-semi-bold">
{isLoggedIn && <div className="text-text-primary">{t('oauth.connect')}</div>}
<div className="text-[var(--color-saas-dify-blue-inverted)]">{authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('oauth.unknownApp')}</div>
{!isLoggedIn && <div className="text-text-primary">{t('oauth.tips.notLoggedIn')}</div>}
{isLoggedIn && <div className="text-text-primary">{t('connect', { ns: 'oauth' })}</div>}
<div className="text-[var(--color-saas-dify-blue-inverted)]">{authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })}</div>
{!isLoggedIn && <div className="text-text-primary">{t('tips.notLoggedIn', { ns: 'oauth' })}</div>}
</div>
<div className="body-md-regular text-text-secondary">{isLoggedIn ? `${authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('oauth.unknownApp')} ${t('oauth.tips.loggedIn')}` : t('oauth.tips.needLogin')}</div>
<div className="body-md-regular text-text-secondary">{isLoggedIn ? `${authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })} ${t('tips.loggedIn', { ns: 'oauth' })}` : t('tips.needLogin', { ns: 'oauth' })}</div>
</div>
{isLoggedIn && userProfile && (
@ -157,7 +157,7 @@ export default function OAuthAuthorize() {
<div className="system-xs-regular text-text-tertiary">{userProfile.email}</div>
</div>
</div>
<Button variant="tertiary" size="small" onClick={onLoginSwitchClick}>{t('oauth.switchAccount')}</Button>
<Button variant="tertiary" size="small" onClick={onLoginSwitchClick}>{t('switchAccount', { ns: 'oauth' })}</Button>
</div>
)}
@ -178,12 +178,12 @@ export default function OAuthAuthorize() {
<div className="flex flex-col items-center gap-2 pt-4">
{!isLoggedIn
? (
<Button variant="primary" size="large" className="w-full" onClick={onLoginSwitchClick}>{t('oauth.login')}</Button>
<Button variant="primary" size="large" className="w-full" onClick={onLoginSwitchClick}>{t('login', { ns: 'oauth' })}</Button>
)
: (
<>
<Button variant="primary" size="large" className="w-full" onClick={onAuthorize} disabled={!client_id || !redirect_uri || isError || authorizing} loading={authorizing}>{t('oauth.continue')}</Button>
<Button size="large" className="w-full" onClick={() => router.push('/apps')}>{t('common.operation.cancel')}</Button>
<Button variant="primary" size="large" className="w-full" onClick={onAuthorize} disabled={!client_id || !redirect_uri || isError || authorizing} loading={authorizing}>{t('continue', { ns: 'oauth' })}</Button>
<Button size="large" className="w-full" onClick={() => router.push('/apps')}>{t('operation.cancel', { ns: 'common' })}</Button>
</>
)}
</div>
@ -199,7 +199,7 @@ export default function OAuthAuthorize() {
</defs>
</svg>
</div>
<div className="system-xs-regular mt-3 text-text-tertiary">{t('oauth.tips.common')}</div>
<div className="system-xs-regular mt-3 text-text-tertiary">{t('tips.common', { ns: 'oauth' })}</div>
</div>
)
}

View File

@ -56,11 +56,11 @@ const ActivateForm = () => {
<div className="flex flex-col md:w-[400px]">
<div className="mx-auto w-full">
<div className="mb-3 flex h-20 w-20 items-center justify-center rounded-[20px] border border-divider-regular bg-components-option-card-option-bg p-5 text-[40px] font-bold shadow-lg">🤷</div>
<h2 className="text-[32px] font-bold text-text-primary">{t('login.invalid')}</h2>
<h2 className="text-[32px] font-bold text-text-primary">{t('invalid', { ns: 'login' })}</h2>
</div>
<div className="mx-auto mt-6 w-full">
<Button variant="primary" className="w-full !text-sm">
<a href="https://dify.ai">{t('login.explore')}</a>
<a href="https://dify.ai">{t('explore', { ns: 'login' })}</a>
</Button>
</div>
</div>

View File

@ -100,12 +100,12 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
setShowEditModal(false)
notify({
type: 'success',
message: t('app.editDone'),
message: t('editDone', { ns: 'app' }),
})
setAppDetail(app)
}
catch {
notify({ type: 'error', message: t('app.editFailed') })
notify({ type: 'error', message: t('editFailed', { ns: 'app' }) })
}
}, [appDetail, notify, setAppDetail, t])
@ -124,14 +124,14 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
setShowDuplicateModal(false)
notify({
type: 'success',
message: t('app.newApp.appCreated'),
message: t('newApp.appCreated', { ns: 'app' }),
})
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
onPlanInfoChanged()
getRedirection(true, newApp, replace)
}
catch {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
notify({ type: 'error', message: t('newApp.appCreateFailed', { ns: 'app' }) })
}
}
@ -152,7 +152,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
URL.revokeObjectURL(url)
}
catch {
notify({ type: 'error', message: t('app.exportFailed') })
notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
}
}
@ -181,7 +181,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
setSecretEnvList(list)
}
catch {
notify({ type: 'error', message: t('app.exportFailed') })
notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
}
}
@ -190,7 +190,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
return
try {
await deleteApp(appDetail.id)
notify({ type: 'success', message: t('app.appDeleted') })
notify({ type: 'success', message: t('appDeleted', { ns: 'app' }) })
onPlanInfoChanged()
setAppDetail()
replace('/apps')
@ -198,7 +198,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
catch (e: any) {
notify({
type: 'error',
message: `${t('app.appDeleteFailed')}${'message' in e ? `: ${e.message}` : ''}`,
message: `${t('appDeleteFailed', { ns: 'app' })}${'message' in e ? `: ${e.message}` : ''}`,
})
}
setShowConfirmDelete(false)
@ -212,7 +212,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
const primaryOperations = [
{
id: 'edit',
title: t('app.editApp'),
title: t('editApp', { ns: 'app' }),
icon: <RiEditLine />,
onClick: () => {
setOpen(false)
@ -222,7 +222,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
},
{
id: 'duplicate',
title: t('app.duplicate'),
title: t('duplicate', { ns: 'app' }),
icon: <RiFileCopy2Line />,
onClick: () => {
setOpen(false)
@ -232,7 +232,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
},
{
id: 'export',
title: t('app.export'),
title: t('export', { ns: 'app' }),
icon: <RiFileDownloadLine />,
onClick: exportCheck,
},
@ -243,7 +243,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
...(appDetail.mode === AppModeEnum.ADVANCED_CHAT || appDetail.mode === AppModeEnum.WORKFLOW)
? [{
id: 'import',
title: t('workflow.common.importDSL'),
title: t('common.importDSL', { ns: 'workflow' }),
icon: <RiFileUploadLine />,
onClick: () => {
setOpen(false)
@ -263,7 +263,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
// Delete operation
{
id: 'delete',
title: t('common.operation.delete'),
title: t('operation.delete', { ns: 'common' }),
icon: <RiDeleteBinLine />,
onClick: () => {
setOpen(false)
@ -277,7 +277,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
const switchOperation = (appDetail.mode === AppModeEnum.COMPLETION || appDetail.mode === AppModeEnum.CHAT)
? {
id: 'switch',
title: t('app.switch'),
title: t('switch', { ns: 'app' }),
icon: <RiExchange2Line />,
onClick: () => {
setOpen(false)
@ -331,14 +331,14 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
</div>
<div className="system-2xs-medium-uppercase whitespace-nowrap text-text-tertiary">
{appDetail.mode === AppModeEnum.ADVANCED_CHAT
? t('app.types.advanced')
? t('types.advanced', { ns: 'app' })
: appDetail.mode === AppModeEnum.AGENT_CHAT
? t('app.types.agent')
? t('types.agent', { ns: 'app' })
: appDetail.mode === AppModeEnum.CHAT
? t('app.types.chatbot')
? t('types.chatbot', { ns: 'app' })
: appDetail.mode === AppModeEnum.COMPLETION
? t('app.types.completion')
: t('app.types.workflow')}
? t('types.completion', { ns: 'app' })
: t('types.workflow', { ns: 'app' })}
</div>
</div>
)}
@ -364,7 +364,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
/>
<div className="flex flex-1 flex-col items-start justify-center overflow-hidden">
<div className="system-md-semibold w-full truncate text-text-secondary">{appDetail.name}</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">{appDetail.mode === AppModeEnum.ADVANCED_CHAT ? t('app.types.advanced') : appDetail.mode === AppModeEnum.AGENT_CHAT ? t('app.types.agent') : appDetail.mode === AppModeEnum.CHAT ? t('app.types.chatbot') : appDetail.mode === AppModeEnum.COMPLETION ? t('app.types.completion') : t('app.types.workflow')}</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">{appDetail.mode === AppModeEnum.ADVANCED_CHAT ? t('types.advanced', { ns: 'app' }) : appDetail.mode === AppModeEnum.AGENT_CHAT ? t('types.agent', { ns: 'app' }) : appDetail.mode === AppModeEnum.CHAT ? t('types.chatbot', { ns: 'app' }) : appDetail.mode === AppModeEnum.COMPLETION ? t('types.completion', { ns: 'app' }) : t('types.workflow', { ns: 'app' })}</div>
</div>
</div>
{/* description */}
@ -438,8 +438,8 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
)}
{showConfirmDelete && (
<Confirm
title={t('app.deleteAppConfirmTitle')}
content={t('app.deleteAppConfirmContent')}
title={t('deleteAppConfirmTitle', { ns: 'app' })}
content={t('deleteAppConfirmContent', { ns: 'app' })}
isShow={showConfirmDelete}
onConfirm={onConfirmDelete}
onCancel={() => setShowConfirmDelete(false)}
@ -462,8 +462,8 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
<Confirm
type="info"
isShow={showExportWarning}
title={t('workflow.sidebar.exportWarning')}
content={t('workflow.sidebar.exportWarningDesc')}
title={t('sidebar.exportWarning', { ns: 'workflow' })}
content={t('sidebar.exportWarningDesc', { ns: 'workflow' })}
onConfirm={handleConfirmExport}
onCancel={() => setShowExportWarning(false)}
/>

View File

@ -148,7 +148,7 @@ const AppOperations = ({
>
<RiMoreLine className="h-3.5 w-3.5 text-components-button-secondary-text" />
<span className="system-xs-medium text-components-button-secondary-text">
{t('common.operation.more')}
{t('operation.more', { ns: 'common' })}
</span>
</Button>
</div>
@ -183,7 +183,7 @@ const AppOperations = ({
>
<RiMoreLine className="h-3.5 w-3.5 text-components-button-secondary-text" />
<span className="system-xs-medium text-components-button-secondary-text">
{t('common.operation.more')}
{t('operation.more', { ns: 'common' })}
</span>
</Button>
</PortalToFollowElemTrigger>

View File

@ -99,7 +99,7 @@ const AppSidebarDropdown = ({ navigation }: Props) => {
<div className="flex w-full">
<div className="system-md-semibold truncate text-text-secondary">{appDetail.name}</div>
</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">{appDetail.mode === AppModeEnum.ADVANCED_CHAT ? t('app.types.advanced') : appDetail.mode === AppModeEnum.AGENT_CHAT ? t('app.types.agent') : appDetail.mode === AppModeEnum.CHAT ? t('app.types.chatbot') : appDetail.mode === AppModeEnum.COMPLETION ? t('app.types.completion') : t('app.types.workflow')}</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">{appDetail.mode === AppModeEnum.ADVANCED_CHAT ? t('types.advanced', { ns: 'app' }) : appDetail.mode === AppModeEnum.AGENT_CHAT ? t('types.agent', { ns: 'app' }) : appDetail.mode === AppModeEnum.CHAT ? t('types.chatbot', { ns: 'app' }) : appDetail.mode === AppModeEnum.COMPLETION ? t('types.completion', { ns: 'app' }) : t('types.workflow', { ns: 'app' })}</div>
</div>
</div>
</div>

View File

@ -98,7 +98,7 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
<div className="system-2xs-medium-uppercase flex text-text-tertiary">{type}</div>
)}
{!hideType && !isExtraInLine && (
<div className="system-2xs-medium-uppercase text-text-tertiary">{isExternal ? t('dataset.externalTag') : type}</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">{isExternal ? t('externalTag', { ns: 'dataset' }) : type}</div>
)}
</div>
)}

View File

@ -73,14 +73,14 @@ const DropDown = ({
URL.revokeObjectURL(url)
}
catch {
Toast.notify({ type: 'error', message: t('app.exportFailed') })
Toast.notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
}
}, [dataset, exportPipelineConfig, handleTrigger, t])
const detectIsUsedByApp = useCallback(async () => {
try {
const { is_using: isUsedByApp } = await checkIsUsedInApp(dataset.id)
setConfirmMessage(isUsedByApp ? t('dataset.datasetUsedByApp')! : t('dataset.deleteDatasetConfirmContent')!)
setConfirmMessage(isUsedByApp ? t('datasetUsedByApp', { ns: 'dataset' })! : t('deleteDatasetConfirmContent', { ns: 'dataset' })!)
setShowConfirmDelete(true)
}
catch (e: any) {
@ -95,7 +95,7 @@ const DropDown = ({
const onConfirmDelete = useCallback(async () => {
try {
await deleteDataset(dataset.id)
Toast.notify({ type: 'success', message: t('dataset.datasetDeleted') })
Toast.notify({ type: 'success', message: t('datasetDeleted', { ns: 'dataset' }) })
invalidDatasetList()
replace('/datasets')
}
@ -141,7 +141,7 @@ const DropDown = ({
)}
{showConfirmDelete && (
<Confirm
title={t('dataset.deleteDatasetConfirmTitle')}
title={t('deleteDatasetConfirmTitle', { ns: 'dataset' })}
content={confirmMessage}
isShow={showConfirmDelete}
onConfirm={onConfirmDelete}

View File

@ -70,10 +70,10 @@ const DatasetInfo: FC<DatasetInfoProps> = ({
{dataset.name}
</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">
{isExternalProvider && t('dataset.externalTag')}
{isExternalProvider && t('externalTag', { ns: 'dataset' })}
{!isExternalProvider && isPipelinePublished && dataset.doc_form && dataset.indexing_technique && (
<div className="flex items-center gap-x-2">
<span>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any) as string}</span>
<span>{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}</span>
<span>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</span>
</div>
)}

View File

@ -26,13 +26,13 @@ const Menu = ({
<div className="flex flex-col p-1">
<MenuItem
Icon={RiEditLine}
name={t('common.operation.edit')}
name={t('operation.edit', { ns: 'common' })}
handleClick={openRenameModal}
/>
{runtimeMode === 'rag_pipeline' && (
<MenuItem
Icon={RiFileDownloadLine}
name={t('datasetPipeline.operations.exportPipeline')}
name={t('operations.exportPipeline', { ns: 'datasetPipeline' })}
handleClick={handleExportPipeline}
/>
)}
@ -43,7 +43,7 @@ const Menu = ({
<div className="flex flex-col p-1">
<MenuItem
Icon={RiDeleteBinLine}
name={t('common.operation.delete')}
name={t('operation.delete', { ns: 'common' })}
handleClick={detectIsUsedByApp}
/>
</div>

View File

@ -113,10 +113,10 @@ const DatasetSidebarDropdown = ({
{dataset.name}
</div>
<div className="system-2xs-medium-uppercase text-text-tertiary">
{isExternalProvider && t('dataset.externalTag')}
{isExternalProvider && t('externalTag', { ns: 'dataset' })}
{!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
<div className="flex items-center gap-x-2">
<span>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any) as string}</span>
<span>{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}</span>
<span>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</span>
</div>
)}

View File

@ -19,7 +19,7 @@ const TooltipContent = ({
return (
<div className="flex items-center gap-x-1">
<span className="system-xs-medium px-0.5 text-text-secondary">{expand ? t('layout.sidebar.collapseSidebar') : t('layout.sidebar.expandSidebar')}</span>
<span className="system-xs-medium px-0.5 text-text-secondary">{expand ? t('sidebar.collapseSidebar', { ns: 'layout' }) : t('sidebar.expandSidebar', { ns: 'layout' })}</span>
<div className="flex items-center gap-x-0.5">
{
TOGGLE_SHORTCUT.map(key => (

View File

@ -22,8 +22,8 @@ const EditItem: FC<Props> = ({
}) => {
const { t } = useTranslation()
const avatar = type === EditItemType.Query ? <User className="h-6 w-6" /> : <Robot className="h-6 w-6" />
const name = type === EditItemType.Query ? t('appAnnotation.addModal.queryName') : t('appAnnotation.addModal.answerName')
const placeholder = type === EditItemType.Query ? t('appAnnotation.addModal.queryPlaceholder') : t('appAnnotation.addModal.answerPlaceholder')
const name = type === EditItemType.Query ? t('addModal.queryName', { ns: 'appAnnotation' }) : t('addModal.answerName', { ns: 'appAnnotation' })
const placeholder = type === EditItemType.Query ? t('addModal.queryPlaceholder', { ns: 'appAnnotation' }) : t('addModal.answerPlaceholder', { ns: 'appAnnotation' })
return (
<div className="flex" onClick={e => e.stopPropagation()}>

View File

@ -33,10 +33,10 @@ const AddAnnotationModal: FC<Props> = ({
const isValid = (payload: AnnotationItemBasic) => {
if (!payload.question)
return t('appAnnotation.errorMessage.queryRequired')
return t('errorMessage.queryRequired', { ns: 'appAnnotation' })
if (!payload.answer)
return t('appAnnotation.errorMessage.answerRequired')
return t('errorMessage.answerRequired', { ns: 'appAnnotation' })
return true
}
@ -76,7 +76,7 @@ const AddAnnotationModal: FC<Props> = ({
isShow={isShow}
onHide={onHide}
maxWidthClassName="!max-w-[480px]"
title={t('appAnnotation.addModal.title') as string}
title={t('addModal.title', { ns: 'appAnnotation' }) as string}
body={(
<div className="space-y-6 p-6 pb-4">
<EditItem
@ -104,11 +104,11 @@ const AddAnnotationModal: FC<Props> = ({
className="flex items-center space-x-2"
>
<Checkbox id="create-next-checkbox" checked={isCreateNext} onCheck={() => setIsCreateNext(!isCreateNext)} />
<div>{t('appAnnotation.addModal.createNext')}</div>
<div>{t('addModal.createNext', { ns: 'appAnnotation' })}</div>
</div>
<div className="mt-2 flex space-x-2">
<Button className="h-7 text-xs" onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button className="h-7 text-xs" variant="primary" onClick={handleSave} loading={isSaving} disabled={isAnnotationFull}>{t('common.operation.add')}</Button>
<Button className="h-7 text-xs" onClick={onHide}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button className="h-7 text-xs" variant="primary" onClick={handleSave} loading={isSaving} disabled={isAnnotationFull}>{t('operation.add', { ns: 'common' })}</Button>
</div>
</div>
</div>

View File

@ -7,7 +7,7 @@ import Confirm from '@/app/components/base/confirm'
import Divider from '@/app/components/base/divider'
import { cn } from '@/utils/classnames'
const i18nPrefix = 'appAnnotation.batchAction'
const i18nPrefix = 'batchAction'
type IBatchActionProps = {
className?: string
@ -45,27 +45,27 @@ const BatchAction: FC<IBatchActionProps> = ({
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-text-accent px-1 py-0.5 text-xs font-medium text-text-primary-on-surface">
{selectedIds.length}
</span>
<span className="text-[13px] font-semibold leading-[16px] text-text-accent">{t(`${i18nPrefix}.selected`)}</span>
<span className="text-[13px] font-semibold leading-[16px] text-text-accent">{t(`${i18nPrefix}.selected`, { ns: 'appAnnotation' })}</span>
</div>
<Divider type="vertical" className="mx-0.5 h-3.5 bg-divider-regular" />
<div className="flex cursor-pointer items-center gap-x-0.5 px-3 py-2" onClick={showDeleteConfirm}>
<RiDeleteBinLine className="h-4 w-4 text-components-button-destructive-ghost-text" />
<button type="button" className="px-0.5 text-[13px] font-medium leading-[16px] text-components-button-destructive-ghost-text">
{t('common.operation.delete')}
{t('operation.delete', { ns: 'common' })}
</button>
</div>
<Divider type="vertical" className="mx-0.5 h-3.5 bg-divider-regular" />
<button type="button" className="px-3.5 py-2 text-[13px] font-medium leading-[16px] text-components-button-ghost-text" onClick={onCancel}>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</button>
</div>
{
isShowDeleteConfirm && (
<Confirm
isShow
title={t('appAnnotation.list.delete.title')}
confirmText={t('common.operation.delete')}
title={t('list.delete.title', { ns: 'appAnnotation' })}
confirmText={t('operation.delete', { ns: 'common' })}
onConfirm={handleBatchDelete}
onCancel={hideDeleteConfirm}
isLoading={isDeleting}

View File

@ -33,36 +33,36 @@ const CSVDownload: FC = () => {
return (
<div className="mt-6">
<div className="system-sm-medium text-text-primary">{t('share.generation.csvStructureTitle')}</div>
<div className="system-sm-medium text-text-primary">{t('generation.csvStructureTitle', { ns: 'share' })}</div>
<div className="mt-2 max-h-[500px] overflow-auto">
<table className="w-full table-fixed border-separate border-spacing-0 rounded-lg border border-divider-regular text-xs">
<thead className="text-text-tertiary">
<tr>
<td className="h-9 border-b border-divider-regular pl-3 pr-2">{t('appAnnotation.batchModal.question')}</td>
<td className="h-9 border-b border-divider-regular pl-3 pr-2">{t('appAnnotation.batchModal.answer')}</td>
<td className="h-9 border-b border-divider-regular pl-3 pr-2">{t('batchModal.question', { ns: 'appAnnotation' })}</td>
<td className="h-9 border-b border-divider-regular pl-3 pr-2">{t('batchModal.answer', { ns: 'appAnnotation' })}</td>
</tr>
</thead>
<tbody className="text-text-secondary">
<tr>
<td className="h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]">
{t('appAnnotation.batchModal.question')}
{t('batchModal.question', { ns: 'appAnnotation' })}
{' '}
1
</td>
<td className="h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]">
{t('appAnnotation.batchModal.answer')}
{t('batchModal.answer', { ns: 'appAnnotation' })}
{' '}
1
</td>
</tr>
<tr>
<td className="h-9 pl-3 pr-2 text-[13px]">
{t('appAnnotation.batchModal.question')}
{t('batchModal.question', { ns: 'appAnnotation' })}
{' '}
2
</td>
<td className="h-9 pl-3 pr-2 text-[13px]">
{t('appAnnotation.batchModal.answer')}
{t('batchModal.answer', { ns: 'appAnnotation' })}
{' '}
2
</td>
@ -79,7 +79,7 @@ const CSVDownload: FC = () => {
>
<div className="system-xs-medium flex h-[18px] items-center space-x-1 text-text-accent">
<DownloadIcon className="mr-1 h-3 w-3" />
{t('appAnnotation.batchModal.template')}
{t('batchModal.template', { ns: 'appAnnotation' })}
</div>
</CSVDownloader>
</div>

View File

@ -50,7 +50,7 @@ const CSVUploader: FC<Props> = ({
return
const files = [...e.dataTransfer.files]
if (files.length > 1) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.count') })
notify({ type: 'error', message: t('stepOne.uploader.validation.count', { ns: 'datasetCreation' }) })
return
}
updateFile(files[0])
@ -98,8 +98,8 @@ const CSVUploader: FC<Props> = ({
<div className="flex w-full items-center justify-center space-x-2">
<CSVIcon className="shrink-0" />
<div className="text-text-tertiary">
{t('appAnnotation.batchModal.csvUploadTitle')}
<span className="cursor-pointer text-text-accent" onClick={selectHandle}>{t('appAnnotation.batchModal.browse')}</span>
{t('batchModal.csvUploadTitle', { ns: 'appAnnotation' })}
<span className="cursor-pointer text-text-accent" onClick={selectHandle}>{t('batchModal.browse', { ns: 'appAnnotation' })}</span>
</div>
</div>
{dragging && <div ref={dragRef} className="absolute left-0 top-0 h-full w-full" />}
@ -113,7 +113,7 @@ const CSVUploader: FC<Props> = ({
<span className="shrink-0 text-text-tertiary">.csv</span>
</div>
<div className="hidden items-center group-hover:flex">
<Button variant="secondary" onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
<Button variant="secondary" onClick={selectHandle}>{t('stepOne.uploader.change', { ns: 'datasetCreation' })}</Button>
<div className="mx-2 h-4 w-px bg-divider-regular" />
<div className="cursor-pointer p-2" onClick={removeFile} data-testid="remove-file-button">
<RiDeleteBinLine className="h-4 w-4 text-text-tertiary" />

View File

@ -54,15 +54,15 @@ const BatchModal: FC<IBatchModalProps> = ({
if (res.job_status === ProcessStatus.WAITING || res.job_status === ProcessStatus.PROCESSING)
setTimeout(() => checkProcess(res.job_id), 2500)
if (res.job_status === ProcessStatus.ERROR)
notify({ type: 'error', message: `${t('appAnnotation.batchModal.runError')}` })
notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}` })
if (res.job_status === ProcessStatus.COMPLETED) {
notify({ type: 'success', message: `${t('appAnnotation.batchModal.completed')}` })
notify({ type: 'success', message: `${t('batchModal.completed', { ns: 'appAnnotation' })}` })
onAdded()
onCancel()
}
}
catch (e: any) {
notify({ type: 'error', message: `${t('appAnnotation.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` })
notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}` })
}
}
@ -78,7 +78,7 @@ const BatchModal: FC<IBatchModalProps> = ({
checkProcess(res.job_id)
}
catch (e: any) {
notify({ type: 'error', message: `${t('appAnnotation.batchModal.runError')}${'message' in e ? `: ${e.message}` : ''}` })
notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}` })
}
}
@ -90,7 +90,7 @@ const BatchModal: FC<IBatchModalProps> = ({
return (
<Modal isShow={isShow} onClose={noop} className="!max-w-[520px] !rounded-xl px-8 py-6">
<div className="system-xl-medium relative pb-1 text-text-primary">{t('appAnnotation.batchModal.title')}</div>
<div className="system-xl-medium relative pb-1 text-text-primary">{t('batchModal.title', { ns: 'appAnnotation' })}</div>
<div className="absolute right-4 top-4 cursor-pointer p-2" onClick={onCancel}>
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
</div>
@ -108,7 +108,7 @@ const BatchModal: FC<IBatchModalProps> = ({
<div className="mt-[28px] flex justify-end pt-6">
<Button className="system-sm-medium mr-2 text-text-tertiary" onClick={onCancel}>
{t('appAnnotation.batchModal.cancel')}
{t('batchModal.cancel', { ns: 'appAnnotation' })}
</Button>
<Button
variant="primary"
@ -116,7 +116,7 @@ const BatchModal: FC<IBatchModalProps> = ({
disabled={isAnnotationFull || !currentCSV}
loading={importStatus === ProcessStatus.PROCESSING || importStatus === ProcessStatus.WAITING}
>
{t('appAnnotation.batchModal.run')}
{t('batchModal.run', { ns: 'appAnnotation' })}
</Button>
</div>
</Modal>

View File

@ -4,13 +4,16 @@ import ClearAllAnnotationsConfirmModal from './index'
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => {
t: (key: string, options?: { ns?: string }) => {
const translations: Record<string, string> = {
'appAnnotation.table.header.clearAllConfirm': 'Clear all annotations?',
'common.operation.confirm': 'Confirm',
'common.operation.cancel': 'Cancel',
'table.header.clearAllConfirm': 'Clear all annotations?',
'operation.confirm': 'Confirm',
'operation.cancel': 'Cancel',
}
return translations[key] || key
if (translations[key])
return translations[key]
const prefix = options?.ns ? `${options.ns}.` : ''
return `${prefix}${key}`
},
}),
}))

View File

@ -24,7 +24,7 @@ const ClearAllAnnotationsConfirmModal: FC<Props> = ({
onCancel={onHide}
onConfirm={onConfirm}
type="danger"
title={t('appAnnotation.table.header.clearAllConfirm')}
title={t('table.header.clearAllConfirm', { ns: 'appAnnotation' })}
/>
)
}

View File

@ -43,9 +43,9 @@ const EditItem: FC<Props> = ({
const [newContent, setNewContent] = useState('')
const showNewContent = newContent && newContent !== content
const avatar = type === EditItemType.Query ? <User className="h-6 w-6" /> : <Robot className="h-6 w-6" />
const name = type === EditItemType.Query ? t('appAnnotation.editModal.queryName') : t('appAnnotation.editModal.answerName')
const editTitle = type === EditItemType.Query ? t('appAnnotation.editModal.yourQuery') : t('appAnnotation.editModal.yourAnswer')
const placeholder = type === EditItemType.Query ? t('appAnnotation.editModal.queryPlaceholder') : t('appAnnotation.editModal.answerPlaceholder')
const name = type === EditItemType.Query ? t('editModal.queryName', { ns: 'appAnnotation' }) : t('editModal.answerName', { ns: 'appAnnotation' })
const editTitle = type === EditItemType.Query ? t('editModal.yourQuery', { ns: 'appAnnotation' }) : t('editModal.yourAnswer', { ns: 'appAnnotation' })
const placeholder = type === EditItemType.Query ? t('editModal.queryPlaceholder', { ns: 'appAnnotation' }) : t('editModal.answerPlaceholder', { ns: 'appAnnotation' })
const [isEdit, setIsEdit] = useState(false)
// Reset newContent when content prop changes
@ -95,7 +95,7 @@ const EditItem: FC<Props> = ({
}}
>
<RiEditLine className="mr-1 h-3.5 w-3.5" />
<div>{t('common.operation.edit')}</div>
<div>{t('operation.edit', { ns: 'common' })}</div>
</div>
)}
@ -119,7 +119,7 @@ const EditItem: FC<Props> = ({
<div className="h-3.5 w-3.5">
<RiDeleteBinLine className="h-3.5 w-3.5" />
</div>
<div>{t('common.operation.delete')}</div>
<div>{t('operation.delete', { ns: 'common' })}</div>
</div>
</div>
)}
@ -136,8 +136,8 @@ const EditItem: FC<Props> = ({
autoFocus
/>
<div className="mt-2 flex space-x-2">
<Button size="small" variant="primary" onClick={handleSave}>{t('common.operation.save')}</Button>
<Button size="small" onClick={handleCancel}>{t('common.operation.cancel')}</Button>
<Button size="small" variant="primary" onClick={handleSave}>{t('operation.save', { ns: 'common' })}</Button>
<Button size="small" onClick={handleCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
</div>
</div>
)}

View File

@ -73,12 +73,12 @@ const EditAnnotationModal: FC<Props> = ({
}
Toast.notify({
message: t('common.api.actionSuccess') as string,
message: t('api.actionSuccess', { ns: 'common' }) as string,
type: 'success',
})
}
catch (error) {
const fallbackMessage = t('common.api.actionFailed') as string
const fallbackMessage = t('api.actionFailed', { ns: 'common' }) as string
const message = error instanceof Error && error.message ? error.message : fallbackMessage
Toast.notify({
message,
@ -96,7 +96,7 @@ const EditAnnotationModal: FC<Props> = ({
isShow={isShow}
onHide={onHide}
maxWidthClassName="!max-w-[480px]"
title={t('appAnnotation.editModal.title') as string}
title={t('editModal.title', { ns: 'appAnnotation' }) as string}
body={(
<div>
<div className="space-y-6 p-6 pb-4">
@ -120,7 +120,7 @@ const EditAnnotationModal: FC<Props> = ({
setShowModal(false)
onHide()
}}
title={t('appDebug.feature.annotation.removeConfirm')}
title={t('feature.annotation.removeConfirm', { ns: 'appDebug' })}
/>
</div>
</div>
@ -142,13 +142,13 @@ const EditAnnotationModal: FC<Props> = ({
onClick={() => setShowModal(true)}
>
<MessageCheckRemove />
<div>{t('appAnnotation.editModal.removeThisCache')}</div>
<div>{t('editModal.removeThisCache', { ns: 'appAnnotation' })}</div>
</div>
{createdAt && (
<div>
{t('appAnnotation.editModal.createdAt')}
{t('editModal.createdAt', { ns: 'appAnnotation' })}
&nbsp;
{formatTime(createdAt, t('appLog.dateTimeFormat') as string)}
{formatTime(createdAt, t('dateTimeFormat', { ns: 'appLog' }) as string)}
</div>
)}
</div>

View File

@ -18,11 +18,11 @@ const EmptyElement: FC = () => {
<div className="flex h-full items-center justify-center">
<div className="box-border h-fit w-[560px] rounded-2xl bg-background-section-burn px-5 py-4">
<span className="system-md-semibold text-text-secondary">
{t('appAnnotation.noData.title')}
{t('noData.title', { ns: 'appAnnotation' })}
<ThreeDotsIcon className="relative -left-1.5 -top-3 inline" />
</span>
<div className="system-sm-regular mt-2 text-text-tertiary">
{t('appAnnotation.noData.description')}
{t('noData.description', { ns: 'appAnnotation' })}
</div>
</div>
</div>

View File

@ -33,7 +33,7 @@ const Filter: FC<IFilterProps> = ({
showLeftIcon
showClearIcon
value={queryParams.keyword}
placeholder={t('common.operation.search')!}
placeholder={t('operation.search', { ns: 'common' })!}
onChange={(e) => {
setQueryParams({ ...queryParams, keyword: e.target.value })
}}

View File

@ -108,12 +108,12 @@ const HeaderOptions: FC<Props> = ({
}}
>
<FilePlus02 className="h-4 w-4 text-text-tertiary" />
<span className="system-sm-regular grow text-left text-text-secondary">{t('appAnnotation.table.header.bulkImport')}</span>
<span className="system-sm-regular grow text-left text-text-secondary">{t('table.header.bulkImport', { ns: 'appAnnotation' })}</span>
</button>
<Menu as="div" className="relative h-full w-full">
<MenuButton className="mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 hover:bg-components-panel-on-panel-item-bg-hover disabled:opacity-50">
<FileDownload02 className="h-4 w-4 text-text-tertiary" />
<span className="system-sm-regular grow text-left text-text-secondary">{t('appAnnotation.table.header.bulkExport')}</span>
<span className="system-sm-regular grow text-left text-text-secondary">{t('table.header.bulkExport', { ns: 'appAnnotation' })}</span>
<ChevronRight className="h-[14px] w-[14px] shrink-0 text-text-tertiary" />
</MenuButton>
<Transition
@ -156,7 +156,7 @@ const HeaderOptions: FC<Props> = ({
>
<RiDeleteBinLine className="h-4 w-4" />
<span className="system-sm-regular grow text-left">
{t('appAnnotation.table.header.clearAll')}
{t('table.header.clearAll', { ns: 'appAnnotation' })}
</span>
</button>
</div>
@ -169,7 +169,7 @@ const HeaderOptions: FC<Props> = ({
<div className="flex space-x-2">
<Button variant="primary" onClick={() => setShowAddModal(true)}>
<RiAddLine className="mr-0.5 h-4 w-4" />
<div>{t('appAnnotation.table.header.addAnnotation')}</div>
<div>{t('table.header.addAnnotation', { ns: 'appAnnotation' })}</div>
</Button>
<CustomPopover
htmlContent={<Operations />}

View File

@ -98,14 +98,14 @@ const Annotation: FC<Props> = (props) => {
const handleAdd = async (payload: AnnotationItemBasic) => {
await addAnnotation(appDetail.id, payload)
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
const handleRemove = async (id: string) => {
await delAnnotation(appDetail.id, id)
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
@ -113,13 +113,13 @@ const Annotation: FC<Props> = (props) => {
const handleBatchDelete = async () => {
try {
await delAnnotations(appDetail.id, selectedIds)
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
setSelectedIds([])
}
catch (e: any) {
Toast.notify({ type: 'error', message: e.message || t('common.api.actionFailed') })
Toast.notify({ type: 'error', message: e.message || t('api.actionFailed', { ns: 'common' }) })
}
}
@ -132,7 +132,7 @@ const Annotation: FC<Props> = (props) => {
if (!currItem)
return
await editAnnotation(appDetail.id, currItem.id, { question, answer })
Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' })
Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
fetchList()
setControlUpdateList(Date.now())
}
@ -144,7 +144,7 @@ const Annotation: FC<Props> = (props) => {
return (
<div className="flex h-full flex-col">
<p className="system-sm-regular text-text-tertiary">{t('appLog.description')}</p>
<p className="system-sm-regular text-text-tertiary">{t('description', { ns: 'appLog' })}</p>
<div className="relative flex h-full flex-1 flex-col py-4">
<Filter appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams}>
<div className="flex items-center space-x-2">
@ -152,7 +152,7 @@ const Annotation: FC<Props> = (props) => {
<>
<div className={cn(!annotationConfig?.enabled && 'pr-2', 'flex h-7 items-center space-x-1 rounded-lg border border-components-panel-border bg-components-panel-bg-blur pl-2')}>
<MessageFast className="h-4 w-4 text-util-colors-indigo-indigo-600" />
<div className="system-sm-medium text-text-primary">{t('appAnnotation.name')}</div>
<div className="system-sm-medium text-text-primary">{t('name', { ns: 'appAnnotation' })}</div>
<Switch
key={controlRefreshSwitch}
defaultValue={annotationConfig?.enabled}
@ -171,7 +171,7 @@ const Annotation: FC<Props> = (props) => {
await ensureJobCompleted(jobId, AnnotationEnableStatus.disable)
await fetchAnnotationConfig()
Toast.notify({
message: t('common.api.actionSuccess'),
message: t('api.actionSuccess', { ns: 'common' }),
type: 'success',
})
}
@ -264,7 +264,7 @@ const Annotation: FC<Props> = (props) => {
await fetchAnnotationConfig()
Toast.notify({
message: t('common.api.actionSuccess'),
message: t('api.actionSuccess', { ns: 'common' }),
type: 'success',
})
setIsShowEdit(false)

View File

@ -68,11 +68,11 @@ const List: FC<Props> = ({
onCheck={handleSelectAll}
/>
</td>
<td className="w-5 whitespace-nowrap bg-background-section-burn pl-2 pr-1">{t('appAnnotation.table.header.question')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.table.header.answer')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.table.header.createdAt')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.table.header.hits')}</td>
<td className="w-[96px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.table.header.actions')}</td>
<td className="w-5 whitespace-nowrap bg-background-section-burn pl-2 pr-1">{t('table.header.question', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('table.header.answer', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('table.header.createdAt', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('table.header.hits', { ns: 'appAnnotation' })}</td>
<td className="w-[96px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3">{t('table.header.actions', { ns: 'appAnnotation' })}</td>
</tr>
</thead>
<tbody className="system-sm-regular text-text-secondary">
@ -110,7 +110,7 @@ const List: FC<Props> = ({
>
{item.answer}
</td>
<td className="p-3 pr-2">{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
<td className="p-3 pr-2">{formatTime(item.created_at, t('dateTimeFormat', { ns: 'appLog' }) as string)}</td>
<td className="p-3 pr-2">{item.hit_count}</td>
<td className="w-[96px] p-3 pr-2" onClick={e => e.stopPropagation()}>
{/* Actions */}

View File

@ -4,13 +4,16 @@ import RemoveAnnotationConfirmModal from './index'
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => {
t: (key: string, options?: { ns?: string }) => {
const translations: Record<string, string> = {
'appDebug.feature.annotation.removeConfirm': 'Remove annotation?',
'common.operation.confirm': 'Confirm',
'common.operation.cancel': 'Cancel',
'feature.annotation.removeConfirm': 'Remove annotation?',
'operation.confirm': 'Confirm',
'operation.cancel': 'Cancel',
}
return translations[key] || key
if (translations[key])
return translations[key]
const prefix = options?.ns ? `${options.ns}.` : ''
return `${prefix}${key}`
},
}),
}))

View File

@ -22,7 +22,7 @@ const RemoveAnnotationConfirmModal: FC<Props> = ({
isShow={isShow}
onCancel={onHide}
onConfirm={onRemove}
title={t('appDebug.feature.annotation.removeConfirm')}
title={t('feature.annotation.removeConfirm', { ns: 'appDebug' })}
/>
)
}

View File

@ -11,7 +11,7 @@ const HitHistoryNoData: FC = () => {
<div className="inline-block rounded-lg border border-divider-subtle p-3">
<ClockFastForward className="h-5 w-5 text-text-tertiary" />
</div>
<div className="system-sm-regular text-text-tertiary">{t('appAnnotation.viewModal.noHitHistory')}</div>
<div className="system-sm-regular text-text-tertiary">{t('viewModal.noHitHistory', { ns: 'appAnnotation' })}</div>
</div>
)
}

View File

@ -81,20 +81,20 @@ const ViewAnnotationModal: FC<Props> = ({
}, [id, isShow])
const tabs = [
{ value: TabType.annotation, text: t('appAnnotation.viewModal.annotatedResponse') },
{ value: TabType.annotation, text: t('viewModal.annotatedResponse', { ns: 'appAnnotation' }) },
{
value: TabType.hitHistory,
text: (
hitHistoryList.length > 0
? (
<div className="flex items-center space-x-1">
<div>{t('appAnnotation.viewModal.hitHistory')}</div>
<div>{t('viewModal.hitHistory', { ns: 'appAnnotation' })}</div>
<Badge
text={`${total} ${t(`appAnnotation.viewModal.hit${hitHistoryList.length > 1 ? 's' : ''}`)}`}
text={`${total} ${t(`viewModal.hit${hitHistoryList.length > 1 ? 's' : ''}`, { ns: 'appAnnotation' })}`}
/>
</div>
)
: t('appAnnotation.viewModal.hitHistory')
: t('viewModal.hitHistory', { ns: 'appAnnotation' })
),
},
]
@ -139,12 +139,12 @@ const ViewAnnotationModal: FC<Props> = ({
<table className={cn('w-full min-w-[440px] border-collapse border-0')}>
<thead className="system-xs-medium-uppercase text-text-tertiary">
<tr>
<td className="w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1">{t('appAnnotation.hitHistoryTable.query')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.hitHistoryTable.match')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.hitHistoryTable.response')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.hitHistoryTable.source')}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.hitHistoryTable.score')}</td>
<td className="w-[160px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3">{t('appAnnotation.hitHistoryTable.time')}</td>
<td className="w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1">{t('hitHistoryTable.query', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('hitHistoryTable.match', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('hitHistoryTable.response', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('hitHistoryTable.source', { ns: 'appAnnotation' })}</td>
<td className="whitespace-nowrap bg-background-section-burn py-1.5 pl-3">{t('hitHistoryTable.score', { ns: 'appAnnotation' })}</td>
<td className="w-[160px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3">{t('hitHistoryTable.time', { ns: 'appAnnotation' })}</td>
</tr>
</thead>
<tbody className="system-sm-regular text-text-secondary">
@ -173,7 +173,7 @@ const ViewAnnotationModal: FC<Props> = ({
</td>
<td className="p-3 pr-2">{item.source}</td>
<td className="p-3 pr-2">{item.score ? item.score.toFixed(2) : '-'}</td>
<td className="p-3 pr-2">{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
<td className="p-3 pr-2">{formatTime(item.created_at, t('dateTimeFormat', { ns: 'appLog' }) as string)}</td>
</tr>
))}
</tbody>
@ -220,7 +220,7 @@ const ViewAnnotationModal: FC<Props> = ({
setShowModal(false)
onHide()
}}
title={t('appDebug.feature.annotation.removeConfirm')}
title={t('feature.annotation.removeConfirm', { ns: 'appDebug' })}
/>
</div>
)}
@ -232,12 +232,12 @@ const ViewAnnotationModal: FC<Props> = ({
onClick={() => setShowModal(true)}
>
<MessageCheckRemove />
<div>{t('appAnnotation.editModal.removeThisCache')}</div>
<div>{t('editModal.removeThisCache', { ns: 'appAnnotation' })}</div>
</div>
<div>
{t('appAnnotation.editModal.createdAt')}
{t('editModal.createdAt', { ns: 'appAnnotation' })}
&nbsp;
{formatTime(createdAt, t('appLog.dateTimeFormat') as string)}
{formatTime(createdAt, t('dateTimeFormat', { ns: 'appLog' }) as string)}
</div>
</div>
)

View File

@ -49,14 +49,14 @@ export default function AddMemberOrGroupDialog() {
<PortalToFollowElemTrigger asChild>
<Button variant="ghost-accent" size="small" className="flex shrink-0 items-center gap-x-0.5" onClick={() => setOpen(!open)}>
<RiAddCircleFill className="h-4 w-4" />
<span>{t('common.operation.add')}</span>
<span>{t('operation.add', { ns: 'common' })}</span>
</Button>
</PortalToFollowElemTrigger>
{open && <FloatingOverlay />}
<PortalToFollowElemContent className="z-[100]">
<div className="relative flex max-h-[400px] w-[400px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]">
<div className="sticky top-0 z-10 bg-components-panel-bg-blur p-2 pb-0.5 backdrop-blur-[5px]">
<Input value={keyword} onChange={handleKeywordChange} showLeftIcon placeholder={t('app.accessControlDialog.operateGroupAndMember.searchPlaceholder') as string} />
<Input value={keyword} onChange={handleKeywordChange} showLeftIcon placeholder={t('accessControlDialog.operateGroupAndMember.searchPlaceholder', { ns: 'app' }) as string} />
</div>
{
isLoading
@ -76,7 +76,7 @@ export default function AddMemberOrGroupDialog() {
)
: (
<div className="flex h-7 items-center justify-center px-2 py-0.5">
<span className="system-xs-regular text-text-tertiary">{t('app.accessControlDialog.operateGroupAndMember.noResult')}</span>
<span className="system-xs-regular text-text-tertiary">{t('accessControlDialog.operateGroupAndMember.noResult', { ns: 'app' })}</span>
</div>
)
}
@ -115,7 +115,7 @@ function SelectedGroupsBreadCrumb() {
}, [setSelectedGroupsForBreadcrumb])
return (
<div className="flex h-7 items-center gap-x-0.5 px-2 py-0.5">
<span className={cn('system-xs-regular text-text-tertiary', selectedGroupsForBreadcrumb.length > 0 && 'cursor-pointer text-text-accent')} onClick={handleReset}>{t('app.accessControlDialog.operateGroupAndMember.allMembers')}</span>
<span className={cn('system-xs-regular text-text-tertiary', selectedGroupsForBreadcrumb.length > 0 && 'cursor-pointer text-text-accent')} onClick={handleReset}>{t('accessControlDialog.operateGroupAndMember.allMembers', { ns: 'app' })}</span>
{selectedGroupsForBreadcrumb.map((group, index) => {
return (
<div key={index} className="system-xs-regular flex items-center gap-x-0.5 text-text-tertiary">
@ -171,7 +171,7 @@ function GroupItem({ group }: GroupItemProps) {
className="flex shrink-0 items-center justify-between px-1.5 py-1"
onClick={handleExpandClick}
>
<span className="px-[3px]">{t('app.accessControlDialog.operateGroupAndMember.expand')}</span>
<span className="px-[3px]">{t('accessControlDialog.operateGroupAndMember.expand', { ns: 'app' })}</span>
<RiArrowRightSLine className="h-4 w-4" />
</Button>
</BaseItem>
@ -210,7 +210,7 @@ function MemberItem({ member }: MemberItemProps) {
{currentUser.email === member.email && (
<p className="system-xs-regular text-text-tertiary">
(
{t('common.you')}
{t('you', { ns: 'common' })}
)
</p>
)}

View File

@ -61,25 +61,25 @@ export default function AccessControl(props: AccessControlProps) {
submitData.subjects = subjects
}
await updateAccessMode(submitData)
Toast.notify({ type: 'success', message: t('app.accessControlDialog.updateSuccess') })
Toast.notify({ type: 'success', message: t('accessControlDialog.updateSuccess', { ns: 'app' }) })
onConfirm?.()
}, [updateAccessMode, app, specificGroups, specificMembers, t, onConfirm, currentMenu])
return (
<AccessControlDialog show onClose={onClose}>
<div className="flex flex-col gap-y-3">
<div className="pb-3 pl-6 pr-14 pt-6">
<DialogTitle className="title-2xl-semi-bold text-text-primary">{t('app.accessControlDialog.title')}</DialogTitle>
<DialogDescription className="system-xs-regular mt-1 text-text-tertiary">{t('app.accessControlDialog.description')}</DialogDescription>
<DialogTitle className="title-2xl-semi-bold text-text-primary">{t('accessControlDialog.title', { ns: 'app' })}</DialogTitle>
<DialogDescription className="system-xs-regular mt-1 text-text-tertiary">{t('accessControlDialog.description', { ns: 'app' })}</DialogDescription>
</div>
<div className="flex flex-col gap-y-1 px-6 pb-3">
<div className="leading-6">
<p className="system-sm-medium text-text-tertiary">{t('app.accessControlDialog.accessLabel')}</p>
<p className="system-sm-medium text-text-tertiary">{t('accessControlDialog.accessLabel', { ns: 'app' })}</p>
</div>
<AccessControlItem type={AccessMode.ORGANIZATION}>
<div className="flex items-center p-3">
<div className="flex grow items-center gap-x-2">
<RiBuildingLine className="h-4 w-4 text-text-primary" />
<p className="system-sm-medium text-text-primary">{t('app.accessControlDialog.accessItems.organization')}</p>
<p className="system-sm-medium text-text-primary">{t('accessControlDialog.accessItems.organization', { ns: 'app' })}</p>
</div>
</div>
</AccessControlItem>
@ -90,7 +90,7 @@ export default function AccessControl(props: AccessControlProps) {
<div className="flex items-center p-3">
<div className="flex grow items-center gap-x-2">
<RiVerifiedBadgeLine className="h-4 w-4 text-text-primary" />
<p className="system-sm-medium text-text-primary">{t('app.accessControlDialog.accessItems.external')}</p>
<p className="system-sm-medium text-text-primary">{t('accessControlDialog.accessItems.external', { ns: 'app' })}</p>
</div>
{!hideTip && <WebAppSSONotEnabledTip />}
</div>
@ -98,13 +98,13 @@ export default function AccessControl(props: AccessControlProps) {
<AccessControlItem type={AccessMode.PUBLIC}>
<div className="flex items-center gap-x-2 p-3">
<RiGlobalLine className="h-4 w-4 text-text-primary" />
<p className="system-sm-medium text-text-primary">{t('app.accessControlDialog.accessItems.anyone')}</p>
<p className="system-sm-medium text-text-primary">{t('accessControlDialog.accessItems.anyone', { ns: 'app' })}</p>
</div>
</AccessControlItem>
</div>
<div className="flex items-center justify-end gap-x-2 p-6 pt-5">
<Button onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button disabled={isPending} loading={isPending} variant="primary" onClick={handleConfirm}>{t('common.operation.confirm')}</Button>
<Button onClick={onClose}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button disabled={isPending} loading={isPending} variant="primary" onClick={handleConfirm}>{t('operation.confirm', { ns: 'common' })}</Button>
</div>
</div>
</AccessControlDialog>

View File

@ -29,7 +29,7 @@ export default function SpecificGroupsOrMembers() {
<div className="flex items-center p-3">
<div className="flex grow items-center gap-x-2">
<RiLockLine className="h-4 w-4 text-text-primary" />
<p className="system-sm-medium text-text-primary">{t('app.accessControlDialog.accessItems.specific')}</p>
<p className="system-sm-medium text-text-primary">{t('accessControlDialog.accessItems.specific', { ns: 'app' })}</p>
</div>
</div>
)
@ -40,7 +40,7 @@ export default function SpecificGroupsOrMembers() {
<div className="flex items-center gap-x-1 p-3">
<div className="flex grow items-center gap-x-1">
<RiLockLine className="h-4 w-4 text-text-primary" />
<p className="system-sm-medium text-text-primary">{t('app.accessControlDialog.accessItems.specific')}</p>
<p className="system-sm-medium text-text-primary">{t('accessControlDialog.accessItems.specific', { ns: 'app' })}</p>
</div>
<div className="flex items-center gap-x-1">
<AddMemberOrGroupDialog />
@ -60,14 +60,14 @@ function RenderGroupsAndMembers() {
const specificGroups = useAccessControlStore(s => s.specificGroups)
const specificMembers = useAccessControlStore(s => s.specificMembers)
if (specificGroups.length <= 0 && specificMembers.length <= 0)
return <div className="px-2 pb-1.5 pt-5"><p className="system-xs-regular text-center text-text-tertiary">{t('app.accessControlDialog.noGroupsOrMembers')}</p></div>
return <div className="px-2 pb-1.5 pt-5"><p className="system-xs-regular text-center text-text-tertiary">{t('accessControlDialog.noGroupsOrMembers', { ns: 'app' })}</p></div>
return (
<>
<p className="system-2xs-medium-uppercase sticky top-0 text-text-tertiary">{t('app.accessControlDialog.groups', { count: specificGroups.length ?? 0 })}</p>
<p className="system-2xs-medium-uppercase sticky top-0 text-text-tertiary">{t('accessControlDialog.groups', { ns: 'app', count: specificGroups.length ?? 0 })}</p>
<div className="flex flex-row flex-wrap gap-1">
{specificGroups.map((group, index) => <GroupItem key={index} group={group} />)}
</div>
<p className="system-2xs-medium-uppercase sticky top-0 text-text-tertiary">{t('app.accessControlDialog.members', { count: specificMembers.length ?? 0 })}</p>
<p className="system-2xs-medium-uppercase sticky top-0 text-text-tertiary">{t('accessControlDialog.members', { ns: 'app', count: specificMembers.length ?? 0 })}</p>
<div className="flex flex-row flex-wrap gap-1">
{specificMembers.map((member, index) => <MemberItem key={index} member={member} />)}
</div>
@ -138,7 +138,7 @@ function BaseItem({ icon, onRemove, children }: BaseItemProps) {
export function WebAppSSONotEnabledTip() {
const { t } = useTranslation()
return (
<Tooltip asChild={false} popupContent={t('app.accessControlDialog.webAppSSONotEnabledTip')}>
<Tooltip asChild={false} popupContent={t('accessControlDialog.webAppSSONotEnabledTip', { ns: 'app' })}>
<RiAlertFill className="h-4 w-4 shrink-0 text-text-warning-secondary" />
</Tooltip>
)

View File

@ -76,8 +76,8 @@ const FeaturesWrappedAppPublisher = (props: Props) => {
/>
{restoreConfirmOpen && (
<Confirm
title={t('appDebug.resetConfig.title')}
content={t('appDebug.resetConfig.message')}
title={t('resetConfig.title', { ns: 'appDebug' })}
content={t('resetConfig.message', { ns: 'appDebug' })}
isShow={restoreConfirmOpen}
onConfirm={handleConfirm}
onCancel={() => setRestoreConfirmOpen(false)}

View File

@ -1,5 +1,6 @@
import type { ModelAndParameter } from '../configuration/debug/types'
import type { InputVar, Variable } from '@/app/components/workflow/types'
import type { I18nKeysByPrefix } from '@/types/i18n'
import type { PublishWorkflowParams } from '@/types/workflow'
import {
RiArrowDownSLine,
@ -53,7 +54,9 @@ import AccessControl from '../app-access-control'
import PublishWithMultipleModel from './publish-with-multiple-model'
import SuggestedAction from './suggested-action'
const ACCESS_MODE_MAP: Record<AccessMode, { label: string, icon: React.ElementType }> = {
type AccessModeLabel = I18nKeysByPrefix<'app', 'accessControlDialog.accessItems.'>
const ACCESS_MODE_MAP: Record<AccessMode, { label: AccessModeLabel, icon: React.ElementType }> = {
[AccessMode.ORGANIZATION]: {
label: 'organization',
icon: RiBuildingLine,
@ -84,7 +87,7 @@ const AccessModeDisplay: React.FC<{ mode?: AccessMode }> = ({ mode }) => {
<>
<Icon className="h-4 w-4 shrink-0 text-text-secondary" />
<div className="grow truncate">
<span className="system-sm-medium text-text-secondary">{t(`app.accessControlDialog.accessItems.${label}` as any) as string}</span>
<span className="system-sm-medium text-text-secondary">{t(`accessControlDialog.accessItems.${label}`, { ns: 'app' })}</span>
</div>
</>
)
@ -162,11 +165,11 @@ const AppPublisher = ({
const disabledFunctionTooltip = useMemo(() => {
if (!publishedAt)
return t('app.notPublishedYet')
return t('notPublishedYet', { ns: 'app' })
if (missingStartNode)
return t('app.noUserInputNode')
return t('noUserInputNode', { ns: 'app' })
if (noAccessPermission)
return t('app.noAccessPermission')
return t('noAccessPermission', { ns: 'app' })
}, [missingStartNode, noAccessPermission, publishedAt])
useEffect(() => {
@ -256,7 +259,7 @@ const AppPublisher = ({
const hasPublishedVersion = !!publishedAt
const workflowToolDisabled = !hasPublishedVersion || !workflowToolAvailable
const workflowToolMessage = workflowToolDisabled ? t('workflow.common.workflowAsToolDisabledHint') : undefined
const workflowToolMessage = workflowToolDisabled ? t('common.workflowAsToolDisabledHint', { ns: 'workflow' }) : undefined
const showStartNodeLimitHint = Boolean(startNodeLimitExceeded)
const upgradeHighlightStyle = useMemo(() => ({
background: 'linear-gradient(97deg, var(--components-input-border-active-prompt-1, rgba(11, 165, 236, 0.95)) -3.64%, var(--components-input-border-active-prompt-2, rgba(21, 90, 239, 0.95)) 45.14%)',
@ -282,7 +285,7 @@ const AppPublisher = ({
className="py-2 pl-3 pr-2"
disabled={disabled}
>
{t('workflow.common.publish')}
{t('common.publish', { ns: 'workflow' })}
<RiArrowDownSLine className="h-4 w-4 text-components-button-primary-text" />
</Button>
</PortalToFollowElemTrigger>
@ -290,13 +293,13 @@ const AppPublisher = ({
<div className="w-[320px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5">
<div className="p-4 pt-3">
<div className="system-xs-medium-uppercase flex h-6 items-center text-text-tertiary">
{publishedAt ? t('workflow.common.latestPublished') : t('workflow.common.currentDraftUnpublished')}
{publishedAt ? t('common.latestPublished', { ns: 'workflow' }) : t('common.currentDraftUnpublished', { ns: 'workflow' })}
</div>
{publishedAt
? (
<div className="flex items-center justify-between">
<div className="system-sm-medium flex items-center text-text-secondary">
{t('workflow.common.publishedAt')}
{t('common.publishedAt', { ns: 'workflow' })}
{' '}
{formatTimeFromNow(publishedAt)}
</div>
@ -307,14 +310,14 @@ const AppPublisher = ({
onClick={handleRestore}
disabled={published}
>
{t('workflow.common.restore')}
{t('common.restore', { ns: 'workflow' })}
</Button>
)}
</div>
)
: (
<div className="system-sm-medium flex items-center text-text-secondary">
{t('workflow.common.autoSaved')}
{t('common.autoSaved', { ns: 'workflow' })}
{' '}
·
{Boolean(draftUpdatedAt) && formatTimeFromNow(draftUpdatedAt!)}
@ -338,10 +341,10 @@ const AppPublisher = ({
>
{
published
? t('workflow.common.published')
? t('common.published', { ns: 'workflow' })
: (
<div className="flex gap-1">
<span>{t('workflow.common.publishUpdate')}</span>
<span>{t('common.publishUpdate', { ns: 'workflow' })}</span>
<div className="flex gap-0.5">
{PUBLISH_SHORTCUT.map(key => (
<span key={key} className="system-kbd h-4 w-4 rounded-[4px] bg-components-kbd-bg-white text-text-primary-on-surface">
@ -359,11 +362,11 @@ const AppPublisher = ({
className="text-sm font-semibold leading-5 text-transparent"
style={upgradeHighlightStyle}
>
<span className="block">{t('workflow.publishLimit.startNodeTitlePrefix')}</span>
<span className="block">{t('workflow.publishLimit.startNodeTitleSuffix')}</span>
<span className="block">{t('publishLimit.startNodeTitlePrefix', { ns: 'workflow' })}</span>
<span className="block">{t('publishLimit.startNodeTitleSuffix', { ns: 'workflow' })}</span>
</p>
<p className="mt-1 text-xs leading-4 text-text-secondary">
{t('workflow.publishLimit.startNodeDesc')}
{t('publishLimit.startNodeDesc', { ns: 'workflow' })}
</p>
<UpgradeBtn
isShort
@ -382,7 +385,7 @@ const AppPublisher = ({
{systemFeatures.webapp_auth.enabled && (
<div className="p-4 pt-3">
<div className="flex h-6 items-center">
<p className="system-xs-medium text-text-tertiary">{t('app.publishApp.title')}</p>
<p className="system-xs-medium text-text-tertiary">{t('publishApp.title', { ns: 'app' })}</p>
</div>
<div
className="flex h-8 cursor-pointer items-center gap-x-0.5 rounded-lg bg-components-input-bg-normal py-1 pl-2.5 pr-2 hover:bg-primary-50 hover:text-text-accent"
@ -393,12 +396,12 @@ const AppPublisher = ({
<div className="flex grow items-center gap-x-1.5 overflow-hidden pr-1">
<AccessModeDisplay mode={appDetail?.access_mode} />
</div>
{!isAppAccessSet && <p className="system-xs-regular shrink-0 text-text-tertiary">{t('app.publishApp.notSet')}</p>}
{!isAppAccessSet && <p className="system-xs-regular shrink-0 text-text-tertiary">{t('publishApp.notSet', { ns: 'app' })}</p>}
<div className="flex h-4 w-4 shrink-0 items-center justify-center">
<RiArrowRightSLine className="h-4 w-4 text-text-quaternary" />
</div>
</div>
{!isAppAccessSet && <p className="system-xs-regular mt-1 text-text-warning">{t('app.publishApp.notSetDesc')}</p>}
{!isAppAccessSet && <p className="system-xs-regular mt-1 text-text-warning">{t('publishApp.notSetDesc', { ns: 'app' })}</p>}
</div>
)}
{
@ -412,7 +415,7 @@ const AppPublisher = ({
link={appURL}
icon={<RiPlayCircleLine className="h-4 w-4" />}
>
{t('workflow.common.runApp')}
{t('common.runApp', { ns: 'workflow' })}
</SuggestedAction>
</Tooltip>
{appDetail?.mode === AppModeEnum.WORKFLOW || appDetail?.mode === AppModeEnum.COMPLETION
@ -424,7 +427,7 @@ const AppPublisher = ({
link={`${appURL}${appURL.includes('?') ? '&' : '?'}mode=batch`}
icon={<RiPlayList2Line className="h-4 w-4" />}
>
{t('workflow.common.batchRunApp')}
{t('common.batchRunApp', { ns: 'workflow' })}
</SuggestedAction>
</Tooltip>
)
@ -437,7 +440,7 @@ const AppPublisher = ({
disabled={!publishedAt}
icon={<CodeBrowser className="h-4 w-4" />}
>
{t('workflow.common.embedIntoSite')}
{t('common.embedIntoSite', { ns: 'workflow' })}
</SuggestedAction>
)}
<Tooltip triggerClassName="flex" disabled={!disabledFunctionButton} popupContent={disabledFunctionTooltip} asChild={false}>
@ -450,17 +453,17 @@ const AppPublisher = ({
disabled={disabledFunctionButton}
icon={<RiPlanetLine className="h-4 w-4" />}
>
{t('workflow.common.openInExplore')}
{t('common.openInExplore', { ns: 'workflow' })}
</SuggestedAction>
</Tooltip>
<Tooltip triggerClassName="flex" disabled={!!publishedAt && !missingStartNode} popupContent={!publishedAt ? t('app.notPublishedYet') : t('app.noUserInputNode')} asChild={false}>
<Tooltip triggerClassName="flex" disabled={!!publishedAt && !missingStartNode} popupContent={!publishedAt ? t('notPublishedYet', { ns: 'app' }) : t('noUserInputNode', { ns: 'app' })} asChild={false}>
<SuggestedAction
className="flex-1"
disabled={!publishedAt || missingStartNode}
link="./develop"
icon={<RiTerminalBoxLine className="h-4 w-4" />}
>
{t('workflow.common.accessAPIReference')}
{t('common.accessAPIReference', { ns: 'workflow' })}
</SuggestedAction>
</Tooltip>
{appDetail?.mode === AppModeEnum.WORKFLOW && (

View File

@ -72,14 +72,14 @@ const PublishWithMultipleModel: FC<PublishWithMultipleModelProps> = ({
disabled={!validModelConfigs.length}
className="mt-3 w-full"
>
{t('appDebug.operation.applyConfig')}
{t('operation.applyConfig', { ns: 'appDebug' })}
<RiArrowDownSLine className="ml-0.5 h-3 w-3" />
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className="z-50 mt-1 w-[288px]">
<div className="rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg">
<div className="flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary">
{t('appDebug.publishAs')}
{t('publishAs', { ns: 'appDebug' })}
</div>
{
validModelConfigs.map((item, index) => (

View File

@ -37,7 +37,7 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
setTitleError(true)
Toast.notify({
type: 'error',
message: t('workflow.versionHistory.editField.titleLengthLimit', { limit: TITLE_MAX_LENGTH }),
message: t('versionHistory.editField.titleLengthLimit', { ns: 'workflow', limit: TITLE_MAX_LENGTH }),
})
return
}
@ -50,7 +50,7 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
setReleaseNotesError(true)
Toast.notify({
type: 'error',
message: t('workflow.versionHistory.editField.releaseNotesLengthLimit', { limit: RELEASE_NOTES_MAX_LENGTH }),
message: t('versionHistory.editField.releaseNotesLengthLimit', { ns: 'workflow', limit: RELEASE_NOTES_MAX_LENGTH }),
})
return
}
@ -75,7 +75,7 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
<Modal className="p-0" isShow={isOpen} onClose={onClose}>
<div className="relative w-full p-6 pb-4 pr-14">
<div className="title-2xl-semi-bold text-text-primary first-letter:capitalize">
{versionInfo?.marked_name ? t('workflow.versionHistory.editVersionInfo') : t('workflow.versionHistory.nameThisVersion')}
{versionInfo?.marked_name ? t('versionHistory.editVersionInfo', { ns: 'workflow' }) : t('versionHistory.nameThisVersion', { ns: 'workflow' })}
</div>
<div className="absolute right-5 top-5 flex h-8 w-8 cursor-pointer items-center justify-center p-1.5" onClick={onClose}>
<RiCloseLine className="h-[18px] w-[18px] text-text-tertiary" />
@ -84,22 +84,22 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
<div className="flex flex-col gap-y-4 px-6 py-3">
<div className="flex flex-col gap-y-1">
<div className="system-sm-semibold flex h-6 items-center text-text-secondary">
{t('workflow.versionHistory.editField.title')}
{t('versionHistory.editField.title', { ns: 'workflow' })}
</div>
<Input
value={title}
placeholder={`${t('workflow.versionHistory.nameThisVersion')}${t('workflow.panel.optional')}`}
placeholder={`${t('versionHistory.nameThisVersion', { ns: 'workflow' })}${t('panel.optional', { ns: 'workflow' })}`}
onChange={handleTitleChange}
destructive={titleError}
/>
</div>
<div className="flex flex-col gap-y-1">
<div className="system-sm-semibold flex h-6 items-center text-text-secondary">
{t('workflow.versionHistory.editField.releaseNotes')}
{t('versionHistory.editField.releaseNotes', { ns: 'workflow' })}
</div>
<Textarea
value={releaseNotes}
placeholder={`${t('workflow.versionHistory.releaseNotesPlaceholder')}${t('workflow.panel.optional')}`}
placeholder={`${t('versionHistory.releaseNotesPlaceholder', { ns: 'workflow' })}${t('panel.optional', { ns: 'workflow' })}`}
onChange={handleDescriptionChange}
destructive={releaseNotesError}
/>
@ -107,8 +107,8 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
</div>
<div className="flex justify-end p-6 pt-5">
<div className="flex items-center gap-x-3">
<Button onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant="primary" onClick={handlePublish}>{t('workflow.common.publish')}</Button>
<Button onClick={onClose}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button variant="primary" onClick={handlePublish}>{t('common.publish', { ns: 'workflow' })}</Button>
</div>
</div>
</Modal>

View File

@ -37,7 +37,7 @@ const OperationBtn: FC<IOperationBtnProps> = ({
{iconMap[type]}
</div>
<div className="text-xs font-medium">
{actionName || t(`common.operation.${type}`)}
{actionName || t(`operation.${type}`, { ns: 'common' })}
</div>
</div>
)

View File

@ -16,12 +16,12 @@ const FormattingChanged: FC<IFormattingChangedProps> = ({
return (
<WarningMask
title={t('appDebug.feature.dataSet.queryVariable.unableToQueryDataSet')}
description={t('appDebug.feature.dataSet.queryVariable.unableToQueryDataSetTip')}
title={t('feature.dataSet.queryVariable.unableToQueryDataSet', { ns: 'appDebug' })}
description={t('feature.dataSet.queryVariable.unableToQueryDataSetTip', { ns: 'appDebug' })}
footer={(
<div className="flex space-x-2">
<Button variant="primary" className="flex !w-[96px] justify-start" onClick={onConfirm}>
<span className="text-[13px] font-medium">{t('appDebug.feature.dataSet.queryVariable.ok')}</span>
<span className="text-[13px] font-medium">{t('feature.dataSet.queryVariable.ok', { ns: 'appDebug' })}</span>
</Button>
</div>
)}

View File

@ -24,15 +24,15 @@ const FormattingChanged: FC<IFormattingChangedProps> = ({
return (
<WarningMask
title={t('appDebug.formattingChangedTitle')}
description={t('appDebug.formattingChangedText')}
title={t('formattingChangedTitle', { ns: 'appDebug' })}
description={t('formattingChangedText', { ns: 'appDebug' })}
footer={(
<div className="flex space-x-2">
<Button variant="primary" className="flex space-x-2" onClick={onConfirm}>
{icon}
<span>{t('common.operation.refresh')}</span>
<span>{t('operation.refresh', { ns: 'common' })}</span>
</Button>
<Button onClick={onCancel}>{t('common.operation.cancel') as string}</Button>
<Button onClick={onCancel}>{t('operation.cancel', { ns: 'common' }) as string}</Button>
</div>
)}
/>

View File

@ -25,11 +25,11 @@ const HasNotSetAPI: FC<IHasNotSetAPIProps> = ({
return (
<WarningMask
title={isTrailFinished ? t('appDebug.notSetAPIKey.trailFinished') : t('appDebug.notSetAPIKey.title')}
description={t('appDebug.notSetAPIKey.description')}
title={isTrailFinished ? t('notSetAPIKey.trailFinished', { ns: 'appDebug' }) : t('notSetAPIKey.title', { ns: 'appDebug' })}
description={t('notSetAPIKey.description', { ns: 'appDebug' })}
footer={(
<Button variant="primary" className="flex space-x-2" onClick={onSetting}>
<span>{t('appDebug.notSetAPIKey.settingBtn')}</span>
<span>{t('notSetAPIKey.settingBtn', { ns: 'appDebug' })}</span>
{icon}
</Button>
)}

View File

@ -94,7 +94,7 @@ const AdvancedPromptInput: FC<Props> = ({
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable) {
notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: promptVariables[i].key }) })
notify({ type: 'error', message: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }) })
return false
}
}
@ -152,14 +152,14 @@ const AdvancedPromptInput: FC<Props> = ({
>
<div className="flex items-center pr-2">
<RiErrorWarningFill className="mr-1 h-4 w-4 text-[#F79009]" />
<div className="text-[13px] font-medium leading-[18px] text-[#DC6803]">{t('appDebug.promptMode.contextMissing')}</div>
<div className="text-[13px] font-medium leading-[18px] text-[#DC6803]">{t('promptMode.contextMissing', { ns: 'appDebug' })}</div>
</div>
<Button
size="small"
variant="secondary-accent"
onClick={onHideContextMissingTip}
>
{t('common.operation.ok')}
{t('operation.ok', { ns: 'common' })}
</Button>
</div>
)
@ -178,12 +178,12 @@ const AdvancedPromptInput: FC<Props> = ({
<div className="flex items-center space-x-1">
<div className="text-sm font-semibold uppercase text-indigo-800">
{t('appDebug.pageTitle.line1')}
{t('pageTitle.line1', { ns: 'appDebug' })}
</div>
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.promptTip')}
{t('promptTip', { ns: 'appDebug' })}
</div>
)}
/>

View File

@ -54,7 +54,7 @@ const ConfirmAddVar: FC<IConfirmAddVarProps> = ({
{VarIcon}
</div>
<div className="grow-1">
<div className="text-sm font-medium text-text-primary">{t('appDebug.autoAddVar')}</div>
<div className="text-sm font-medium text-text-primary">{t('autoAddVar', { ns: 'appDebug' })}</div>
<div className="mt-[15px] flex max-h-[66px] flex-wrap space-x-1 overflow-y-auto px-1">
{varNameArr.map(name => (
<VarHighlight key={name} name={name} />
@ -63,8 +63,8 @@ const ConfirmAddVar: FC<IConfirmAddVarProps> = ({
</div>
</div>
<div className="mt-7 flex justify-end space-x-2">
<Button onClick={onCancel}>{t('common.operation.cancel')}</Button>
<Button variant="primary" onClick={onConfirm}>{t('common.operation.add')}</Button>
<Button onClick={onCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button variant="primary" onClick={onConfirm}>{t('operation.add', { ns: 'common' })}</Button>
</div>
</div>

View File

@ -26,11 +26,11 @@ const EditModal: FC<Props> = ({
const [tempData, setTempData] = useState(data)
return (
<Modal
title={t('appDebug.feature.conversationHistory.editModal.title')}
title={t('feature.conversationHistory.editModal.title', { ns: 'appDebug' })}
isShow={isShow}
onClose={onClose}
>
<div className="mt-6 text-sm font-medium leading-[21px] text-text-primary">{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
<div className="mt-6 text-sm font-medium leading-[21px] text-text-primary">{t('feature.conversationHistory.editModal.userPrefix', { ns: 'appDebug' })}</div>
<input
className="mt-2 box-border h-10 w-full rounded-lg bg-components-input-bg-normal px-3 text-sm leading-10"
value={tempData.user_prefix}
@ -40,7 +40,7 @@ const EditModal: FC<Props> = ({
})}
/>
<div className="mt-6 text-sm font-medium leading-[21px] text-text-primary">{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
<div className="mt-6 text-sm font-medium leading-[21px] text-text-primary">{t('feature.conversationHistory.editModal.assistantPrefix', { ns: 'appDebug' })}</div>
<input
className="mt-2 box-border h-10 w-full rounded-lg bg-components-input-bg-normal px-3 text-sm leading-10"
value={tempData.assistant_prefix}
@ -48,12 +48,12 @@ const EditModal: FC<Props> = ({
...tempData,
assistant_prefix: e.target.value,
})}
placeholder={t('common.chat.conversationNamePlaceholder') || ''}
placeholder={t('chat.conversationNamePlaceholder', { ns: 'common' }) || ''}
/>
<div className="mt-10 flex justify-end">
<Button className="mr-2 shrink-0" onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant="primary" className="shrink-0" onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
<Button className="mr-2 shrink-0" onClick={onClose}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button variant="primary" className="shrink-0" onClick={() => onSave(tempData)} loading={saveLoading}>{t('operation.save', { ns: 'common' })}</Button>
</div>
</Modal>
)

View File

@ -24,7 +24,7 @@ const HistoryPanel: FC<Props> = ({
className="mt-2"
title={(
<div className="flex items-center gap-2">
<div>{t('appDebug.feature.conversationHistory.title')}</div>
<div>{t('feature.conversationHistory.title', { ns: 'appDebug' })}</div>
</div>
)}
headerIcon={(
@ -34,7 +34,7 @@ const HistoryPanel: FC<Props> = ({
)}
headerRight={(
<div className="flex items-center">
<div className="text-xs text-text-tertiary">{t('appDebug.feature.conversationHistory.description')}</div>
<div className="text-xs text-text-tertiary">{t('feature.conversationHistory.description', { ns: 'appDebug' })}</div>
<div className="ml-3 h-[14px] w-[1px] bg-divider-regular"></div>
<OperationBtn type="edit" onClick={onShowEditModal} />
</div>
@ -44,14 +44,14 @@ const HistoryPanel: FC<Props> = ({
{showWarning && (
<div className="flex justify-between rounded-b-xl bg-background-section-burn px-3 py-2 text-xs text-text-secondary">
<div>
{t('appDebug.feature.conversationHistory.tip')}
{t('feature.conversationHistory.tip', { ns: 'appDebug' })}
<a
href={docLink('/learn-more/extended-reading/what-is-llmops', { 'zh-Hans': '/learn-more/extended-reading/prompt-engineering/README' })}
target="_blank"
rel="noopener noreferrer"
className="text-[#155EEF]"
>
{t('appDebug.feature.conversationHistory.learnMore')}
{t('feature.conversationHistory.learnMore', { ns: 'appDebug' })}
</a>
</div>
</div>

View File

@ -162,7 +162,7 @@ const Prompt: FC<IPromptProps> = ({
className="mt-3 w-full"
>
<RiAddLine className="mr-2 h-4 w-4" />
<div>{t('appDebug.promptMode.operation.addMessage')}</div>
<div>{t('promptMode.operation.addMessage', { ns: 'appDebug' })}</div>
</Button>
)}
</div>

View File

@ -92,7 +92,7 @@ const Prompt: FC<ISimplePromptInput> = ({
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable) {
notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: promptVariables[i].key }) })
notify({ type: 'error', message: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }) })
return false
}
}
@ -178,12 +178,12 @@ const Prompt: FC<ISimplePromptInput> = ({
{!noTitle && (
<div className="flex h-11 items-center justify-between pl-3 pr-2.5">
<div className="flex items-center space-x-1">
<div className="h2 system-sm-semibold-uppercase text-text-secondary">{mode !== AppModeEnum.COMPLETION ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
<div className="h2 system-sm-semibold-uppercase text-text-secondary">{mode !== AppModeEnum.COMPLETION ? t('chatSubTitle', { ns: 'appDebug' }) : t('completionSubTitle', { ns: 'appDebug' })}</div>
{!readonly && (
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.promptTip')}
{t('promptTip', { ns: 'appDebug' })}
</div>
)}
/>

View File

@ -25,7 +25,7 @@ const Field: FC<Props> = ({
{isOptional && (
<span className="system-xs-regular ml-1 text-text-tertiary">
(
{t('appDebug.variableConfig.optional')}
{t('variableConfig.optional', { ns: 'appDebug' })}
)
</span>
)}

View File

@ -102,7 +102,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
if (!isValid) {
Toast.notify({
type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('appDebug.variableConfig.varName') }) as string,
message: t(`varKeyError.${errorMessageKey}`, { ns: 'appDebug', key: t('variableConfig.varName', { ns: 'appDebug' }) }),
})
return false
}
@ -144,40 +144,40 @@ const ConfigModal: FC<IConfigModalProps> = ({
const selectOptions: SelectItem[] = [
{
name: t('appDebug.variableConfig.text-input'),
name: t('variableConfig.text-input', { ns: 'appDebug' }),
value: InputVarType.textInput,
},
{
name: t('appDebug.variableConfig.paragraph'),
name: t('variableConfig.paragraph', { ns: 'appDebug' }),
value: InputVarType.paragraph,
},
{
name: t('appDebug.variableConfig.select'),
name: t('variableConfig.select', { ns: 'appDebug' }),
value: InputVarType.select,
},
{
name: t('appDebug.variableConfig.number'),
name: t('variableConfig.number', { ns: 'appDebug' }),
value: InputVarType.number,
},
{
name: t('appDebug.variableConfig.checkbox'),
name: t('variableConfig.checkbox', { ns: 'appDebug' }),
value: InputVarType.checkbox,
},
...(supportFile
? [
{
name: t('appDebug.variableConfig.single-file'),
name: t('variableConfig.single-file', { ns: 'appDebug' }),
value: InputVarType.singleFile,
},
{
name: t('appDebug.variableConfig.multi-files'),
name: t('variableConfig.multi-files', { ns: 'appDebug' }),
value: InputVarType.multiFiles,
},
]
: []),
...((!isBasicApp && isSupportJSON)
? [{
name: t('appDebug.variableConfig.json'),
name: t('variableConfig.json', { ns: 'appDebug' }),
value: InputVarType.jsonObject,
}]
: []),
@ -224,7 +224,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
if (!isValid) {
Toast.notify({
type: 'error',
message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }) as string,
message: t(`varKeyError.${errorMessageKey}`, { ns: 'appDebug', key: errorKey }),
})
return
}
@ -246,7 +246,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
return
if (!tempPayload.label) {
Toast.notify({ type: 'error', message: t('appDebug.variableConfig.errorMsg.labelNameRequired') })
Toast.notify({ type: 'error', message: t('variableConfig.errorMsg.labelNameRequired', { ns: 'appDebug' }) })
return
}
if (isStringInput || type === InputVarType.number) {
@ -254,7 +254,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
}
else if (type === InputVarType.select) {
if (options?.length === 0) {
Toast.notify({ type: 'error', message: t('appDebug.variableConfig.errorMsg.atLeastOneOption') })
Toast.notify({ type: 'error', message: t('variableConfig.errorMsg.atLeastOneOption', { ns: 'appDebug' }) })
return
}
const obj: Record<string, boolean> = {}
@ -267,19 +267,19 @@ const ConfigModal: FC<IConfigModalProps> = ({
obj[o] = true
})
if (hasRepeatedItem) {
Toast.notify({ type: 'error', message: t('appDebug.variableConfig.errorMsg.optionRepeat') })
Toast.notify({ type: 'error', message: t('variableConfig.errorMsg.optionRepeat', { ns: 'appDebug' }) })
return
}
onConfirm(tempPayload, moreInfo)
}
else if ([InputVarType.singleFile, InputVarType.multiFiles].includes(type)) {
if (tempPayload.allowed_file_types?.length === 0) {
const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConfig.file.supportFileTypes') })
const errorMessages = t('errorMsg.fieldRequired', { ns: 'workflow', field: t('variableConfig.file.supportFileTypes', { ns: 'appDebug' }) })
Toast.notify({ type: 'error', message: errorMessages })
return
}
if (tempPayload.allowed_file_types?.includes(SupportUploadFileTypes.custom) && !tempPayload.allowed_file_extensions?.length) {
const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConfig.file.custom.name') })
const errorMessages = t('errorMsg.fieldRequired', { ns: 'workflow', field: t('variableConfig.file.custom.name', { ns: 'appDebug' }) })
Toast.notify({ type: 'error', message: errorMessages })
return
}
@ -292,34 +292,34 @@ const ConfigModal: FC<IConfigModalProps> = ({
return (
<Modal
title={t(`appDebug.variableConfig.${isCreate ? 'addModalTitle' : 'editModalTitle'}`)}
title={t(`variableConfig.${isCreate ? 'addModalTitle' : 'editModalTitle'}`, { ns: 'appDebug' })}
isShow={isShow}
onClose={onClose}
>
<div className="mb-8" ref={modalRef} tabIndex={-1}>
<div className="space-y-2">
<Field title={t('appDebug.variableConfig.fieldType')}>
<Field title={t('variableConfig.fieldType', { ns: 'appDebug' })}>
<TypeSelector value={type} items={selectOptions} onSelect={handleTypeChange} />
</Field>
<Field title={t('appDebug.variableConfig.varName')}>
<Field title={t('variableConfig.varName', { ns: 'appDebug' })}>
<Input
value={variable}
onChange={handleVarNameChange}
onBlur={handleVarKeyBlur}
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
placeholder={t('variableConfig.inputPlaceholder', { ns: 'appDebug' })!}
/>
</Field>
<Field title={t('appDebug.variableConfig.labelName')}>
<Field title={t('variableConfig.labelName', { ns: 'appDebug' })}>
<Input
value={label as string}
onChange={e => handlePayloadChange('label')(e.target.value)}
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
placeholder={t('variableConfig.inputPlaceholder', { ns: 'appDebug' })!}
/>
</Field>
{isStringInput && (
<Field title={t('appDebug.variableConfig.maxLength')}>
<Field title={t('variableConfig.maxLength', { ns: 'appDebug' })}>
<ConfigString maxLength={type === InputVarType.textInput ? TEXT_MAX_LENGTH : Infinity} modelId={modelConfig.model_id} value={max_length} onChange={handlePayloadChange('max_length')} />
</Field>
@ -327,50 +327,50 @@ const ConfigModal: FC<IConfigModalProps> = ({
{/* Default value for text input */}
{type === InputVarType.textInput && (
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<Input
value={tempPayload.default || ''}
onChange={e => handlePayloadChange('default')(e.target.value || undefined)}
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
placeholder={t('variableConfig.inputPlaceholder', { ns: 'appDebug' })!}
/>
</Field>
)}
{/* Default value for paragraph */}
{type === InputVarType.paragraph && (
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<Textarea
value={String(tempPayload.default ?? '')}
onChange={e => handlePayloadChange('default')(e.target.value || undefined)}
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
placeholder={t('variableConfig.inputPlaceholder', { ns: 'appDebug' })!}
/>
</Field>
)}
{/* Default value for number input */}
{type === InputVarType.number && (
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<Input
type="number"
value={tempPayload.default || ''}
onChange={e => handlePayloadChange('default')(e.target.value || undefined)}
placeholder={t('appDebug.variableConfig.inputPlaceholder')!}
placeholder={t('variableConfig.inputPlaceholder', { ns: 'appDebug' })!}
/>
</Field>
)}
{type === InputVarType.checkbox && (
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<SimpleSelect
className="w-full"
optionWrapClassName="max-h-[140px] overflow-y-auto"
items={[
{ value: CHECKBOX_DEFAULT_TRUE_VALUE, name: t('appDebug.variableConfig.startChecked') },
{ value: CHECKBOX_DEFAULT_FALSE_VALUE, name: t('appDebug.variableConfig.noDefaultSelected') },
{ value: CHECKBOX_DEFAULT_TRUE_VALUE, name: t('variableConfig.startChecked', { ns: 'appDebug' }) },
{ value: CHECKBOX_DEFAULT_FALSE_VALUE, name: t('variableConfig.noDefaultSelected', { ns: 'appDebug' }) },
]}
defaultValue={checkboxDefaultSelectValue}
onSelect={item => handlePayloadChange('default')(parseCheckboxSelectValue(String(item.value)))}
placeholder={t('appDebug.variableConfig.selectDefaultValue')}
placeholder={t('variableConfig.selectDefaultValue', { ns: 'appDebug' })}
allowSearch={false}
/>
</Field>
@ -378,17 +378,17 @@ const ConfigModal: FC<IConfigModalProps> = ({
{type === InputVarType.select && (
<>
<Field title={t('appDebug.variableConfig.options')}>
<Field title={t('variableConfig.options', { ns: 'appDebug' })}>
<ConfigSelect options={options || []} onChange={handlePayloadChange('options')} />
</Field>
{options && options.length > 0 && (
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<SimpleSelect
key={`default-select-${options.join('-')}`}
className="w-full"
optionWrapClassName="max-h-[140px] overflow-y-auto"
items={[
{ value: '', name: t('appDebug.variableConfig.noDefaultValue') },
{ value: '', name: t('variableConfig.noDefaultValue', { ns: 'appDebug' }) },
...options.filter(opt => opt.trim() !== '').map(option => ({
value: option,
name: option,
@ -396,7 +396,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
]}
defaultValue={tempPayload.default || ''}
onSelect={item => handlePayloadChange('default')(item.value === '' ? undefined : item.value)}
placeholder={t('appDebug.variableConfig.selectDefaultValue')}
placeholder={t('variableConfig.selectDefaultValue', { ns: 'appDebug' })}
allowSearch={false}
/>
</Field>
@ -411,7 +411,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
onChange={(p: UploadFileSetting) => setTempPayload(p as InputVar)}
isMultiple={type === InputVarType.multiFiles}
/>
<Field title={t('appDebug.variableConfig.defaultValue')}>
<Field title={t('variableConfig.defaultValue', { ns: 'appDebug' })}>
<FileUploaderInAttachmentWrapper
value={(type === InputVarType.singleFile ? (tempPayload.default ? [tempPayload.default] : []) : (tempPayload.default || [])) as unknown as FileEntity[]}
onChange={(files) => {
@ -432,7 +432,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
)}
{type === InputVarType.jsonObject && (
<Field title={t('appDebug.variableConfig.jsonSchema')} isOptional>
<Field title={t('variableConfig.jsonSchema', { ns: 'appDebug' })} isOptional>
<CodeEditor
language={CodeLanguage.json}
value={jsonSchemaStr}
@ -448,12 +448,12 @@ const ConfigModal: FC<IConfigModalProps> = ({
<div className="!mt-5 flex h-6 items-center space-x-2">
<Checkbox checked={tempPayload.required} disabled={tempPayload.hide} onCheck={() => handlePayloadChange('required')(!tempPayload.required)} />
<span className="system-sm-semibold text-text-secondary">{t('appDebug.variableConfig.required')}</span>
<span className="system-sm-semibold text-text-secondary">{t('variableConfig.required', { ns: 'appDebug' })}</span>
</div>
<div className="!mt-5 flex h-6 items-center space-x-2">
<Checkbox checked={tempPayload.hide} disabled={tempPayload.required} onCheck={() => handlePayloadChange('hide')(!tempPayload.hide)} />
<span className="system-sm-semibold text-text-secondary">{t('appDebug.variableConfig.hide')}</span>
<span className="system-sm-semibold text-text-secondary">{t('variableConfig.hide', { ns: 'appDebug' })}</span>
</div>
</div>
</div>

View File

@ -90,7 +90,7 @@ const ConfigSelect: FC<IConfigSelectProps> = ({
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>
)

View File

@ -84,21 +84,21 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
})
const newList = newPromptVariables
let errorMsgKey = ''
let typeName = ''
let errorMsgKey: 'varKeyError.keyAlreadyExists' | '' = ''
let typeName: 'variableConfig.varName' | 'variableConfig.labelName' | '' = ''
if (hasDuplicateStr(newList.map(item => item.key))) {
errorMsgKey = 'appDebug.varKeyError.keyAlreadyExists'
typeName = 'appDebug.variableConfig.varName'
errorMsgKey = 'varKeyError.keyAlreadyExists'
typeName = 'variableConfig.varName'
}
else if (hasDuplicateStr(newList.map(item => item.name as string))) {
errorMsgKey = 'appDebug.varKeyError.keyAlreadyExists'
typeName = 'appDebug.variableConfig.labelName'
errorMsgKey = 'varKeyError.keyAlreadyExists'
typeName = 'variableConfig.labelName'
}
if (errorMsgKey) {
if (errorMsgKey && typeName) {
Toast.notify({
type: 'error',
message: t(errorMsgKey as any, { key: t(typeName as any) as string }) as string,
message: t(errorMsgKey, { ns: 'appDebug', key: t(typeName, { ns: 'appDebug' }) }),
})
return false
}
@ -149,7 +149,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable && i !== index) {
Toast.notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { key: promptVariables[i].key }) })
Toast.notify({ type: 'error', message: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }) })
return false
}
}
@ -238,12 +238,12 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
className="mt-2"
title={(
<div className="flex items-center">
<div className="mr-1">{t('appDebug.variableTitle')}</div>
<div className="mr-1">{t('variableTitle', { ns: 'appDebug' })}</div>
{!readonly && (
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.variableTip')}
{t('variableTip', { ns: 'appDebug' })}
</div>
)}
/>
@ -255,7 +255,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
>
{!hasVar && (
<div className="mt-1 px-3 pb-3">
<div className="pb-1 pt-2 text-xs text-text-tertiary">{t('appDebug.notSetVar')}</div>
<div className="pb-1 pt-2 text-xs text-text-tertiary">{t('notSetVar', { ns: 'appDebug' })}</div>
</div>
)}
{hasVar && (
@ -307,8 +307,8 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
{isShowDeleteContextVarModal && (
<Confirm
isShow={isShowDeleteContextVarModal}
title={t('appDebug.feature.dataSet.queryVariable.deleteContextVarTitle', { varName: promptVariables[removeIndex as number]?.name })}
content={t('appDebug.feature.dataSet.queryVariable.deleteContextVarTip')}
title={t('feature.dataSet.queryVariable.deleteContextVarTitle', { ns: 'appDebug', varName: promptVariables[removeIndex as number]?.name })}
content={t('feature.dataSet.queryVariable.deleteContextVarTip', { ns: 'appDebug' })}
onConfirm={() => {
didRemoveVar(removeIndex as number)
hideDeleteContextVarModal()

View File

@ -16,8 +16,8 @@ const ModalFoot: FC<IModalFootProps> = ({
const { t } = useTranslation()
return (
<div className="flex justify-end gap-2">
<Button onClick={onCancel}>{t('common.operation.cancel')}</Button>
<Button variant="primary" onClick={onConfirm}>{t('common.operation.save')}</Button>
<Button onClick={onCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button variant="primary" onClick={onConfirm}>{t('operation.save', { ns: 'common' })}</Button>
</div>
)
}

View File

@ -1,6 +1,7 @@
'use client'
import type { FC } from 'react'
import type { InputVarType } from '@/app/components/workflow/types'
import type { I18nKeysByPrefix } from '@/types/i18n'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon'
@ -12,7 +13,9 @@ export type ISelectTypeItemProps = {
onClick: () => void
}
const i18nFileTypeMap: Record<string, string> = {
type VariableConfigTypeKey = I18nKeysByPrefix<'appDebug', 'variableConfig.'>
const i18nTypeMap: Partial<Record<InputVarType, VariableConfigTypeKey>> = {
'file': 'single-file',
'file-list': 'multi-files',
}
@ -23,7 +26,8 @@ const SelectTypeItem: FC<ISelectTypeItemProps> = ({
onClick,
}) => {
const { t } = useTranslation()
const typeName = t(`appDebug.variableConfig.${i18nFileTypeMap[type] || type}` as any) as string
const typeKey = i18nTypeMap[type] ?? type as VariableConfigTypeKey
const typeName = t(`variableConfig.${typeKey}`, { ns: 'appDebug' })
return (
<div

View File

@ -62,15 +62,15 @@ const SelectVarType: FC<Props> = ({
<PortalToFollowElemContent style={{ zIndex: 1000 }}>
<div className="min-w-[192px] rounded-lg border border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm">
<div className="p-1">
<SelectItem type={InputVarType.textInput} value="string" text={t('appDebug.variableConfig.string')} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.paragraph} value="paragraph" text={t('appDebug.variableConfig.paragraph')} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.select} value="select" text={t('appDebug.variableConfig.select')} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.number} value="number" text={t('appDebug.variableConfig.number')} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.checkbox} value="checkbox" text={t('appDebug.variableConfig.checkbox')} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.textInput} value="string" text={t('variableConfig.string', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.paragraph} value="paragraph" text={t('variableConfig.paragraph', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.select} value="select" text={t('variableConfig.select', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.number} value="number" text={t('variableConfig.number', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
<SelectItem type={InputVarType.checkbox} value="checkbox" text={t('variableConfig.checkbox', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
</div>
<div className="h-px border-t border-components-panel-border"></div>
<div className="p-1">
<SelectItem Icon={ApiConnection} value="api" text={t('appDebug.variableConfig.apiBasedVar')} onClick={handleChange}></SelectItem>
<SelectItem Icon={ApiConnection} value="api" text={t('variableConfig.apiBasedVar', { ns: 'appDebug' })} onClick={handleChange}></SelectItem>
</div>
</div>
</PortalToFollowElemContent>

View File

@ -65,11 +65,11 @@ const ConfigVision: FC = () => {
</div>
</div>
<div className="flex grow items-center">
<div className="system-sm-semibold mr-1 text-text-secondary">{t('appDebug.vision.name')}</div>
<div className="system-sm-semibold mr-1 text-text-secondary">{t('vision.name', { ns: 'appDebug' })}</div>
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.vision.description')}
{t('vision.description', { ns: 'appDebug' })}
</div>
)}
/>

View File

@ -42,15 +42,15 @@ const ParamConfigContent: FC = () => {
return (
<div>
<div className="text-base font-semibold leading-6 text-text-primary">{t('appDebug.vision.visionSettings.title')}</div>
<div className="text-base font-semibold leading-6 text-text-primary">{t('vision.visionSettings.title', { ns: 'appDebug' })}</div>
<div className="space-y-6 pt-3">
<div>
<div className="mb-2 flex items-center space-x-1">
<div className="text-[13px] font-semibold leading-[18px] text-text-secondary">{t('appDebug.vision.visionSettings.resolution')}</div>
<div className="text-[13px] font-semibold leading-[18px] text-text-secondary">{t('vision.visionSettings.resolution', { ns: 'appDebug' })}</div>
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => (
{t('vision.visionSettings.resolutionTooltip', { ns: 'appDebug' }).split('\n').map(item => (
<div key={item}>{item}</div>
))}
</div>
@ -60,7 +60,7 @@ const ParamConfigContent: FC = () => {
<div className="flex items-center gap-1">
<OptionCard
className="grow"
title={t('appDebug.vision.visionSettings.high')}
title={t('vision.visionSettings.high', { ns: 'appDebug' })}
selected={file?.image?.detail === Resolution.high}
onSelect={() => handleChange({
...file,
@ -69,7 +69,7 @@ const ParamConfigContent: FC = () => {
/>
<OptionCard
className="grow"
title={t('appDebug.vision.visionSettings.low')}
title={t('vision.visionSettings.low', { ns: 'appDebug' })}
selected={file?.image?.detail === Resolution.low}
onSelect={() => handleChange({
...file,
@ -79,11 +79,11 @@ const ParamConfigContent: FC = () => {
</div>
</div>
<div>
<div className="mb-2 text-[13px] font-semibold leading-[18px] text-text-secondary">{t('appDebug.vision.visionSettings.uploadMethod')}</div>
<div className="mb-2 text-[13px] font-semibold leading-[18px] text-text-secondary">{t('vision.visionSettings.uploadMethod', { ns: 'appDebug' })}</div>
<div className="flex items-center gap-1">
<OptionCard
className="grow"
title={t('appDebug.vision.visionSettings.both')}
title={t('vision.visionSettings.both', { ns: 'appDebug' })}
selected={!!file?.allowed_file_upload_methods?.includes(TransferMethod.local_file) && !!file?.allowed_file_upload_methods?.includes(TransferMethod.remote_url)}
onSelect={() => handleChange({
...file,
@ -92,7 +92,7 @@ const ParamConfigContent: FC = () => {
/>
<OptionCard
className="grow"
title={t('appDebug.vision.visionSettings.localUpload')}
title={t('vision.visionSettings.localUpload', { ns: 'appDebug' })}
selected={!!file?.allowed_file_upload_methods?.includes(TransferMethod.local_file) && file?.allowed_file_upload_methods?.length === 1}
onSelect={() => handleChange({
...file,
@ -101,7 +101,7 @@ const ParamConfigContent: FC = () => {
/>
<OptionCard
className="grow"
title={t('appDebug.vision.visionSettings.url')}
title={t('vision.visionSettings.url', { ns: 'appDebug' })}
selected={!!file?.allowed_file_upload_methods?.includes(TransferMethod.remote_url) && file?.allowed_file_upload_methods?.length === 1}
onSelect={() => handleChange({
...file,
@ -114,7 +114,7 @@ const ParamConfigContent: FC = () => {
<ParamItem
id="upload_limit"
className=""
name={t('appDebug.vision.visionSettings.uploadLimit')}
name={t('vision.visionSettings.uploadLimit', { ns: 'appDebug' })}
noTooltip
{...{
default: 2,

View File

@ -28,7 +28,7 @@ const ParamsConfig: FC = () => {
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<Button variant="ghost" size="small" className={cn('')}>
<RiSettings2Line className="h-3.5 w-3.5" />
<div className="ml-1">{t('appDebug.voice.settings')}</div>
<div className="ml-1">{t('voice.settings', { ns: 'appDebug' })}</div>
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 50 }}>

View File

@ -7,7 +7,10 @@ import AgentSettingButton from './agent-setting-button'
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
t: (key: string, options?: { ns?: string }) => {
const prefix = options?.ns ? `${options.ns}.` : ''
return `${prefix}${key}`
},
}),
}))

View File

@ -28,7 +28,7 @@ const AgentSettingButton: FC<Props> = ({
<>
<Button onClick={() => setIsShowAgentSetting(true)} className="mr-2 shrink-0">
<RiSettings2Line className="mr-1 h-4 w-4 text-text-tertiary" />
{t('appDebug.agent.setting.name')}
{t('agent.setting.name', { ns: 'appDebug' })}
</Button>
{isShowAgentSetting && (
<AgentSetting

View File

@ -61,7 +61,7 @@ const AgentSetting: FC<Props> = ({
>
<div className="flex h-14 shrink-0 items-center justify-between border-b border-divider-regular pl-6 pr-5">
<div className="flex flex-col text-base font-semibold text-text-primary">
<div className="leading-6">{t('appDebug.agent.setting.name')}</div>
<div className="leading-6">{t('agent.setting.name', { ns: 'appDebug' })}</div>
</div>
<div className="flex items-center">
<div
@ -85,10 +85,10 @@ const AgentSetting: FC<Props> = ({
icon={
<CuteRobot className="h-4 w-4 text-indigo-600" />
}
name={t('appDebug.agent.agentMode')}
description={t('appDebug.agent.agentModeDes')}
name={t('agent.agentMode', { ns: 'appDebug' })}
description={t('agent.agentModeDes', { ns: 'appDebug' })}
>
<div className="text-[13px] font-medium leading-[18px] text-text-primary">{isFunctionCall ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</div>
<div className="text-[13px] font-medium leading-[18px] text-text-primary">{isFunctionCall ? t('agent.agentModeType.functionCall', { ns: 'appDebug' }) : t('agent.agentModeType.ReACT', { ns: 'appDebug' })}</div>
</ItemPanel>
<ItemPanel
@ -96,8 +96,8 @@ const AgentSetting: FC<Props> = ({
icon={
<Unblur className="h-4 w-4 text-[#FB6514]" />
}
name={t('appDebug.agent.setting.maximumIterations.name')}
description={t('appDebug.agent.setting.maximumIterations.description')}
name={t('agent.setting.maximumIterations.name', { ns: 'appDebug' })}
description={t('agent.setting.maximumIterations.description', { ns: 'appDebug' })}
>
<div className="flex items-center">
<Slider
@ -138,7 +138,7 @@ const AgentSetting: FC<Props> = ({
{!isFunctionCall && (
<div className="rounded-xl bg-background-section-burn py-2 shadow-xs">
<div className="flex h-8 items-center px-4 text-sm font-semibold leading-6 text-text-secondary">{t('tools.builtInPromptTitle')}</div>
<div className="flex h-8 items-center px-4 text-sm font-semibold leading-6 text-text-secondary">{t('builtInPromptTitle', { ns: 'tools' })}</div>
<div className="h-[396px] overflow-y-auto whitespace-pre-line px-4 text-sm font-normal leading-5 text-text-secondary">
{isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion}
</div>
@ -156,13 +156,13 @@ const AgentSetting: FC<Props> = ({
onClick={onCancel}
className="mr-2"
>
{t('common.operation.cancel')}
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
variant="primary"
onClick={handleSave}
>
{t('common.operation.save')}
{t('operation.save', { ns: 'common' })}
</Button>
</div>
</div>

View File

@ -152,11 +152,11 @@ const AgentTools: FC = () => {
noBodySpacing={tools.length === 0}
title={(
<div className="flex items-center">
<div className="mr-1">{t('appDebug.agent.tools.name')}</div>
<div className="mr-1">{t('agent.tools.name', { ns: 'appDebug' })}</div>
<Tooltip
popupContent={(
<div className="w-[180px]">
{t('appDebug.agent.tools.description')}
{t('agent.tools.description', { ns: 'appDebug' })}
</div>
)}
/>
@ -169,7 +169,7 @@ const AgentTools: FC = () => {
/
{tools.length}
&nbsp;
{t('appDebug.agent.tools.enabled')}
{t('agent.tools.enabled', { ns: 'appDebug' })}
</div>
{tools.length < MAX_TOOLS_NUM && (
<>
@ -220,8 +220,8 @@ const AgentTools: FC = () => {
popupContent={(
<div className="w-[180px]">
<div className="mb-1.5 text-text-secondary">{item.tool_name}</div>
<div className="mb-1.5 text-text-tertiary">{t('tools.toolNameUsageTip')}</div>
<div className="cursor-pointer text-text-accent" onClick={() => copy(item.tool_name)}>{t('tools.copyToolName')}</div>
<div className="mb-1.5 text-text-tertiary">{t('toolNameUsageTip', { ns: 'tools' })}</div>
<div className="cursor-pointer text-text-accent" onClick={() => copy(item.tool_name)}>{t('copyToolName', { ns: 'tools' })}</div>
</div>
)}
>
@ -238,7 +238,7 @@ const AgentTools: FC = () => {
{item.isDeleted && (
<div className="mr-2 flex items-center">
<Tooltip
popupContent={t('tools.toolRemoved')}
popupContent={t('toolRemoved', { ns: 'tools' })}
>
<div className="mr-1 cursor-pointer rounded-md p-1 hover:bg-black/5">
<AlertTriangle className="h-4 w-4 text-[#F79009]" />
@ -264,7 +264,7 @@ const AgentTools: FC = () => {
<div className="mr-2 hidden items-center gap-1 group-hover:flex">
{!item.notAuthor && (
<Tooltip
popupContent={t('tools.setBuiltInTools.infoAndSetting')}
popupContent={t('setBuiltInTools.infoAndSetting', { ns: 'tools' })}
needsDelay={false}
>
<div
@ -319,7 +319,7 @@ const AgentTools: FC = () => {
setIsShowSettingTool(true)
}}
>
{t('tools.notAuthorized')}
{t('notAuthorized', { ns: 'tools' })}
<Indicator className="ml-2" color="orange" />
</Button>
)}

Some files were not shown because too many files have changed in this diff Show More