mirror of https://github.com/langgenius/dify.git
type safe
This commit is contained in:
parent
caf3e58f61
commit
d9a439d774
|
|
@ -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'
|
||||
|
|
@ -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(`actionMsg.${message}` as any, { ns: 'common' }) as string,
|
||||
message: t(`actionMsg.${message}`, { ns: 'common' }) as string,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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' },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -53,7 +56,7 @@ const LongTimeRangePicker: FC<Props> = ({
|
|||
|
||||
return (
|
||||
<SimpleSelect
|
||||
items={Object.entries(periodMapping).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}` as any, { ns: 'appLog' }) 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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +69,7 @@ const RangeSelector: FC<Props> = ({
|
|||
}, [])
|
||||
return (
|
||||
<SimpleSelect
|
||||
items={ranges.map(v => ({ ...v, name: t(`filter.period.${v.name}` as any, { ns: 'appLog' }) as string }))}
|
||||
items={ranges.map(v => ({ ...v, name: t(`filter.period.${v.name}`, { ns: 'appLog' }) }))}
|
||||
className="mt-0 !w-40"
|
||||
notClearable={true}
|
||||
onSelect={handleSelectRange}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ const DatasetInfo: FC<DatasetInfoProps> = ({
|
|||
{isExternalProvider && t('externalTag', { ns: 'dataset' })}
|
||||
{!isExternalProvider && isPipelinePublished && dataset.doc_form && dataset.indexing_technique && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span>{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any, { ns: 'dataset' }) 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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const DatasetSidebarDropdown = ({
|
|||
{isExternalProvider && t('externalTag', { ns: 'dataset' })}
|
||||
{!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span>{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any, { ns: 'dataset' }) 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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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(`accessControlDialog.accessItems.${label}` as any, { ns: 'app' }) as string}</span>
|
||||
<span className="system-sm-medium text-text-secondary">{t(`accessControlDialog.accessItems.${label}`, { ns: 'app' })}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
if (!isValid) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t(`varKeyError.${errorMessageKey}` as any, { ns: 'appDebug', key: t('variableConfig.varName', { ns: 'appDebug' }) }) as string,
|
||||
message: t(`varKeyError.${errorMessageKey}`, { ns: 'appDebug', key: t('variableConfig.varName', { ns: 'appDebug' }) }),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
if (!isValid) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t(`varKeyError.${errorMessageKey}` as any, { ns: 'appDebug', key: errorKey }) as string,
|
||||
message: t(`varKeyError.${errorMessageKey}`, { ns: 'appDebug', key: errorKey }),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(`variableConfig.${i18nFileTypeMap[type] || type}` as any, { ns: 'appDebug' }) as string
|
||||
const typeKey = i18nTypeMap[type] ?? type as VariableConfigTypeKey
|
||||
const typeName = t(`variableConfig.${typeKey}`, { ns: 'appDebug' })
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import { GeneratorType } from './types'
|
|||
import useGenData from './use-gen-data'
|
||||
|
||||
const i18nPrefix = 'generate'
|
||||
|
||||
export type IGetAutomaticResProps = {
|
||||
mode: AppModeEnum
|
||||
isShow: boolean
|
||||
|
|
@ -131,17 +132,19 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||
icon: RiGitCommitLine,
|
||||
key: 'GitGud',
|
||||
},
|
||||
]
|
||||
] as const
|
||||
|
||||
// eslint-disable-next-line sonarjs/no-nested-template-literals, sonarjs/no-nested-conditional
|
||||
const [instructionFromSessionStorage, setInstruction] = useSessionStorageState<string>(`improve-instruction-${flowId}${isBasicMode ? '' : `-${nodeId}${editorId ? `-${editorId}` : ''}`}`)
|
||||
const instruction = instructionFromSessionStorage || ''
|
||||
const [ideaOutput, setIdeaOutput] = useState<string>('')
|
||||
|
||||
type TemplateKey = typeof tryList[number]['key']
|
||||
|
||||
const [editorKey, setEditorKey] = useState(`${flowId}-0`)
|
||||
const handleChooseTemplate = useCallback((key: string) => {
|
||||
const handleChooseTemplate = useCallback((key: TemplateKey) => {
|
||||
return () => {
|
||||
const template = t(`generate.template.${key}.instruction` as any, { ns: 'appDebug' }) as string
|
||||
const template = t(`generate.template.${key}.instruction` as const, { ns: 'appDebug' })
|
||||
setInstruction(template)
|
||||
setEditorKey(`${flowId}-${Date.now()}`)
|
||||
}
|
||||
|
|
@ -323,7 +326,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||
<TryLabel
|
||||
key={item.key}
|
||||
Icon={item.icon}
|
||||
text={t(`generate.template.${item.key}.name` as any, { ns: 'appDebug' }) as string}
|
||||
text={t(`generate.template.${item.key}.name`, { ns: 'appDebug' })}
|
||||
onClick={handleChooseTemplate(item.key)}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { QueryParam } from './index'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import { RiCalendarLine } from '@remixicon/react'
|
||||
import dayjs from 'dayjs'
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
||||
|
|
@ -15,7 +16,9 @@ dayjs.extend(quarterOfYear)
|
|||
|
||||
const today = dayjs()
|
||||
|
||||
export const TIME_PERIOD_MAPPING: { [key: string]: { value: number, name: string } } = {
|
||||
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
|
||||
|
||||
export const TIME_PERIOD_MAPPING: { [key: string]: { value: number, name: TimePeriodName } } = {
|
||||
1: { value: 0, name: 'today' },
|
||||
2: { value: 7, name: 'last7days' },
|
||||
3: { value: 28, name: 'last4weeks' },
|
||||
|
|
@ -50,7 +53,7 @@ const Filter: FC<IFilterProps> = ({ isChatMode, appId, queryParams, setQueryPara
|
|||
setQueryParams({ ...queryParams, period: item.value })
|
||||
}}
|
||||
onClear={() => setQueryParams({ ...queryParams, period: '9' })}
|
||||
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}` as any, { ns: 'appLog' }) as string }))}
|
||||
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}`, { ns: 'appLog' }) }))}
|
||||
/>
|
||||
<Chip
|
||||
className="min-w-[150px]"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import type { AppDetailResponse } from '@/models/app'
|
||||
import type { AppTrigger } from '@/service/use-tools'
|
||||
import type { AppSSO } from '@/types/app'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import Link from 'next/link'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -23,7 +24,7 @@ import { canFindTool } from '@/utils'
|
|||
|
||||
export type ITriggerCardProps = {
|
||||
appInfo: AppDetailResponse & Partial<AppSSO>
|
||||
onToggleResult?: (err: Error | null, message?: string) => void
|
||||
onToggleResult?: (err: Error | null, message?: I18nKeysByPrefix<'common', 'actionMsg.'>) => void
|
||||
}
|
||||
|
||||
const getTriggerIcon = (trigger: AppTrigger, triggerPlugins: any[]) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { QueryParam } from './index'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import { RiCalendarLine } from '@remixicon/react'
|
||||
import dayjs from 'dayjs'
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
||||
|
|
@ -14,7 +15,9 @@ dayjs.extend(quarterOfYear)
|
|||
|
||||
const today = dayjs()
|
||||
|
||||
export const TIME_PERIOD_MAPPING: { [key: string]: { value: number, name: string } } = {
|
||||
type TimePeriodName = I18nKeysByPrefix<'appLog', 'filter.period.'>
|
||||
|
||||
export const TIME_PERIOD_MAPPING: { [key: string]: { value: number, name: TimePeriodName } } = {
|
||||
1: { value: 0, name: 'today' },
|
||||
2: { value: 7, name: 'last7days' },
|
||||
3: { value: 28, name: 'last4weeks' },
|
||||
|
|
@ -55,7 +58,7 @@ const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps)
|
|||
setQueryParams({ ...queryParams, period: item.value })
|
||||
}}
|
||||
onClear={() => setQueryParams({ ...queryParams, period: '9' })}
|
||||
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}` as any, { ns: 'appLog' }) as string }))}
|
||||
items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`filter.period.${v.name}`, { ns: 'appLog' }) }))}
|
||||
/>
|
||||
<Input
|
||||
wrapperClassName="w-[200px]"
|
||||
|
|
|
|||
|
|
@ -90,11 +90,11 @@ const BlockInput: FC<IBlockInputProps> = ({
|
|||
const handleSubmit = (value: string) => {
|
||||
if (onConfirm) {
|
||||
const keys = getInputKeys(value)
|
||||
const { isValid, errorKey, errorMessageKey } = checkKeys(keys)
|
||||
if (!isValid) {
|
||||
const result = checkKeys(keys)
|
||||
if (!result.isValid) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t(`varKeyError.${errorMessageKey}` as any, { ns: 'appDebug', key: errorKey }) as string,
|
||||
message: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,31 +4,31 @@ import dayjs from './utils/dayjs'
|
|||
|
||||
const YEAR_RANGE = 100
|
||||
|
||||
const daysInWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const
|
||||
|
||||
export const useDaysOfWeek = () => {
|
||||
const { t } = useTranslation()
|
||||
const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => t(`daysInWeek.${day}` as any, { ns: 'time' }) as string)
|
||||
|
||||
return daysOfWeek
|
||||
return daysInWeek.map(day => t(`daysInWeek.${day}`, { ns: 'time' }))
|
||||
}
|
||||
|
||||
const monthNames = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
] as const
|
||||
|
||||
export const useMonths = () => {
|
||||
const { t } = useTranslation()
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
].map(month => t(`months.${month}` as any, { ns: 'time' }) as string)
|
||||
|
||||
return months
|
||||
return monthNames.map(month => t(`months.${month}`, { ns: 'time' }))
|
||||
}
|
||||
|
||||
export const useYearOptions = () => {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,28 @@
|
|||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { RiLock2Fill } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type EncryptedKey = I18nKeysWithPrefix<'common', 'provider.encrypted.'>
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
frontTextKey?: string
|
||||
backTextKey?: string
|
||||
frontTextKey?: EncryptedKey
|
||||
backTextKey?: EncryptedKey
|
||||
}
|
||||
|
||||
const DEFAULT_FRONT_KEY: EncryptedKey = 'provider.encrypted.front'
|
||||
const DEFAULT_BACK_KEY: EncryptedKey = 'provider.encrypted.back'
|
||||
|
||||
export const EncryptedBottom = (props: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { frontTextKey, backTextKey, className } = props
|
||||
const { frontTextKey = DEFAULT_FRONT_KEY, backTextKey = DEFAULT_BACK_KEY, className } = props
|
||||
|
||||
return (
|
||||
<div className={cn('system-xs-regular flex items-center justify-center rounded-b-2xl border-t-[0.5px] border-divider-subtle bg-background-soft px-2 py-3 text-text-tertiary', className)}>
|
||||
<RiLock2Fill className="mx-1 h-3 w-3 text-text-quaternary" />
|
||||
{t((frontTextKey || 'provider.encrypted.front') as any, { ns: 'common' })}
|
||||
{t(frontTextKey, { ns: 'common' })}
|
||||
<Link
|
||||
className="mx-1 text-text-accent"
|
||||
target="_blank"
|
||||
|
|
@ -25,7 +31,7 @@ export const EncryptedBottom = (props: Props) => {
|
|||
>
|
||||
PKCS1_OAEP
|
||||
</Link>
|
||||
{t((backTextKey || 'provider.encrypted.back') as any, { ns: 'common' })}
|
||||
{t(backTextKey, { ns: 'common' })}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react'
|
||||
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
|
|
@ -18,6 +19,8 @@ import { useAppVoices } from '@/service/use-apps'
|
|||
import { TtsAutoPlay } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type VoiceLanguageKey = I18nKeysWithPrefix<'common', 'voice.language.'>
|
||||
|
||||
type VoiceParamConfigProps = {
|
||||
onClose: () => void
|
||||
onChange?: OnFeaturesChange
|
||||
|
|
@ -97,7 +100,7 @@ const VoiceParamConfig = ({
|
|||
className="h-full w-full cursor-pointer rounded-lg border-0 bg-components-input-bg-normal py-1.5 pl-3 pr-10 focus-visible:bg-state-base-hover focus-visible:outline-none group-hover:bg-state-base-hover sm:text-sm sm:leading-6"
|
||||
>
|
||||
<span className={cn('block truncate text-left text-text-secondary', !languageItem?.name && 'text-text-tertiary')}>
|
||||
{languageItem?.name ? t(`voice.language.${languageItem?.value.replace('-', '')}` as any, { ns: 'common' }) as string : localLanguagePlaceholder}
|
||||
{languageItem?.name ? t(`voice.language.${languageItem?.value.replace('-', '')}` as VoiceLanguageKey, { ns: 'common' }) : localLanguagePlaceholder}
|
||||
</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronDownIcon
|
||||
|
|
@ -128,7 +131,7 @@ const VoiceParamConfig = ({
|
|||
<span
|
||||
className={cn('block', selected && 'font-normal')}
|
||||
>
|
||||
{t(`voice.language.${(item.value).toString().replace('-', '')}` as any, { ns: 'common' }) as string}
|
||||
{t(`voice.language.${(item.value).toString().replace('-', '')}` as VoiceLanguageKey, { ns: 'common' })}
|
||||
</span>
|
||||
{(selected || item.value === text2speech?.language) && (
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { InputType } from './types'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import {
|
||||
RiAlignLeft,
|
||||
RiCheckboxLine,
|
||||
|
|
@ -11,7 +13,9 @@ import { useTranslation } from 'react-i18next'
|
|||
import { PipelineInputVarType } from '@/models/pipeline'
|
||||
import { InputTypeEnum } from './types'
|
||||
|
||||
const i18nFileTypeMap: Record<string, string> = {
|
||||
type VariableConfigKeySuffix = I18nKeysByPrefix<'appDebug', 'variableConfig.'>
|
||||
|
||||
const i18nFileTypeMap: Partial<Record<InputType, VariableConfigKeySuffix>> = {
|
||||
'number': 'number',
|
||||
'file': 'single-file',
|
||||
'file-list': 'multi-files',
|
||||
|
|
@ -44,7 +48,7 @@ export const useInputTypeOptions = (supportFile: boolean) => {
|
|||
return options.map((value) => {
|
||||
return {
|
||||
value,
|
||||
label: t(`variableConfig.${i18nFileTypeMap[value] || value}` as any, { ns: 'appDebug' }),
|
||||
label: t(`variableConfig.${i18nFileTypeMap[value] || value}` as `variableConfig.${VariableConfigKeySuffix}`, { ns: 'appDebug' }),
|
||||
Icon: INPUT_TYPE_ICON[value],
|
||||
type: DATA_TYPE[value],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ const TagInput: FC<TagInputProps> = ({
|
|||
setValue(e.target.value)
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={t((placeholder || (isSpecialMode ? 'model.params.stop_sequencesPlaceholder' : 'segment.addKeyWord')) as any, { ns: isSpecialMode ? 'common' : 'datasetDocuments' })}
|
||||
placeholder={placeholder || (isSpecialMode ? t('model.params.stop_sequencesPlaceholder', { ns: 'common' }) : t('segment.addKeyWord', { ns: 'datasetDocuments' }))}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const i18nPrefix = `plans.${plan}`
|
||||
const i18nPrefix = `plans.${plan}` as const
|
||||
const isFreePlan = plan === Plan.sandbox
|
||||
const isMostPopularPlan = plan === Plan.professional
|
||||
const planInfo = ALL_PLANS[plan]
|
||||
|
|
@ -106,7 +106,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
|
|||
{ICON_MAP[plan]}
|
||||
<div className="flex min-h-[104px] flex-col gap-y-2">
|
||||
<div className="flex items-center gap-x-2.5">
|
||||
<div className="text-[30px] font-medium leading-[1.2] text-text-primary">{t(`${i18nPrefix}.name` as any, { ns: 'billing' }) as string}</div>
|
||||
<div className="text-[30px] font-medium leading-[1.2] text-text-primary">{t(`${i18nPrefix}.name`, { ns: 'billing' })}</div>
|
||||
{
|
||||
isMostPopularPlan && (
|
||||
<div className="flex items-center justify-center bg-saas-dify-blue-static px-1.5 py-1">
|
||||
|
|
@ -117,7 +117,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
|
|||
)
|
||||
}
|
||||
</div>
|
||||
<div className="system-sm-regular text-text-secondary">{t(`${i18nPrefix}.description` as any, { ns: 'billing' }) as string}</div>
|
||||
<div className="system-sm-regular text-text-secondary">{t(`${i18nPrefix}.description`, { ns: 'billing' })}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Price */}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const Button = ({
|
|||
}: ButtonProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const i18nPrefix = `plans.${plan}`
|
||||
const i18nPrefix = `plans.${plan}` as const
|
||||
const isPremiumPlan = plan === SelfHostedPlan.premium
|
||||
const AwsMarketplace = useMemo(() => {
|
||||
return theme === Theme.light ? AwsMarketplaceLight : AwsMarketplaceDark
|
||||
|
|
@ -42,7 +42,7 @@ const Button = ({
|
|||
onClick={handleGetPayUrl}
|
||||
>
|
||||
<div className="flex grow items-center gap-x-2">
|
||||
<span>{t(`${i18nPrefix}.btnText` as any, { ns: 'billing' }) as string}</span>
|
||||
<span>{t(`${i18nPrefix}.btnText`, { ns: 'billing' })}</span>
|
||||
{isPremiumPlan && (
|
||||
<span className="pb-px pt-[7px]">
|
||||
<AwsMarketplace className="h-6" />
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const SelfHostedPlanItem: FC<SelfHostedPlanItemProps> = ({
|
|||
plan,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const i18nPrefix = `plans.${plan}`
|
||||
const i18nPrefix = `plans.${plan}` as const
|
||||
const isFreePlan = plan === SelfHostedPlan.community
|
||||
const isPremiumPlan = plan === SelfHostedPlan.premium
|
||||
const isEnterprisePlan = plan === SelfHostedPlan.enterprise
|
||||
|
|
@ -85,16 +85,16 @@ const SelfHostedPlanItem: FC<SelfHostedPlanItemProps> = ({
|
|||
<div className=" flex flex-col gap-y-6 px-1 pt-10">
|
||||
{STYLE_MAP[plan].icon}
|
||||
<div className="flex min-h-[104px] flex-col gap-y-2">
|
||||
<div className="text-[30px] font-medium leading-[1.2] text-text-primary">{t(`${i18nPrefix}.name` as any, { ns: 'billing' }) as string}</div>
|
||||
<div className="system-md-regular line-clamp-2 text-text-secondary">{t(`${i18nPrefix}.description` as any, { ns: 'billing' }) as string}</div>
|
||||
<div className="text-[30px] font-medium leading-[1.2] text-text-primary">{t(`${i18nPrefix}.name`, { ns: 'billing' })}</div>
|
||||
<div className="system-md-regular line-clamp-2 text-text-secondary">{t(`${i18nPrefix}.description`, { ns: 'billing' })}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Price */}
|
||||
<div className="flex items-end gap-x-2 px-1 pb-8 pt-4">
|
||||
<div className="title-4xl-semi-bold shrink-0 text-text-primary">{t(`${i18nPrefix}.price` as any, { ns: 'billing' }) as string}</div>
|
||||
<div className="title-4xl-semi-bold shrink-0 text-text-primary">{t(`${i18nPrefix}.price`, { ns: 'billing' })}</div>
|
||||
{!isFreePlan && (
|
||||
<span className="system-md-regular pb-0.5 text-text-tertiary">
|
||||
{t(`${i18nPrefix}.priceTip` as any, { ns: 'billing' }) as string}
|
||||
{t(`${i18nPrefix}.priceTip`, { ns: 'billing' })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,14 +11,15 @@ const List = ({
|
|||
plan,
|
||||
}: ListProps) => {
|
||||
const { t } = useTranslation()
|
||||
const i18nPrefix = `plans.${plan}`
|
||||
const features = t(`${i18nPrefix}.features` as any, { ns: 'billing', returnObjects: true }) as unknown as string[]
|
||||
const i18nPrefix = `plans.${plan}` as const
|
||||
const features = t(`${i18nPrefix}.features`, { ns: 'billing', returnObjects: true }) as string[]
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-[10px] p-6">
|
||||
<div className="system-md-semibold text-text-secondary">
|
||||
<Trans
|
||||
i18nKey={t(`${i18nPrefix}.includesTitle` as any, { ns: 'billing' }) as string}
|
||||
i18nKey={`${i18nPrefix}.includesTitle`}
|
||||
ns="billing"
|
||||
components={{ highlight: <span className="text-text-warning"></span> }}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,12 +26,19 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => {
|
|||
|
||||
if (plan.type === Plan.team || plan.type === Plan.enterprise)
|
||||
return DocumentProcessingPriority.topPriority
|
||||
|
||||
return DocumentProcessingPriority.standard
|
||||
}, [plan])
|
||||
|
||||
return (
|
||||
<Tooltip popupContent={(
|
||||
<div>
|
||||
<div className="mb-1 text-xs font-semibold text-text-primary">{`${t('plansCommon.documentProcessingPriority', { ns: 'billing' })}: ${t(`plansCommon.priority.${priority}` as any, { ns: 'billing' }) as string}`}</div>
|
||||
<div className="mb-1 text-xs font-semibold text-text-primary">
|
||||
{t('plansCommon.documentProcessingPriority', { ns: 'billing' })}
|
||||
:
|
||||
{' '}
|
||||
{t(`plansCommon.priority.${priority}`, { ns: 'billing' })}
|
||||
</div>
|
||||
{
|
||||
priority !== DocumentProcessingPriority.topPriority && (
|
||||
<div className="text-xs text-text-secondary">{t('plansCommon.documentProcessingPriorityTip', { ns: 'billing' })}</div>
|
||||
|
|
@ -51,7 +58,7 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => {
|
|||
<RiAedFill className="mr-0.5 size-3" />
|
||||
)
|
||||
}
|
||||
<span>{t(`plansCommon.priority.${priority}` as any, { ns: 'billing' }) as string}</span>
|
||||
<span>{t(`plansCommon.priority.${priority}`, { ns: 'billing' })}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
'use client'
|
||||
import type { CSSProperties, FC } from 'react'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
|
@ -16,7 +17,7 @@ type Props = {
|
|||
isShort?: boolean
|
||||
onClick?: () => void
|
||||
loc?: string
|
||||
labelKey?: string
|
||||
labelKey?: I18nKeysWithPrefix<'billing', 'upgradeBtn.'>
|
||||
}
|
||||
|
||||
const UpgradeBtn: FC<Props> = ({
|
||||
|
|
@ -47,7 +48,7 @@ const UpgradeBtn: FC<Props> = ({
|
|||
}
|
||||
|
||||
const defaultBadgeLabel = t(isShort ? 'upgradeBtn.encourageShort' : 'upgradeBtn.encourage', { ns: 'billing' })
|
||||
const label = labelKey ? t(labelKey as any, { ns: 'billing' }) : defaultBadgeLabel
|
||||
const label = labelKey ? t(labelKey, { ns: 'billing' }) : defaultBadgeLabel
|
||||
|
||||
if (isPlain) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ const EconomicalRetrievalMethodConfig: FC<Props> = ({
|
|||
<div className="space-y-2">
|
||||
<RadioCard
|
||||
icon={icon}
|
||||
title={t(`retrieval.${type}.title` as any, { ns: 'dataset' }) as string}
|
||||
description={t(`retrieval.${type}.description` as any, { ns: 'dataset' }) as string}
|
||||
title={t(`retrieval.${type}.title`, { ns: 'dataset' })}
|
||||
description={t(`retrieval.${type}.description`, { ns: 'dataset' })}
|
||||
noRadio
|
||||
chosenConfigWrapClassName="!pb-3"
|
||||
chosenConfig={(
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const Content = ({
|
|||
{name}
|
||||
</div>
|
||||
<div className="system-2xs-medium-uppercase text-text-tertiary">
|
||||
{t(`chunkingMode.${DOC_FORM_TEXT[chunkStructure]}` as any, { ns: 'dataset' }) as string}
|
||||
{t(`chunkingMode.${DOC_FORM_TEXT[chunkStructure]}`, { ns: 'dataset' })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ type Props = {
|
|||
batchId: string
|
||||
documents?: FullDocumentDetail[]
|
||||
indexingType?: string
|
||||
retrievalMethod?: string
|
||||
retrievalMethod?: RETRIEVE_METHOD
|
||||
}
|
||||
|
||||
const RuleDetail: FC<{
|
||||
sourceData?: ProcessRuleResponse
|
||||
indexingType?: string
|
||||
retrievalMethod?: string
|
||||
retrievalMethod?: RETRIEVE_METHOD
|
||||
}> = ({ sourceData, indexingType, retrievalMethod }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ const RuleDetail: FC<{
|
|||
<FieldInfo
|
||||
label={t('form.retrievalSetting.title', { ns: 'datasetSettings' })}
|
||||
// displayedValue={t(`datasetSettings.form.retrievalSetting.${retrievalMethod}`) as string}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod}.title` as any, { ns: 'dataset' }) as string}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod ?? 'semantic_search'}.title`, { ns: 'dataset' })}
|
||||
valueIcon={(
|
||||
<Image
|
||||
className="size-4"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
'use client'
|
||||
import type { createDocumentResponse, FullDocumentDetail } from '@/models/datasets'
|
||||
import type { RETRIEVE_METHOD } from '@/types/app'
|
||||
import { RiBookOpenLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
|
@ -14,7 +15,7 @@ type StepThreeProps = {
|
|||
datasetId?: string
|
||||
datasetName?: string
|
||||
indexingType?: string
|
||||
retrievalMethod?: string
|
||||
retrievalMethod?: RETRIEVE_METHOD
|
||||
creationCache?: createDocumentResponse
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const RuleDetail = ({
|
|||
/>
|
||||
<FieldInfo
|
||||
label={t('form.retrievalSetting.title', { ns: 'datasetSettings' })}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod}.title` as any, { ns: 'dataset' }) as string}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod ?? 'semantic_search'}.title`, { ns: 'dataset' })}
|
||||
valueIcon={(
|
||||
<Image
|
||||
className="size-4"
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const RuleDetail: FC<IRuleDetailProps> = React.memo(({
|
|||
/>
|
||||
<FieldInfo
|
||||
label={t('form.retrievalSetting.title', { ns: 'datasetSettings' })}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod}.title` as any, { ns: 'dataset' }) as string}
|
||||
displayedValue={t(`retrieval.${indexingType === IndexingType.ECONOMICAL ? 'keyword_search' : retrievalMethod ?? 'semantic_search'}.title`, { ns: 'dataset' })}
|
||||
valueIcon={(
|
||||
<Image
|
||||
className="size-4"
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ const QueryInput = ({
|
|||
className="flex h-7 cursor-pointer items-center space-x-0.5 rounded-lg border-[0.5px] border-components-button-secondary-bg bg-components-button-secondary-bg px-1.5 shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover"
|
||||
>
|
||||
{icon}
|
||||
<div className="text-xs font-medium uppercase text-text-secondary">{t(`retrieval.${retrievalMethod}.title` as any, { ns: 'dataset' }) as string}</div>
|
||||
<div className="text-xs font-medium uppercase text-text-secondary">{t(`retrieval.${retrievalMethod}.title`, { ns: 'dataset' })}</div>
|
||||
<RiEqualizer2Line className="size-4 text-components-menu-item-text"></RiEqualizer2Line>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -217,9 +217,9 @@ const DatasetCard = ({
|
|||
{dataset.doc_form && (
|
||||
<span
|
||||
className="min-w-0 max-w-full truncate"
|
||||
title={t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any, { ns: 'dataset' }) as string}
|
||||
title={t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}
|
||||
>
|
||||
{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any, { ns: 'dataset' }) as string}
|
||||
{t(`chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`, { ns: 'dataset' })}
|
||||
</span>
|
||||
)}
|
||||
{dataset.indexing_technique && (
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const Category: FC<ICategoryProps> = ({
|
|||
className={itemClassName(name === value)}
|
||||
onClick={() => onChange(name)}
|
||||
>
|
||||
{`category.${name}` in exploreI18n ? t(`category.${name}` as any, { ns: 'explore' }) as string : name}
|
||||
{`category.${name}` in exploreI18n ? t(`category.${name}`, { ns: 'explore' }) : name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
'use client'
|
||||
import type { RoleKey } from './role-selector'
|
||||
import type { InvitationResult } from '@/models/common'
|
||||
import { RiCloseLine, RiErrorWarningFill } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
|
|
@ -47,7 +48,7 @@ const InviteModal = ({
|
|||
}, [licenseLimit, emails])
|
||||
|
||||
const { locale } = useContext(I18n)
|
||||
const [role, setRole] = useState<string>('normal')
|
||||
const [role, setRole] = useState<RoleKey>('normal')
|
||||
|
||||
const [isSubmitting, {
|
||||
setTrue: setIsSubmitting,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,18 @@ import {
|
|||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const roleI18nKeyMap = {
|
||||
normal: 'members.normal',
|
||||
editor: 'members.editor',
|
||||
admin: 'members.admin',
|
||||
dataset_operator: 'members.datasetOperator',
|
||||
} as const
|
||||
|
||||
export type RoleKey = keyof typeof roleI18nKeyMap
|
||||
|
||||
export type RoleSelectorProps = {
|
||||
value: string
|
||||
onChange: (role: string) => void
|
||||
value: RoleKey
|
||||
onChange: (role: RoleKey) => void
|
||||
}
|
||||
|
||||
const RoleSelector = ({ value, onChange }: RoleSelectorProps) => {
|
||||
|
|
@ -21,8 +30,6 @@ const RoleSelector = ({ value, onChange }: RoleSelectorProps) => {
|
|||
const [open, setOpen] = useState(false)
|
||||
const { datasetOperatorEnabled } = useProviderContext()
|
||||
|
||||
const toHump = (name: string) => name.replace(/_(\w)/g, (all, letter) => letter.toUpperCase())
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
|
|
@ -36,7 +43,7 @@ const RoleSelector = ({ value, onChange }: RoleSelectorProps) => {
|
|||
className="block"
|
||||
>
|
||||
<div className={cn('flex cursor-pointer items-center rounded-lg bg-components-input-bg-normal px-3 py-2 hover:bg-state-base-hover', open && 'bg-state-base-hover')}>
|
||||
<div className="mr-2 grow text-sm leading-5 text-text-primary">{t('members.invitedAsRole', { ns: 'common', role: t(`members.${toHump(value)}` as any, { ns: 'common' }) })}</div>
|
||||
<div className="mr-2 grow text-sm leading-5 text-text-primary">{t('members.invitedAsRole', { ns: 'common', role: t(roleI18nKeyMap[value], { ns: 'common' }) })}</div>
|
||||
<RiArrowDownSLine className="h-4 w-4 shrink-0 text-text-secondary" />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,15 @@ type IOperationProps = {
|
|||
onOperate: () => void
|
||||
}
|
||||
|
||||
const roleI18nKeyMap = {
|
||||
admin: { label: 'members.admin', tip: 'members.adminTip' },
|
||||
editor: { label: 'members.editor', tip: 'members.editorTip' },
|
||||
normal: { label: 'members.normal', tip: 'members.normalTip' },
|
||||
dataset_operator: { label: 'members.datasetOperator', tip: 'members.datasetOperatorTip' },
|
||||
} as const
|
||||
|
||||
type OperationRoleKey = keyof typeof roleI18nKeyMap
|
||||
|
||||
const Operation = ({
|
||||
member,
|
||||
operatorRole,
|
||||
|
|
@ -35,26 +44,25 @@ const Operation = ({
|
|||
normal: t('members.normal', { ns: 'common' }),
|
||||
dataset_operator: t('members.datasetOperator', { ns: 'common' }),
|
||||
}
|
||||
const roleList = useMemo(() => {
|
||||
const roleList = useMemo((): OperationRoleKey[] => {
|
||||
if (operatorRole === 'owner') {
|
||||
return [
|
||||
'admin',
|
||||
'editor',
|
||||
'normal',
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] as const : []),
|
||||
]
|
||||
}
|
||||
if (operatorRole === 'admin') {
|
||||
return [
|
||||
'editor',
|
||||
'normal',
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] as const : []),
|
||||
]
|
||||
}
|
||||
return []
|
||||
}, [operatorRole, datasetOperatorEnabled])
|
||||
const { notify } = useContext(ToastContext)
|
||||
const toHump = (name: string) => name.replace(/_(\w)/g, (all, letter) => letter.toUpperCase())
|
||||
const handleDeleteMemberOrCancelInvitation = async () => {
|
||||
setOpen(false)
|
||||
try {
|
||||
|
|
@ -106,8 +114,8 @@ const Operation = ({
|
|||
: <div className="mr-1 mt-[2px] h-4 w-4 text-text-accent" />
|
||||
}
|
||||
<div>
|
||||
<div className="system-sm-semibold whitespace-nowrap text-text-secondary">{t(`members.${toHump(role)}` as any, { ns: 'common' })}</div>
|
||||
<div className="system-xs-regular whitespace-nowrap text-text-tertiary">{t(`members.${toHump(role)}Tip` as any, { ns: 'common' })}</div>
|
||||
<div className="system-sm-semibold whitespace-nowrap text-text-secondary">{t(roleI18nKeyMap[role].label, { ns: 'common' })}</div>
|
||||
<div className="system-xs-regular whitespace-nowrap text-text-tertiary">{t(roleI18nKeyMap[role].tip, { ns: 'common' })}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
|
|||
import { TONE_LIST } from '@/config'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const toneI18nKeyMap = {
|
||||
Creative: 'model.tone.Creative',
|
||||
Balanced: 'model.tone.Balanced',
|
||||
Precise: 'model.tone.Precise',
|
||||
Custom: 'model.tone.Custom',
|
||||
} as const
|
||||
|
||||
type PresetsParameterProps = {
|
||||
onSelect: (toneId: number) => void
|
||||
}
|
||||
|
|
@ -44,7 +51,7 @@ const PresetsParameter: FC<PresetsParameterProps> = ({
|
|||
text: (
|
||||
<div className="flex h-full items-center">
|
||||
{getToneIcon(tone.id)}
|
||||
{t(`model.tone.${tone.name}` as any, { ns: 'common' }) as string}
|
||||
{t(toneI18nKeyMap[tone.name], { ns: 'common' })}
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ type DeprecationNoticeProps = {
|
|||
|
||||
const i18nPrefix = 'detailPanel.deprecation'
|
||||
|
||||
type DeprecatedReasonKey = 'businessAdjustments' | 'ownershipTransferred' | 'noMaintainer'
|
||||
const validReasonKeys: DeprecatedReasonKey[] = ['businessAdjustments', 'ownershipTransferred', 'noMaintainer']
|
||||
|
||||
function isValidReasonKey(key: string): key is DeprecatedReasonKey {
|
||||
return (validReasonKeys as string[]).includes(key)
|
||||
}
|
||||
|
||||
const DeprecationNotice: FC<DeprecationNoticeProps> = ({
|
||||
status,
|
||||
deprecatedReason,
|
||||
|
|
@ -37,19 +44,15 @@ const DeprecationNotice: FC<DeprecationNoticeProps> = ({
|
|||
|
||||
const deprecatedReasonKey = useMemo(() => {
|
||||
if (!deprecatedReason)
|
||||
return ''
|
||||
return camelCase(deprecatedReason)
|
||||
return null
|
||||
const key = camelCase(deprecatedReason)
|
||||
if (isValidReasonKey(key))
|
||||
return key
|
||||
return null
|
||||
}, [deprecatedReason])
|
||||
|
||||
// Check if the deprecatedReasonKey exists in i18n
|
||||
const hasValidDeprecatedReason = useMemo(() => {
|
||||
if (!deprecatedReason || !deprecatedReasonKey)
|
||||
return false
|
||||
|
||||
// Define valid reason keys that exist in i18n
|
||||
const validReasonKeys = ['businessAdjustments', 'ownershipTransferred', 'noMaintainer']
|
||||
return validReasonKeys.includes(deprecatedReasonKey)
|
||||
}, [deprecatedReason, deprecatedReasonKey])
|
||||
const hasValidDeprecatedReason = deprecatedReasonKey !== null
|
||||
|
||||
if (status !== 'deleted')
|
||||
return null
|
||||
|
|
@ -82,7 +85,7 @@ const DeprecationNotice: FC<DeprecationNoticeProps> = ({
|
|||
),
|
||||
}}
|
||||
values={{
|
||||
deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}` as any, { ns: 'plugin' }) as string,
|
||||
deprecatedReason: deprecatedReasonKey ? t(`${i18nPrefix}.reason.${deprecatedReasonKey}`, { ns: 'plugin' }) : '',
|
||||
alternativePluginId,
|
||||
}}
|
||||
/>
|
||||
|
|
@ -91,7 +94,7 @@ const DeprecationNotice: FC<DeprecationNoticeProps> = ({
|
|||
{
|
||||
hasValidDeprecatedReason && !alternativePluginId && (
|
||||
<span>
|
||||
{t(`${i18nPrefix}.onlyReason` as any, { ns: 'plugin', deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}` as any, { ns: 'plugin' }) as string }) as string}
|
||||
{t(`${i18nPrefix}.onlyReason`, { ns: 'plugin', deprecatedReason: deprecatedReasonKey ? t(`${i18nPrefix}.reason.${deprecatedReasonKey}`, { ns: 'plugin' }) : '' })}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const LoopResultPanel: FC<Props> = ({
|
|||
<div className={cn(!noWrap && 'shrink-0 ', 'px-4 pt-3')}>
|
||||
<div className="flex h-8 shrink-0 items-center justify-between">
|
||||
<div className="system-xl-semibold truncate text-text-primary">
|
||||
{t(`${i18nPrefix}.testRunLoop` as any, { ns: 'workflow' }) as string}
|
||||
{t(`${i18nPrefix}.testRunLoop`, { ns: 'workflow' }) }
|
||||
</div>
|
||||
<div className="ml-2 shrink-0 cursor-pointer p-1" onClick={onHide}>
|
||||
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const ForgotPasswordForm = () => {
|
|||
{...register('email')}
|
||||
placeholder={t('emailPlaceholder', { ns: 'login' }) || ''}
|
||||
/>
|
||||
{errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}` as any, { ns: 'login' })}</span>}
|
||||
{errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}` as 'error.emailInValid', { ns: 'login' })}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const InstallForm = () => {
|
|||
placeholder={t('emailPlaceholder', { ns: 'login' }) || ''}
|
||||
className="system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs"
|
||||
/>
|
||||
{errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}` as any, { ns: 'login' })}</span>}
|
||||
{errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}` as 'error.emailInValid', { ns: 'login' })}</span>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -154,7 +154,7 @@ const InstallForm = () => {
|
|||
className="system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs"
|
||||
/>
|
||||
</div>
|
||||
{errors.name && <span className="text-sm text-red-400">{t(`${errors.name.message}` as any, { ns: 'login' })}</span>}
|
||||
{errors.name && <span className="text-sm text-red-400">{t(`${errors.name.message}` as 'error.nameEmpty', { ns: 'login' })}</span>}
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export const TONE_LIST = [
|
|||
id: 4,
|
||||
name: 'Custom',
|
||||
},
|
||||
]
|
||||
] as const
|
||||
|
||||
export const DEFAULT_CHAT_PROMPT_CONFIG = {
|
||||
prompt: [
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import noAsAnyInT from './rules/no-as-any-in-t.js'
|
||||
import noLegacyNamespacePrefix from './rules/no-legacy-namespace-prefix.js'
|
||||
import requireNsOption from './rules/require-ns-option.js'
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ const plugin = {
|
|||
version: '1.0.0',
|
||||
},
|
||||
rules: {
|
||||
'no-as-any-in-t': noAsAnyInT,
|
||||
'no-legacy-namespace-prefix': noLegacyNamespacePrefix,
|
||||
'require-ns-option': requireNsOption,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
export default {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow using "as any" type assertion in t() function calls',
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
noAsAnyInT:
|
||||
'Avoid using "as any" in t() function calls. Use proper i18n key types instead.',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
/**
|
||||
* Check if this is a t() function call
|
||||
* @param {import('estree').CallExpression} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTCall(node) {
|
||||
// Direct t() call
|
||||
if (node.callee.type === 'Identifier' && node.callee.name === 't')
|
||||
return true
|
||||
// i18n.t() or similar member expression
|
||||
if (
|
||||
node.callee.type === 'MemberExpression'
|
||||
&& node.callee.property.type === 'Identifier'
|
||||
&& node.callee.property.name === 't'
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is a TSAsExpression with "any" type
|
||||
* @param {object} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isAsAny(node) {
|
||||
return (
|
||||
node.type === 'TSAsExpression'
|
||||
&& node.typeAnnotation
|
||||
&& node.typeAnnotation.type === 'TSAnyKeyword'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (!isTCall(node) || node.arguments.length === 0)
|
||||
return
|
||||
|
||||
const firstArg = node.arguments[0]
|
||||
|
||||
// Check if the first argument uses "as any"
|
||||
if (isAsAny(firstArg)) {
|
||||
context.report({
|
||||
node: firstArg,
|
||||
messageId: 'noAsAnyInT',
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -165,6 +165,7 @@ export default antfu(
|
|||
'dify-i18n': difyI18n,
|
||||
},
|
||||
rules: {
|
||||
'dify-i18n/no-as-any-in-t': 'error',
|
||||
'dify-i18n/no-legacy-namespace-prefix': 'error',
|
||||
'dify-i18n/require-ns-option': 'error',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type IndexingTechnique = I18nKeysByPrefix<'dataset', 'indexingTechnique.'>
|
||||
type IndexingMethod = I18nKeysByPrefix<'dataset', 'indexingMethod.'>
|
||||
|
||||
export const useKnowledge = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const formatIndexingTechnique = useCallback((indexingTechnique: string) => {
|
||||
return t(`indexingTechnique.${indexingTechnique}` as any, { ns: 'dataset' }) as string
|
||||
const formatIndexingTechnique = useCallback((indexingTechnique: IndexingTechnique) => {
|
||||
return t(`indexingTechnique.${indexingTechnique}`, { ns: 'dataset' }) as string
|
||||
}, [t])
|
||||
|
||||
const formatIndexingMethod = useCallback((indexingMethod: string, isEco?: boolean) => {
|
||||
const formatIndexingMethod = useCallback((indexingMethod: IndexingMethod, isEco?: boolean) => {
|
||||
if (isEco)
|
||||
return t('indexingMethod.invertedIndex', { ns: 'dataset' })
|
||||
|
||||
return t(`indexingMethod.${indexingMethod}` as any, { ns: 'dataset' }) as string
|
||||
return t(`indexingMethod.${indexingMethod}`, { ns: 'dataset' }) as string
|
||||
}, [t])
|
||||
|
||||
const formatIndexingTechniqueAndMethod = useCallback((indexingTechnique: string, indexingMethod: string) => {
|
||||
const formatIndexingTechniqueAndMethod = useCallback((indexingTechnique: IndexingTechnique, indexingMethod: IndexingMethod) => {
|
||||
let result = formatIndexingTechnique(indexingTechnique)
|
||||
|
||||
if (indexingMethod)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
'use client'
|
||||
import type { DocType } from '@/models/datasets'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { ChunkingMode } from '@/models/datasets'
|
||||
|
|
@ -86,7 +87,7 @@ export const useMetadataMap = (): MetadataMap => {
|
|||
},
|
||||
'volume/issue/page_numbers': { label: t(`${fieldPrefix}.paper.volumeIssuePage`, { ns: 'datasetDocuments' }) },
|
||||
'doi': { label: t(`${fieldPrefix}.paper.DOI`, { ns: 'datasetDocuments' }) },
|
||||
'topic/keywords': { label: t(`${fieldPrefix}.paper.topicKeywords` as any, { ns: 'datasetDocuments' }) as string },
|
||||
'topic/keywords': { label: t(`${fieldPrefix}.paper.topicsKeywords`, { ns: 'datasetDocuments' }) },
|
||||
'abstract': {
|
||||
label: t(`${fieldPrefix}.paper.abstract`, { ns: 'datasetDocuments' }),
|
||||
inputType: 'textarea',
|
||||
|
|
@ -160,7 +161,7 @@ export const useMetadataMap = (): MetadataMap => {
|
|||
'end_date': { label: t(`${fieldPrefix}.IMChat.endDate`, { ns: 'datasetDocuments' }) },
|
||||
'participants': { label: t(`${fieldPrefix}.IMChat.participants`, { ns: 'datasetDocuments' }) },
|
||||
'topicKeywords': {
|
||||
label: t(`${fieldPrefix}.IMChat.topicKeywords` as any, { ns: 'datasetDocuments' }) as string,
|
||||
label: t(`${fieldPrefix}.IMChat.topicsKeywords`, { ns: 'datasetDocuments' }),
|
||||
inputType: 'textarea',
|
||||
},
|
||||
'fileType': { label: t(`${fieldPrefix}.IMChat.fileType`, { ns: 'datasetDocuments' }) },
|
||||
|
|
@ -193,7 +194,7 @@ export const useMetadataMap = (): MetadataMap => {
|
|||
allowEdit: false,
|
||||
subFieldsMap: {
|
||||
'title': { label: t(`${fieldPrefix}.notion.title`, { ns: 'datasetDocuments' }) },
|
||||
'language': { label: t(`${fieldPrefix}.notion.lang` as any, { ns: 'datasetDocuments' }) as string, inputType: 'select' },
|
||||
'language': { label: t(`${fieldPrefix}.notion.language`, { ns: 'datasetDocuments' }), inputType: 'select' },
|
||||
'author/creator': { label: t(`${fieldPrefix}.notion.author`, { ns: 'datasetDocuments' }) },
|
||||
'creation_date': { label: t(`${fieldPrefix}.notion.createdTime`, { ns: 'datasetDocuments' }) },
|
||||
'last_modified_date': {
|
||||
|
|
@ -201,7 +202,7 @@ export const useMetadataMap = (): MetadataMap => {
|
|||
},
|
||||
'notion_page_link': { label: t(`${fieldPrefix}.notion.url`, { ns: 'datasetDocuments' }) },
|
||||
'category/tags': { label: t(`${fieldPrefix}.notion.tag`, { ns: 'datasetDocuments' }) },
|
||||
'description': { label: t(`${fieldPrefix}.notion.desc` as any, { ns: 'datasetDocuments' }) as string },
|
||||
'description': { label: t(`${fieldPrefix}.notion.description`, { ns: 'datasetDocuments' }) },
|
||||
},
|
||||
},
|
||||
synced_from_github: {
|
||||
|
|
@ -241,7 +242,7 @@ export const useMetadataMap = (): MetadataMap => {
|
|||
},
|
||||
'data_source_type': {
|
||||
label: t(`${fieldPrefix}.originInfo.source`, { ns: 'datasetDocuments' }),
|
||||
render: value => t(`metadata.source.${value === 'notion_import' ? 'notion' : value}` as any, { ns: 'datasetDocuments' }) as string,
|
||||
render: (value: I18nKeysByPrefix<'datasetDocuments', 'metadata.source.'> | 'notion_import') => t(`metadata.source.${value === 'notion_import' ? 'notion' : value}`, { ns: 'datasetDocuments' }),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -323,7 +324,7 @@ export const useLanguages = () => {
|
|||
cs: t(`${langPrefix}cs`, { ns: 'datasetDocuments' }),
|
||||
th: t(`${langPrefix}th`, { ns: 'datasetDocuments' }),
|
||||
id: t(`${langPrefix}id`, { ns: 'datasetDocuments' }),
|
||||
ro: t(`${langPrefix}ro` as any, { ns: 'datasetDocuments' }) as string,
|
||||
ro: t(`${langPrefix}ro`, { ns: 'datasetDocuments' }),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import tools from '../i18n/en-US/tools.json'
|
|||
import workflow from '../i18n/en-US/workflow.json'
|
||||
|
||||
// @keep-sorted
|
||||
export const namespaces = {
|
||||
export const resources = {
|
||||
app,
|
||||
appAnnotation,
|
||||
appApi,
|
||||
|
|
@ -79,7 +79,8 @@ export type CamelCase<S extends string> = S extends `${infer T}-${infer U}`
|
|||
? `${T}${Capitalize<CamelCase<U>>}`
|
||||
: S
|
||||
|
||||
export type NamespaceCamelCase = keyof typeof namespaces
|
||||
export type Resources = typeof resources
|
||||
export type NamespaceCamelCase = keyof Resources
|
||||
export type NamespaceKebabCase = KebabCase<NamespaceCamelCase>
|
||||
|
||||
const requireSilent = async (lang: Locale, namespace: NamespaceKebabCase) => {
|
||||
|
|
@ -94,7 +95,7 @@ const requireSilent = async (lang: Locale, namespace: NamespaceKebabCase) => {
|
|||
return res
|
||||
}
|
||||
|
||||
const NAMESPACES = Object.keys(namespaces).map(kebabCase) as NamespaceKebabCase[]
|
||||
const NAMESPACES = Object.keys(resources).map(kebabCase) as NamespaceKebabCase[]
|
||||
|
||||
// Load a single namespace for a language
|
||||
export const loadNamespace = async (lang: Locale, ns: NamespaceKebabCase) => {
|
||||
|
|
@ -116,7 +117,7 @@ export const loadLangResources = async (lang: Locale) => {
|
|||
// Initial resources: load en-US namespaces for fallback/default locale
|
||||
const getInitialTranslations = () => {
|
||||
return {
|
||||
'en-US': namespaces,
|
||||
'en-US': resources,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ if (!i18n.isInitialized) {
|
|||
fallbackLng: 'en-US',
|
||||
resources: getInitialTranslations(),
|
||||
defaultNS: 'common',
|
||||
ns: Object.keys(namespaces),
|
||||
ns: Object.keys(resources),
|
||||
keySeparator: false,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
"plans.community.includesTitle": "Free Features:",
|
||||
"plans.community.name": "Community",
|
||||
"plans.community.price": "Free",
|
||||
"plans.community.priceTip": "",
|
||||
"plans.enterprise.btnText": "Contact Sales",
|
||||
"plans.enterprise.description": "For enterprise requiring organization-grade security, compliance, scalability, control and custom solutions",
|
||||
"plans.enterprise.features": [
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@
|
|||
"metadata.languageMap.no": "Norwegian",
|
||||
"metadata.languageMap.pl": "Polish",
|
||||
"metadata.languageMap.pt": "Portuguese",
|
||||
"metadata.languageMap.ro": "Romanian",
|
||||
"metadata.languageMap.ru": "Russian",
|
||||
"metadata.languageMap.sv": "Swedish",
|
||||
"metadata.languageMap.th": "Thai",
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@
|
|||
"retrieval.hybrid_search.description": "Execute full-text search and vector searches simultaneously, re-rank to select the best match for the user's query. Users can choose to set weights or configure to a Rerank model.",
|
||||
"retrieval.hybrid_search.recommend": "Recommend",
|
||||
"retrieval.hybrid_search.title": "Hybrid Search",
|
||||
"retrieval.invertedIndex.description": "Inverted Index is a structure used for efficient retrieval. Organized by terms, each term points to documents or web pages containing it.",
|
||||
"retrieval.invertedIndex.title": "Inverted Index",
|
||||
"retrieval.keyword_search.description": "Inverted Index is a structure used for efficient retrieval. Organized by terms, each term points to documents or web pages containing it.",
|
||||
"retrieval.keyword_search.title": "Inverted Index",
|
||||
"retrieval.semantic_search.description": "Generate query embeddings and search for the text chunk most similar to its vector representation.",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"category.Entertainment": "Entertainment",
|
||||
"category.HR": "HR",
|
||||
"category.Programming": "Programming",
|
||||
"category.Recommended": "Recommended",
|
||||
"category.Translate": "Translate",
|
||||
"category.Workflow": "Workflow",
|
||||
"category.Writing": "Writing",
|
||||
|
|
|
|||
|
|
@ -971,6 +971,7 @@
|
|||
"singleRun.startRun": "Start Run",
|
||||
"singleRun.testRun": "Test Run",
|
||||
"singleRun.testRunIteration": "Test Run Iteration",
|
||||
"singleRun.testRunLoop": "Test Run Loop",
|
||||
"tabs.addAll": "Add all",
|
||||
"tabs.agent": "Agent Strategy",
|
||||
"tabs.allAdded": "All added",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type { IndexingType } from '@/app/components/datasets/create/step-two'
|
|||
import type { MetadataItemWithValue } from '@/app/components/datasets/metadata/types'
|
||||
import type { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
|
||||
import type { AppIconType, AppModeEnum, RetrievalConfig, TransferMethod } from '@/types/app'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import { ExternalKnowledgeBase, General, ParentChild, Qa } from '@/app/components/base/icons/src/public/knowledge/dataset-card'
|
||||
import { GeneralChunk, ParentChildChunk, QuestionAndAnswer } from '@/app/components/base/icons/src/vender/knowledge'
|
||||
|
||||
|
|
@ -804,7 +805,9 @@ export const DOC_FORM_ICON: Record<ChunkingMode.text | ChunkingMode.qa | Chunkin
|
|||
[ChunkingMode.parentChild]: ParentChildChunk,
|
||||
}
|
||||
|
||||
export const DOC_FORM_TEXT: Record<ChunkingMode, string> = {
|
||||
type ChunkingModeText = I18nKeysByPrefix<'dataset', 'chunkingMode.'>
|
||||
|
||||
export const DOC_FORM_TEXT: Record<ChunkingMode, ChunkingModeText> = {
|
||||
[ChunkingMode.text]: 'general',
|
||||
[ChunkingMode.qa]: 'qa',
|
||||
[ChunkingMode.parentChild]: 'parentChild',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,25 @@
|
|||
import type { namespaces } from '../i18n-config/i18next-config'
|
||||
import type { NamespaceCamelCase, Resources } from '../i18n-config/i18next-config'
|
||||
import 'i18next'
|
||||
|
||||
declare module 'i18next' {
|
||||
// eslint-disable-next-line ts/consistent-type-definitions
|
||||
interface CustomTypeOptions {
|
||||
defaultNS: 'common'
|
||||
resources: typeof namespaces
|
||||
resources: Resources
|
||||
keySeparator: false
|
||||
}
|
||||
}
|
||||
|
||||
export type I18nKeysByPrefix<
|
||||
NS extends NamespaceCamelCase,
|
||||
Prefix extends string = '',
|
||||
> = keyof Resources[NS] extends infer K
|
||||
? K extends `${Prefix}${infer Rest}`
|
||||
? Rest
|
||||
: never
|
||||
: never
|
||||
|
||||
export type I18nKeysWithPrefix<
|
||||
NS extends NamespaceCamelCase,
|
||||
Prefix extends string = '',
|
||||
> = Extract<keyof Resources[NS], `${Prefix}${string}`>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { InputVar } from '@/app/components/workflow/types'
|
||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||
import {
|
||||
CONTEXT_PLACEHOLDER_TEXT,
|
||||
HISTORY_PLACEHOLDER_TEXT,
|
||||
|
|
@ -49,7 +50,9 @@ export const getNewVarInWorkflow = (key: string, type = InputVarType.textInput):
|
|||
}
|
||||
}
|
||||
|
||||
export const checkKey = (key: string, canBeEmpty?: boolean, _keys?: string[]) => {
|
||||
export type VarKeyErrorMessageKey = I18nKeysByPrefix<'appDebug', 'varKeyError.'>
|
||||
|
||||
export const checkKey = (key: string, canBeEmpty?: boolean, _keys?: string[]): true | VarKeyErrorMessageKey => {
|
||||
if (key.length === 0 && !canBeEmpty)
|
||||
return 'canNoBeEmpty'
|
||||
|
||||
|
|
@ -68,10 +71,14 @@ export const checkKey = (key: string, canBeEmpty?: boolean, _keys?: string[]) =>
|
|||
return 'notValid'
|
||||
}
|
||||
|
||||
export const checkKeys = (keys: string[], canBeEmpty?: boolean) => {
|
||||
type CheckKeysResult
|
||||
= | { isValid: true, errorKey: '', errorMessageKey: '' }
|
||||
| { isValid: false, errorKey: string, errorMessageKey: VarKeyErrorMessageKey }
|
||||
|
||||
export const checkKeys = (keys: string[], canBeEmpty?: boolean): CheckKeysResult => {
|
||||
let isValid = true
|
||||
let errorKey = ''
|
||||
let errorMessageKey = ''
|
||||
let errorMessageKey: VarKeyErrorMessageKey | '' = ''
|
||||
keys.forEach((key) => {
|
||||
if (!isValid)
|
||||
return
|
||||
|
|
@ -83,7 +90,7 @@ export const checkKeys = (keys: string[], canBeEmpty?: boolean) => {
|
|||
errorMessageKey = res
|
||||
}
|
||||
})
|
||||
return { isValid, errorKey, errorMessageKey }
|
||||
return { isValid, errorKey, errorMessageKey } as CheckKeysResult
|
||||
}
|
||||
|
||||
export const hasDuplicateStr = (strArr: string[]) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue