diff --git a/.github/workflows/translate-i18n-base-on-english.yml b/.github/workflows/translate-i18n-base-on-english.yml index 8bb82d5d44..87e24a4f90 100644 --- a/.github/workflows/translate-i18n-base-on-english.yml +++ b/.github/workflows/translate-i18n-base-on-english.yml @@ -1,4 +1,4 @@ -name: Check i18n Files and Create PR +name: Translate i18n Files Based on English on: push: @@ -67,25 +67,19 @@ jobs: working-directory: ./web run: pnpm run auto-gen-i18n ${{ env.FILE_ARGS }} - - name: Generate i18n type definitions - if: env.FILES_CHANGED == 'true' - working-directory: ./web - run: pnpm run gen:i18n-types - - name: Create Pull Request if: env.FILES_CHANGED == 'true' uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: 'chore(i18n): update translations based on en-US changes' - title: 'chore(i18n): translate i18n files and update type definitions' + title: 'chore(i18n): translate i18n files based on en-US changes' body: | - This PR was automatically created to update i18n files and TypeScript type definitions based on changes in en-US locale. + This PR was automatically created to update i18n translation files based on changes in en-US locale. **Triggered by:** ${{ github.sha }} **Changes included:** - Updated translation files for all locales - - Regenerated TypeScript type definitions for type safety branch: chore/automated-i18n-updates-${{ github.sha }} delete-branch: true diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml index adf52a1362..1a8925e38d 100644 --- a/.github/workflows/web-tests.yml +++ b/.github/workflows/web-tests.yml @@ -38,9 +38,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Check i18n types synchronization - run: pnpm run check:i18n-types - - name: Run tests run: pnpm test:coverage diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx index e9877f1715..34bb0f82f8 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -104,7 +104,7 @@ const CardView: FC = ({ appId, isInPanel, className }) => { notify({ type, - message: t(`common.actionMsg.${message}`), + message: t(`common.actionMsg.${message}` as any) as string, }) } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx index 557b723259..55ed906a45 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx @@ -53,7 +53,7 @@ const LongTimeRangePicker: FC = ({ return ( ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))} + items={Object.entries(periodMapping).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}` as any) as string }))} className="mt-0 !w-40" notClearable={true} onSelect={handleSelect} diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx index be7181c759..88cb79ce0d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx @@ -66,7 +66,7 @@ const RangeSelector: FC = ({ }, []) return ( ({ ...v, name: t(`appLog.filter.period.${v.name}`) }))} + items={ranges.map(v => ({ ...v, name: t(`appLog.filter.period.${v.name}` as any) as string }))} className="mt-0 !w-40" notClearable={true} onSelect={handleSelectRange} diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx index 9dfeaef528..aa64df3449 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx @@ -1,15 +1,15 @@ import * as React from 'react' import Form from '@/app/components/datasets/settings/form' -import { getLocaleOnServer, useTranslation as translate } from '@/i18n-config/server' +import { getLocaleOnServer, getTranslation } from '@/i18n-config/server' const Settings = async () => { const locale = await getLocaleOnServer() - const { t } = await translate(locale, 'dataset-settings') + const { t } = await getTranslation(locale, 'dataset-settings') return (
-
{t('title')}
+
{t('title') as any}
{t('desc')}
diff --git a/web/app/components/app-sidebar/dataset-info/index.tsx b/web/app/components/app-sidebar/dataset-info/index.tsx index ce409ff13a..39a1115062 100644 --- a/web/app/components/app-sidebar/dataset-info/index.tsx +++ b/web/app/components/app-sidebar/dataset-info/index.tsx @@ -73,7 +73,7 @@ const DatasetInfo: FC = ({ {isExternalProvider && t('dataset.externalTag')} {!isExternalProvider && isPipelinePublished && dataset.doc_form && dataset.indexing_technique && (
- {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)} + {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any) as string} {formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}
)} diff --git a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx index d8e26826ca..0e55a8af65 100644 --- a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx @@ -116,7 +116,7 @@ const DatasetSidebarDropdown = ({ {isExternalProvider && t('dataset.externalTag')} {!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
- {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)} + {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any) as string} {formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}
)} diff --git a/web/app/components/app/annotation/header-opts/index.spec.tsx b/web/app/components/app/annotation/header-opts/index.spec.tsx index c742f8effc..c52507fb22 100644 --- a/web/app/components/app/annotation/header-opts/index.spec.tsx +++ b/web/app/components/app/annotation/header-opts/index.spec.tsx @@ -1,5 +1,6 @@ import type { ComponentProps } from 'react' import type { AnnotationItemBasic } from '../type' +import type { Locale } from '@/i18n-config' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' @@ -166,7 +167,7 @@ type HeaderOptionsProps = ComponentProps const renderComponent = ( props: Partial = {}, - locale: string = LanguagesSupported[0] as string, + locale: Locale = LanguagesSupported[0], ) => { const defaultProps: HeaderOptionsProps = { appId: 'test-app-id', @@ -353,7 +354,7 @@ describe('HeaderOptions', () => { }) const revokeSpy = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(vi.fn()) - renderComponent({}, LanguagesSupported[1] as string) + renderComponent({}, LanguagesSupported[1]) await expandExportMenu(user) @@ -441,7 +442,7 @@ describe('HeaderOptions', () => { view.rerender( = ({ mode }) => { <>
- {t(`app.accessControlDialog.accessItems.${label}`)} + {t(`app.accessControlDialog.accessItems.${label}` as any) as string}
) diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 41f37b5895..b1d8e8cd19 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -96,7 +96,7 @@ const ConfigModal: FC = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: t('appDebug.variableConfig.varName') }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('appDebug.variableConfig.varName') }) as string, }) return false } @@ -216,7 +216,7 @@ const ConfigModal: FC = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }) as string, }) return } diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 7a2a86393a..bf528f8ca6 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -98,7 +98,7 @@ const ConfigVar: FC = ({ promptVariables, readonly, onPromptVar if (errorMsgKey) { Toast.notify({ type: 'error', - message: t(errorMsgKey, { key: t(typeName) }), + message: t(errorMsgKey as any, { key: t(typeName as any) as string }) as string, }) return false } diff --git a/web/app/components/app/configuration/config-var/select-type-item/index.tsx b/web/app/components/app/configuration/config-var/select-type-item/index.tsx index ccb958977c..a72a74a5e9 100644 --- a/web/app/components/app/configuration/config-var/select-type-item/index.tsx +++ b/web/app/components/app/configuration/config-var/select-type-item/index.tsx @@ -23,7 +23,7 @@ const SelectTypeItem: FC = ({ onClick, }) => { const { t } = useTranslation() - const typeName = t(`appDebug.variableConfig.${i18nFileTypeMap[type] || type}`) + const typeName = t(`appDebug.variableConfig.${i18nFileTypeMap[type] || type}` as any) as string return (
= ({ const [editorKey, setEditorKey] = useState(`${flowId}-0`) const handleChooseTemplate = useCallback((key: string) => { return () => { - const template = t(`appDebug.generate.template.${key}.instruction`) + const template = t(`appDebug.generate.template.${key}.instruction` as any) as string setInstruction(template) setEditorKey(`${flowId}-${Date.now()}`) } @@ -322,7 +322,7 @@ const GetAutomaticRes: FC = ({ ))} diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 1454ab0c62..243aafdbdd 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -295,7 +295,7 @@ const SettingsModal: FC = ({ isExternal rowClass={rowClass} labelClass={labelClass} - t={t} + t={t as any} topK={topK} scoreThreshold={scoreThreshold} scoreThresholdEnabled={scoreThresholdEnabled} @@ -308,7 +308,7 @@ const SettingsModal: FC = ({ isExternal={false} rowClass={rowClass} labelClass={labelClass} - t={t} + t={t as any} indexMethod={indexMethod} retrievalConfig={retrievalConfig} showMultiModalTip={showMultiModalTip} diff --git a/web/app/components/app/log/filter.tsx b/web/app/components/app/log/filter.tsx index 4a0103449f..26c21e6cf6 100644 --- a/web/app/components/app/log/filter.tsx +++ b/web/app/components/app/log/filter.tsx @@ -50,7 +50,7 @@ const Filter: FC = ({ 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(`appLog.filter.period.${v.name}`) }))} + items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}` as any) as string }))} /> = ({ 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(`appLog.filter.period.${v.name}`) }))} + items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}` as any) as string }))} /> = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }) as string, }) return } diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx index 24df08f8a8..3c7fd576a3 100644 --- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx +++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx @@ -3,6 +3,7 @@ import type { ChatItem, Feedback, } from '../types' +import type { Locale } from '@/i18n-config' import type { // AppData, ConversationItem, @@ -93,7 +94,7 @@ export const useEmbeddedChatbot = () => { if (localeParam) { // If locale parameter exists in URL, use it instead of default - await changeLanguage(localeParam) + await changeLanguage(localeParam as Locale) } else if (localeFromSysVar) { // If locale is set as a system variable, use that diff --git a/web/app/components/base/date-and-time-picker/hooks.ts b/web/app/components/base/date-and-time-picker/hooks.ts index f79f28053f..ba66873cc0 100644 --- a/web/app/components/base/date-and-time-picker/hooks.ts +++ b/web/app/components/base/date-and-time-picker/hooks.ts @@ -6,7 +6,7 @@ const YEAR_RANGE = 100 export const useDaysOfWeek = () => { const { t } = useTranslation() - const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => t(`time.daysInWeek.${day}`)) + const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => t(`time.daysInWeek.${day}` as any) as string) return daysOfWeek } @@ -26,7 +26,7 @@ export const useMonths = () => { 'October', 'November', 'December', - ].map(month => t(`time.months.${month}`)) + ].map(month => t(`time.months.${month}` as any) as string) return months } diff --git a/web/app/components/base/encrypted-bottom/index.tsx b/web/app/components/base/encrypted-bottom/index.tsx index ff75d53db6..75b3b8151d 100644 --- a/web/app/components/base/encrypted-bottom/index.tsx +++ b/web/app/components/base/encrypted-bottom/index.tsx @@ -16,7 +16,7 @@ export const EncryptedBottom = (props: Props) => { return (
- {t(frontTextKey || 'common.provider.encrypted.front')} + {t((frontTextKey || 'common.provider.encrypted.front') as any) as string} { > PKCS1_OAEP - {t(backTextKey || 'common.provider.encrypted.back')} + {t((backTextKey || 'common.provider.encrypted.back') as any) as string}
) } diff --git a/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx b/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx index fc1052e172..ca407a69ce 100644 --- a/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx +++ b/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx @@ -97,7 +97,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" > - {languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder} + {languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}` as any) as string : localLanguagePlaceholder} - {t(`common.voice.language.${(item.value).toString().replace('-', '')}`)} + {t(`common.voice.language.${(item.value).toString().replace('-', '')}` as any) as string} {(selected || item.value === text2speech?.language) && ( { handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 }) }, onErrorCallback: (error?: any) => { - const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t) + const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t as any) notify({ type: 'error', message: errorMessage }) handleUpdateFile({ ...uploadingFile, progress: -1 }) }, @@ -287,7 +287,7 @@ export const useFile = (fileConfig: FileUpload, noNeedToCheckEnable = true) => { handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 }) }, onErrorCallback: (error?: any) => { - const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t) + const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t as any) notify({ type: 'error', message: errorMessage }) handleUpdateFile({ ...uploadingFile, progress: -1 }) }, diff --git a/web/app/components/base/form/components/field/input-type-select/hooks.tsx b/web/app/components/base/form/components/field/input-type-select/hooks.tsx index 67621fef67..eb7da8d9d0 100644 --- a/web/app/components/base/form/components/field/input-type-select/hooks.tsx +++ b/web/app/components/base/form/components/field/input-type-select/hooks.tsx @@ -44,7 +44,7 @@ export const useInputTypeOptions = (supportFile: boolean) => { return options.map((value) => { return { value, - label: t(`appDebug.variableConfig.${i18nFileTypeMap[value] || value}`), + label: t(`appDebug.variableConfig.${i18nFileTypeMap[value] || value}` as any), Icon: INPUT_TYPE_ICON[value], type: DATA_TYPE[value], } diff --git a/web/app/components/base/image-uploader/hooks.ts b/web/app/components/base/image-uploader/hooks.ts index 065a808d33..f098c378eb 100644 --- a/web/app/components/base/image-uploader/hooks.ts +++ b/web/app/components/base/image-uploader/hooks.ts @@ -82,7 +82,7 @@ export const useImageFiles = () => { setFiles(newFiles) }, onErrorCallback: (error?: any) => { - const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t) + const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t as any) notify({ type: 'error', message: errorMessage }) const newFiles = [...files.slice(0, index), { ...currentImageFile, progress: -1 }, ...files.slice(index + 1)] filesRef.current = newFiles @@ -160,7 +160,7 @@ export const useLocalFileUploader = ({ limit, disabled = false, onUpload }: useL onUpload({ ...imageFile, fileId: res.id, progress: 100 }) }, onErrorCallback: (error?: any) => { - const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t) + const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t as any) notify({ type: 'error', message: errorMessage }) onUpload({ ...imageFile, progress: -1 }) }, diff --git a/web/app/components/base/tag-input/index.tsx b/web/app/components/base/tag-input/index.tsx index 3241543565..6fe0016a1b 100644 --- a/web/app/components/base/tag-input/index.tsx +++ b/web/app/components/base/tag-input/index.tsx @@ -127,7 +127,7 @@ const TagInput: FC = ({ setValue(e.target.value) }} onKeyDown={handleKeyDown} - placeholder={t(placeholder || (isSpecialMode ? 'common.model.params.stop_sequencesPlaceholder' : 'datasetDocuments.segment.addKeyWord'))} + placeholder={t((placeholder || (isSpecialMode ? 'common.model.params.stop_sequencesPlaceholder' : 'datasetDocuments.segment.addKeyWord')) as any)} />
) diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx index 57c85cf297..345c915c2b 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx @@ -106,7 +106,7 @@ const CloudPlanItem: FC = ({ {ICON_MAP[plan]}
-
{t(`${i18nPrefix}.name`)}
+
{t(`${i18nPrefix}.name` as any) as string}
{ isMostPopularPlan && (
@@ -117,7 +117,7 @@ const CloudPlanItem: FC = ({ ) }
-
{t(`${i18nPrefix}.description`)}
+
{t(`${i18nPrefix}.description` as any) as string}
{/* Price */} diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx index 544141a6a5..73c7f31cb5 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx @@ -42,7 +42,7 @@ const Button = ({ onClick={handleGetPayUrl} >
- {t(`${i18nPrefix}.btnText`)} + {t(`${i18nPrefix}.btnText` as any) as string} {isPremiumPlan && ( diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx index b89d0c6941..a1880af523 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx @@ -85,16 +85,16 @@ const SelfHostedPlanItem: FC = ({
{STYLE_MAP[plan].icon}
-
{t(`${i18nPrefix}.name`)}
-
{t(`${i18nPrefix}.description`)}
+
{t(`${i18nPrefix}.name` as any) as string}
+
{t(`${i18nPrefix}.description` as any) as string}
{/* Price */}
-
{t(`${i18nPrefix}.price`)}
+
{t(`${i18nPrefix}.price` as any) as string}
{!isFreePlan && ( - {t(`${i18nPrefix}.priceTip`)} + {t(`${i18nPrefix}.priceTip` as any) as string} )}
diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx index 4ed307d36e..e7828decb9 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx @@ -12,13 +12,13 @@ const List = ({ }: ListProps) => { const { t } = useTranslation() const i18nPrefix = `billing.plans.${plan}` - const features = t(`${i18nPrefix}.features`, { returnObjects: true }) as string[] + const features = t(`${i18nPrefix}.features` as any, { returnObjects: true }) as unknown as string[] return (
}} />
diff --git a/web/app/components/billing/priority-label/index.tsx b/web/app/components/billing/priority-label/index.tsx index e5a6857078..b66fdc7ea9 100644 --- a/web/app/components/billing/priority-label/index.tsx +++ b/web/app/components/billing/priority-label/index.tsx @@ -31,7 +31,7 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => { return ( -
{`${t('billing.plansCommon.documentProcessingPriority')}: ${t(`billing.plansCommon.priority.${priority}`)}`}
+
{`${t('billing.plansCommon.documentProcessingPriority')}: ${t(`billing.plansCommon.priority.${priority}` as any) as string}`}
{ priority !== DocumentProcessingPriority.topPriority && (
{t('billing.plansCommon.documentProcessingPriorityTip')}
@@ -51,7 +51,7 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => { ) } - {t(`billing.plansCommon.priority.${priority}`)} + {t(`billing.plansCommon.priority.${priority}` as any) as string}
) diff --git a/web/app/components/billing/upgrade-btn/index.tsx b/web/app/components/billing/upgrade-btn/index.tsx index 0f23022b35..2a43090d84 100644 --- a/web/app/components/billing/upgrade-btn/index.tsx +++ b/web/app/components/billing/upgrade-btn/index.tsx @@ -46,8 +46,8 @@ const UpgradeBtn: FC = ({ } } - const defaultBadgeLabel = t(`billing.upgradeBtn.${isShort ? 'encourageShort' : 'encourage'}`) - const label = labelKey ? t(labelKey) : defaultBadgeLabel + const defaultBadgeLabel = t(`billing.upgradeBtn.${isShort ? 'encourageShort' : 'encourage'}` as any) as string + const label = labelKey ? t(labelKey as any) as string : defaultBadgeLabel if (isPlain) { return ( @@ -56,7 +56,7 @@ const UpgradeBtn: FC = ({ style={style} onClick={onClick} > - {labelKey ? label : t('billing.upgradeBtn.plain')} + {labelKey ? label : t('billing.upgradeBtn.plain' as any) as string} ) } diff --git a/web/app/components/custom/custom-web-app-brand/index.tsx b/web/app/components/custom/custom-web-app-brand/index.tsx index 3c6557dc63..6a86441bd4 100644 --- a/web/app/components/custom/custom-web-app-brand/index.tsx +++ b/web/app/components/custom/custom-web-app-brand/index.tsx @@ -68,7 +68,7 @@ const CustomWebAppBrand = () => { setFileId(res.id) }, onErrorCallback: (error?: any) => { - const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t) + const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t as any) notify({ type: 'error', message: errorMessage }) setUploadProgress(-1) }, diff --git a/web/app/components/datasets/common/image-uploader/hooks/use-upload.ts b/web/app/components/datasets/common/image-uploader/hooks/use-upload.ts index 7a0868b14c..44bde33a96 100644 --- a/web/app/components/datasets/common/image-uploader/hooks/use-upload.ts +++ b/web/app/components/datasets/common/image-uploader/hooks/use-upload.ts @@ -145,7 +145,7 @@ export const useUpload = () => { handleUpdateFile({ ...uploadingFile, uploadedId: res.id, progress: 100 }) }, onErrorCallback: (error?: any) => { - const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t) + const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t as any) Toast.notify({ type: 'error', message: errorMessage }) handleUpdateFile({ ...uploadingFile, progress: -1 }) }, @@ -187,7 +187,7 @@ export const useUpload = () => { }) }, onErrorCallback: (error?: any) => { - const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t) + const errorMessage = getFileUploadErrorMessage(error, t('common.fileUploader.uploadFromComputerUploadError'), t as any) Toast.notify({ type: 'error', message: errorMessage }) handleUpdateFile({ ...uploadingFile, progress: -1 }) }, diff --git a/web/app/components/datasets/common/retrieval-method-info/index.tsx b/web/app/components/datasets/common/retrieval-method-info/index.tsx index df8a93f666..3ab33e0278 100644 --- a/web/app/components/datasets/common/retrieval-method-info/index.tsx +++ b/web/app/components/datasets/common/retrieval-method-info/index.tsx @@ -33,8 +33,8 @@ const EconomicalRetrievalMethodConfig: FC = ({
- {t(`dataset.chunkingMode.${DOC_FORM_TEXT[chunkStructure]}`)} + {t(`dataset.chunkingMode.${DOC_FORM_TEXT[chunkStructure]}` as any) as string}
diff --git a/web/app/components/datasets/create/embedding-process/index.tsx b/web/app/components/datasets/create/embedding-process/index.tsx index 541eb62b60..d2050268aa 100644 --- a/web/app/components/datasets/create/embedding-process/index.tsx +++ b/web/app/components/datasets/create/embedding-process/index.tsx @@ -141,7 +141,7 @@ const RuleDetail: FC<{ { - const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t) + const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t as any) notify({ type: 'error', message: errorMessage }) onFileUpdate(fileItem, -2, fileListRef.current) return Promise.resolve({ ...fileItem }) diff --git a/web/app/components/datasets/create/top-bar/index.tsx b/web/app/components/datasets/create/top-bar/index.tsx index 3f30d9a8da..632ad3ab73 100644 --- a/web/app/components/datasets/create/top-bar/index.tsx +++ b/web/app/components/datasets/create/top-bar/index.tsx @@ -39,7 +39,7 @@ export const TopBar: FC = (props) => {
({ - name: t(STEP_T_MAP[i + 1]), + name: t(STEP_T_MAP[i + 1] as any) as string, }))} {...rest} /> diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx index 31570ef4cf..c36155e104 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx @@ -155,7 +155,7 @@ const LocalFile = ({ return Promise.resolve({ ...completeFile }) }) .catch((e) => { - const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t) + const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t as any) notify({ type: 'error', message: errorMessage }) updateFile(fileItem, -2, fileListRef.current) return Promise.resolve({ ...fileItem }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx index a16e284bcf..55590636a6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx @@ -63,7 +63,7 @@ const RuleDetail = ({ /> = ({ return Promise.resolve({ ...completeFile }) }) .catch((e) => { - const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t) + const errorMessage = getFileUploadErrorMessage(e, t('datasetCreation.stepOne.uploader.failed'), t as any) notify({ type: 'error', message: errorMessage }) const errorFile = { ...fileItem, diff --git a/web/app/components/datasets/documents/detail/embedding/index.tsx b/web/app/components/datasets/documents/detail/embedding/index.tsx index db83d89c40..2978ec5681 100644 --- a/web/app/components/datasets/documents/detail/embedding/index.tsx +++ b/web/app/components/datasets/documents/detail/embedding/index.tsx @@ -133,7 +133,7 @@ const RuleDetail: FC = React.memo(({ /> {icon} -
{t(`dataset.retrieval.${retrievalMethod}.title`)}
+
{t(`dataset.retrieval.${retrievalMethod}.title` as any) as string}
)} diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx index 8087b80fda..e72349db3a 100644 --- a/web/app/components/datasets/list/dataset-card/index.tsx +++ b/web/app/components/datasets/list/dataset-card/index.tsx @@ -217,17 +217,17 @@ const DatasetCard = ({ {dataset.doc_form && ( - {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)} + {t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}` as any) as string} )} {dataset.indexing_technique && ( - {formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)} + {formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method) as any} )} {dataset.is_multimodal && ( diff --git a/web/app/components/explore/category.tsx b/web/app/components/explore/category.tsx index eba883d849..593ed8d938 100644 --- a/web/app/components/explore/category.tsx +++ b/web/app/components/explore/category.tsx @@ -50,7 +50,7 @@ const Category: FC = ({ className={itemClassName(name === value)} onClick={() => onChange(name)} > - {(categoryI18n as any)[name] ? t(`explore.category.${name}`) : name} + {(categoryI18n as any)[name] ? t(`explore.category.${name}` as any) as string : name}
))} diff --git a/web/app/components/goto-anything/actions/commands/theme.tsx b/web/app/components/goto-anything/actions/commands/theme.tsx index dc8ca46bc0..34df84e33b 100644 --- a/web/app/components/goto-anything/actions/commands/theme.tsx +++ b/web/app/components/goto-anything/actions/commands/theme.tsx @@ -36,13 +36,13 @@ const buildThemeCommands = (query: string, locale?: string): CommandSearchResult const q = query.toLowerCase() const list = THEME_ITEMS.filter(item => !q - || i18n.t(item.titleKey, { lng: locale }).toLowerCase().includes(q) + || i18n.t(item.titleKey as any, { lng: locale }).toLowerCase().includes(q) || item.id.includes(q), ) return list.map(item => ({ id: item.id, - title: i18n.t(item.titleKey, { lng: locale }), - description: i18n.t(item.descKey, { lng: locale }), + title: i18n.t(item.titleKey as any, { lng: locale }), + description: i18n.t(item.descKey as any, { lng: locale }), type: 'command' as const, icon: (
diff --git a/web/app/components/goto-anything/command-selector.tsx b/web/app/components/goto-anything/command-selector.tsx index 0d89669ec8..f62a7a3829 100644 --- a/web/app/components/goto-anything/command-selector.tsx +++ b/web/app/components/goto-anything/command-selector.tsx @@ -117,7 +117,7 @@ const CommandSelector: FC = ({ actions, onCommandSelect, searchFilter, co '/community': 'app.gotoAnything.actions.communityDesc', '/zen': 'app.gotoAnything.actions.zenDesc', } - return t(slashKeyMap[item.key] || item.description) + return t((slashKeyMap[item.key] || item.description) as any) })() ) : ( @@ -128,7 +128,7 @@ const CommandSelector: FC = ({ actions, onCommandSelect, searchFilter, co '@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc', '@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc', } - return t(keyMap[item.key]) + return t(keyMap[item.key] as any) as string })() )} diff --git a/web/app/components/goto-anything/index.tsx b/web/app/components/goto-anything/index.tsx index 1c61cb9516..76c2e26ebd 100644 --- a/web/app/components/goto-anything/index.tsx +++ b/web/app/components/goto-anything/index.tsx @@ -243,7 +243,7 @@ const GotoAnything: FC = ({ knowledge: 'app.gotoAnything.emptyState.noKnowledgeBasesFound', node: 'app.gotoAnything.emptyState.noWorkflowNodesFound', } - return t(keyMap[commandType] || 'app.gotoAnything.noResults') + return t((keyMap[commandType] || 'app.gotoAnything.noResults') as any) })() : t('app.gotoAnything.noResults')}
@@ -410,7 +410,7 @@ const GotoAnything: FC = ({ 'workflow-node': 'app.gotoAnything.groups.workflowNodes', 'command': 'app.gotoAnything.groups.commands', } - return t(typeMap[type] || `${type}s`) + return t((typeMap[type] || `${type}s`) as any) })()} className="p-2 capitalize text-text-secondary" > diff --git a/web/app/components/header/account-setting/language-page/index.tsx b/web/app/components/header/account-setting/language-page/index.tsx index 00db4dbbaf..0568cb1ec9 100644 --- a/web/app/components/header/account-setting/language-page/index.tsx +++ b/web/app/components/header/account-setting/language-page/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { Item } from '@/app/components/base/select' +import type { Locale } from '@/i18n-config' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' @@ -32,7 +33,7 @@ export default function LanguagePage() { await updateUserProfile({ url, body: { [bodyKey]: item.value } }) notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - setLocaleOnClient(item.value.toString()) + setLocaleOnClient(item.value.toString() as Locale) } catch (e) { notify({ type: 'error', message: (e as Error).message }) diff --git a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx index 7d8169e4c4..07f89df24d 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx @@ -36,7 +36,7 @@ const RoleSelector = ({ value, onChange }: RoleSelectorProps) => { className="block" >
-
{t('common.members.invitedAsRole', { role: t(`common.members.${toHump(value)}`) })}
+
{t('common.members.invitedAsRole', { role: t(`common.members.${toHump(value)}` as any) })}
diff --git a/web/app/components/header/account-setting/members-page/operation/index.tsx b/web/app/components/header/account-setting/members-page/operation/index.tsx index 2b3c9e350a..da61746685 100644 --- a/web/app/components/header/account-setting/members-page/operation/index.tsx +++ b/web/app/components/header/account-setting/members-page/operation/index.tsx @@ -106,8 +106,8 @@ const Operation = ({ :
}
-
{t(`common.members.${toHump(role)}`)}
-
{t(`common.members.${toHump(role)}Tip`)}
+
{t(`common.members.${toHump(role)}` as any)}
+
{t(`common.members.${toHump(role)}Tip` as any)}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx index d53467028c..9e8637ce49 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx @@ -44,7 +44,7 @@ const PresetsParameter: FC = ({ text: (
{getToneIcon(tone.id)} - {t(`common.model.tone.${tone.name}`) as string} + {t(`common.model.tone.${tone.name}` as any) as string}
), } diff --git a/web/app/components/plugins/base/deprecation-notice.tsx b/web/app/components/plugins/base/deprecation-notice.tsx index 8832c77961..76d64a45f4 100644 --- a/web/app/components/plugins/base/deprecation-notice.tsx +++ b/web/app/components/plugins/base/deprecation-notice.tsx @@ -82,7 +82,7 @@ const DeprecationNotice: FC = ({ ), }} values={{ - deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`), + deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}` as any) as string, alternativePluginId, }} /> @@ -91,7 +91,7 @@ const DeprecationNotice: FC = ({ { hasValidDeprecatedReason && !alternativePluginId && ( - {t(`${i18nPrefix}.onlyReason`, { deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`) })} + {t(`${i18nPrefix}.onlyReason` as any, { deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}` as any) as string }) as string} ) } diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx index 1cb15bf70b..f063a8d572 100644 --- a/web/app/components/plugins/card/index.tsx +++ b/web/app/components/plugins/card/index.tsx @@ -1,11 +1,14 @@ 'use client' import type { Plugin } from '../types' +import type { Locale } from '@/i18n-config' import { RiAlertFill } from '@remixicon/react' import * as React from 'react' import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks' import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' -import { renderI18nObject } from '@/i18n-config' +import { + renderI18nObject, +} from '@/i18n-config' import { getLanguage } from '@/i18n-config/language' import { Theme } from '@/types/app' import { cn } from '@/utils/classnames' @@ -30,7 +33,7 @@ export type Props = { footer?: React.ReactNode isLoading?: boolean loadingFileName?: string - locale?: string + locale?: Locale limitedInstall?: boolean } diff --git a/web/app/components/plugins/hooks.ts b/web/app/components/plugins/hooks.ts index 8303a4cc46..423ce1023c 100644 --- a/web/app/components/plugins/hooks.ts +++ b/web/app/components/plugins/hooks.ts @@ -20,7 +20,7 @@ export const useTags = (translateFromOut?: TFunction) => { return tagKeys.map((tag) => { return { name: tag, - label: t(`pluginTags.tags.${tag}`), + label: t(`pluginTags.tags.${tag}` as any) as string, } }) }, [t]) @@ -66,14 +66,14 @@ export const useCategories = (translateFromOut?: TFunction, isSingle?: boolean) } return { name: category, - label: isSingle ? t(`plugin.categorySingle.${category}`) : t(`plugin.category.${category}s`), + label: isSingle ? t(`plugin.categorySingle.${category}` as any) as string : t(`plugin.category.${category}s` as any) as string, } }) }, [t, isSingle]) const categoriesMap = useMemo(() => { return categories.reduce((acc, category) => { - acc[category.name] = category + acc[category.name] = category as any return acc }, {} as Record) }, [categories]) diff --git a/web/app/components/plugins/marketplace/description/index.tsx b/web/app/components/plugins/marketplace/description/index.tsx index 5b1ac6bb09..66a6368c08 100644 --- a/web/app/components/plugins/marketplace/description/index.tsx +++ b/web/app/components/plugins/marketplace/description/index.tsx @@ -1,10 +1,11 @@ +import type { Locale } from '@/i18n-config' import { getLocaleOnServer, - useTranslation as translate, + getTranslation as translate, } from '@/i18n-config/server' type DescriptionProps = { - locale?: string + locale?: Locale } const Description = async ({ locale: localeFromProps, diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 47acb840e4..ff9a4d60bc 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -1,5 +1,6 @@ import type { MarketplaceCollection, SearchParams } from './types' import type { Plugin } from '@/app/components/plugins/types' +import type { Locale } from '@/i18n-config' import { TanstackQueryInitializer } from '@/context/query-client' import { MarketplaceContextProvider } from './context' import Description from './description' @@ -8,7 +9,7 @@ import StickySearchAndSwitchWrapper from './sticky-search-and-switch-wrapper' import { getMarketplaceCollectionsAndPlugins } from './utils' type MarketplaceProps = { - locale: string + locale: Locale showInstallButton?: boolean shouldExclude?: boolean searchParams?: SearchParams diff --git a/web/app/components/plugins/marketplace/list/card-wrapper.tsx b/web/app/components/plugins/marketplace/list/card-wrapper.tsx index 159107eb97..ddc505b0d8 100644 --- a/web/app/components/plugins/marketplace/list/card-wrapper.tsx +++ b/web/app/components/plugins/marketplace/list/card-wrapper.tsx @@ -1,5 +1,6 @@ 'use client' import type { Plugin } from '@/app/components/plugins/types' +import type { Locale } from '@/i18n-config' import { RiArrowRightUpLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useTheme } from 'next-themes' @@ -17,7 +18,7 @@ import { getPluginDetailLinkInMarketplace, getPluginLinkInMarketplace } from '.. type CardWrapperProps = { plugin: Plugin showInstallButton?: boolean - locale?: string + locale?: Locale } const CardWrapperComponent = ({ plugin, diff --git a/web/app/components/plugins/marketplace/list/index.tsx b/web/app/components/plugins/marketplace/list/index.tsx index 95f7cb37a8..54889b232f 100644 --- a/web/app/components/plugins/marketplace/list/index.tsx +++ b/web/app/components/plugins/marketplace/list/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { Plugin } from '../../types' import type { MarketplaceCollection } from '../types' +import type { Locale } from '@/i18n-config' import { cn } from '@/utils/classnames' import Empty from '../empty' import CardWrapper from './card-wrapper' @@ -11,7 +12,7 @@ type ListProps = { marketplaceCollectionPluginsMap: Record plugins?: Plugin[] showInstallButton?: boolean - locale: string + locale: Locale cardContainerClassName?: string cardRender?: (plugin: Plugin) => React.JSX.Element | null onMoreClick?: () => void diff --git a/web/app/components/plugins/marketplace/list/list-with-collection.tsx b/web/app/components/plugins/marketplace/list/list-with-collection.tsx index c401fbe3b9..2d246efb82 100644 --- a/web/app/components/plugins/marketplace/list/list-with-collection.tsx +++ b/web/app/components/plugins/marketplace/list/list-with-collection.tsx @@ -3,6 +3,7 @@ import type { MarketplaceCollection } from '../types' import type { SearchParamsFromCollection } from '@/app/components/plugins/marketplace/types' import type { Plugin } from '@/app/components/plugins/types' +import type { Locale } from '@/i18n-config' import { RiArrowRightSLine } from '@remixicon/react' import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks' import { getLanguage } from '@/i18n-config/language' @@ -13,7 +14,7 @@ type ListWithCollectionProps = { marketplaceCollections: MarketplaceCollection[] marketplaceCollectionPluginsMap: Record showInstallButton?: boolean - locale: string + locale: Locale cardContainerClassName?: string cardRender?: (plugin: Plugin) => React.JSX.Element | null onMoreClick?: (searchParams?: SearchParamsFromCollection) => void diff --git a/web/app/components/plugins/marketplace/list/list-wrapper.tsx b/web/app/components/plugins/marketplace/list/list-wrapper.tsx index f2fbd085f0..650c8a7447 100644 --- a/web/app/components/plugins/marketplace/list/list-wrapper.tsx +++ b/web/app/components/plugins/marketplace/list/list-wrapper.tsx @@ -1,6 +1,7 @@ 'use client' import type { Plugin } from '../../types' import type { MarketplaceCollection } from '../types' +import type { Locale } from '@/i18n-config' import { useEffect } from 'react' import Loading from '@/app/components/base/loading' import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks' @@ -12,7 +13,7 @@ type ListWrapperProps = { marketplaceCollections: MarketplaceCollection[] marketplaceCollectionPluginsMap: Record showInstallButton?: boolean - locale: string + locale: Locale } const ListWrapper = ({ marketplaceCollections, diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx index 16a789e67b..31e0bd6a85 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx @@ -330,7 +330,7 @@ export const CommonCreateModal = ({ onClose, createType, builder }: Props) => { return ( = MAX_COUNT ? t('pluginTrigger.subscription.maxCount', { num: MAX_COUNT }) : t(`pluginTrigger.subscription.addType.options.${methodType.toLowerCase()}.description`)} + popupContent={subscriptionCount >= MAX_COUNT ? t('pluginTrigger.subscription.maxCount', { num: MAX_COUNT }) : t(`pluginTrigger.subscription.addType.options.${methodType.toLowerCase()}.description` as any)} disabled={!(supportedMethods?.length === 1 || subscriptionCount >= MAX_COUNT)} > - brief: Record - description: Record + label: Partial> + brief: Partial> + description: Partial> // Repo readme.md content introduction: string repository: string diff --git a/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts b/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts index 6464534c83..45c36196f5 100644 --- a/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts @@ -36,8 +36,8 @@ export const useAvailableNodesMetaData = () => { const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => { const { metaData } = node - const title = t(`workflow.blocks.${metaData.type}`) - const description = t(`workflow.blocksAbout.${metaData.type}`) + const title = t(`workflow.blocks.${metaData.type}` as any) as string + const description = t(`workflow.blocksAbout.${metaData.type}` as any) as string return { ...node, metaData: { diff --git a/web/app/components/rag-pipeline/hooks/use-pipeline-template.ts b/web/app/components/rag-pipeline/hooks/use-pipeline-template.ts index 5955ee5c45..fc64e6c8c7 100644 --- a/web/app/components/rag-pipeline/hooks/use-pipeline-template.ts +++ b/web/app/components/rag-pipeline/hooks/use-pipeline-template.ts @@ -14,7 +14,7 @@ export const usePipelineTemplate = () => { data: { ...knowledgeBaseDefault.defaultValue as KnowledgeBaseNodeType, type: knowledgeBaseDefault.metaData.type, - title: t(`workflow.blocks.${knowledgeBaseDefault.metaData.type}`), + title: t(`workflow.blocks.${knowledgeBaseDefault.metaData.type}` as any) as string, selected: true, }, position: { diff --git a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx index 4ecee282f9..5c85aee32c 100644 --- a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx @@ -111,7 +111,7 @@ const GetSchema: FC = ({ }} className="system-sm-regular cursor-pointer whitespace-nowrap rounded-lg px-3 py-1.5 leading-5 text-text-secondary hover:bg-components-panel-on-panel-item-bg-hover" > - {t(`tools.createTool.exampleOptions.${item.key}`)} + {t(`tools.createTool.exampleOptions.${item.key}` as any) as string} ))} diff --git a/web/app/components/tools/edit-custom-collection-modal/index.tsx b/web/app/components/tools/edit-custom-collection-modal/index.tsx index 93ef9142d9..474c262010 100644 --- a/web/app/components/tools/edit-custom-collection-modal/index.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/index.tsx @@ -292,7 +292,7 @@ const EditCustomCollectionModal: FC = ({
{t('tools.createTool.authMethod.title')}
setCredentialsModalShow(true)}> -
{t(`tools.createTool.authMethod.types.${credential.auth_type}`)}
+
{t(`tools.createTool.authMethod.types.${credential.auth_type}` as any) as string}
diff --git a/web/app/components/tools/edit-custom-collection-modal/test-api.tsx b/web/app/components/tools/edit-custom-collection-modal/test-api.tsx index 8aacb7ad07..30ead4425b 100644 --- a/web/app/components/tools/edit-custom-collection-modal/test-api.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/test-api.tsx @@ -78,7 +78,7 @@ const TestApi: FC = ({
{t('tools.createTool.authMethod.title')}
setCredentialsModalShow(true)}> -
{t(`tools.createTool.authMethod.types.${tempCredential.auth_type}`)}
+
{t(`tools.createTool.authMethod.types.${tempCredential.auth_type}` as any) as string}
diff --git a/web/app/components/tools/provider/empty.tsx b/web/app/components/tools/provider/empty.tsx index 7e916ba62f..e79607751e 100644 --- a/web/app/components/tools/provider/empty.tsx +++ b/web/app/components/tools/provider/empty.tsx @@ -33,17 +33,17 @@ const Empty = ({ const Comp = (hasLink ? Link : 'div') as any const linkProps = hasLink ? { href: getLink(type), target: '_blank' } : {} const renderType = isAgent ? 'agent' : type - const hasTitle = t(`tools.addToolModal.${renderType}.title`) !== `tools.addToolModal.${renderType}.title` + const hasTitle = t(`tools.addToolModal.${renderType}.title` as any) as string !== `tools.addToolModal.${renderType}.title` return (
- {hasTitle ? t(`tools.addToolModal.${renderType}.title`) : 'No tools available'} + {hasTitle ? t(`tools.addToolModal.${renderType}.title` as any) as string : 'No tools available'}
{(!isAgent && hasTitle) && ( - {t(`tools.addToolModal.${renderType}.tip`)} + {t(`tools.addToolModal.${renderType}.tip` as any) as string} {' '} {hasLink && } diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts index 24d862321d..7f08612234 100644 --- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts @@ -42,8 +42,8 @@ export const useAvailableNodesMetaData = () => { const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => { const { metaData } = node - const title = t(`workflow.blocks.${metaData.type}`) - const description = t(`workflow.blocksAbout.${metaData.type}`) + const title = t(`workflow.blocks.${metaData.type}` as any) as string + const description = t(`workflow.blocksAbout.${metaData.type}` as any) as string const helpLinkPath = `guides/workflow/node/${metaData.helpLinkUri}` return { ...node, diff --git a/web/app/components/workflow-app/hooks/use-workflow-template.ts b/web/app/components/workflow-app/hooks/use-workflow-template.ts index b48a68e1eb..46f2c3e0a3 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-template.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-template.ts @@ -18,7 +18,7 @@ export const useWorkflowTemplate = () => { data: { ...startDefault.defaultValue as StartNodeType, type: startDefault.metaData.type, - title: t(`workflow.blocks.${startDefault.metaData.type}`), + title: t(`workflow.blocks.${startDefault.metaData.type}` as any) as string, }, position: START_INITIAL_POSITION, }) @@ -34,7 +34,7 @@ export const useWorkflowTemplate = () => { }, selected: true, type: llmDefault.metaData.type, - title: t(`workflow.blocks.${llmDefault.metaData.type}`), + title: t(`workflow.blocks.${llmDefault.metaData.type}` as any) as string, }, position: { x: START_INITIAL_POSITION.x + NODE_WIDTH_X_OFFSET, @@ -48,7 +48,7 @@ export const useWorkflowTemplate = () => { ...answerDefault.defaultValue, answer: `{{#${llmNode.id}.text#}}`, type: answerDefault.metaData.type, - title: t(`workflow.blocks.${answerDefault.metaData.type}`), + title: t(`workflow.blocks.${answerDefault.metaData.type}` as any) as string, }, position: { x: START_INITIAL_POSITION.x + NODE_WIDTH_X_OFFSET * 2, diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx index e626073a3a..51d08d15df 100644 --- a/web/app/components/workflow/block-selector/blocks.tsx +++ b/web/app/components/workflow/block-selector/blocks.tsx @@ -85,7 +85,7 @@ const Blocks = ({ { classification !== '-' && !!filteredList.length && (
- {t(`workflow.tabs.${classification}`)} + {t(`workflow.tabs.${classification}` as any) as string}
) } diff --git a/web/app/components/workflow/block-selector/featured-tools.tsx b/web/app/components/workflow/block-selector/featured-tools.tsx index 1e6b976739..343d56bd1a 100644 --- a/web/app/components/workflow/block-selector/featured-tools.tsx +++ b/web/app/components/workflow/block-selector/featured-tools.tsx @@ -2,6 +2,7 @@ import type { ToolWithProvider } from '../types' import type { ToolDefaultValue, ToolValue } from './types' import type { Plugin } from '@/app/components/plugins/types' +import type { Locale } from '@/i18n-config' import { RiMoreLine } from '@remixicon/react' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' @@ -178,7 +179,7 @@ const FeaturedTools = ({ onInstallSuccess={async () => { await onInstallSuccess?.() }} - t={t} + t={t as any} /> ))}
@@ -221,7 +222,7 @@ const FeaturedTools = ({ type FeaturedToolUninstalledItemProps = { plugin: Plugin - language: string + language: Locale onInstallSuccess?: () => Promise | void t: (key: string, options?: Record) => string } diff --git a/web/app/components/workflow/block-selector/featured-triggers.tsx b/web/app/components/workflow/block-selector/featured-triggers.tsx index c986a8abc0..66705a9d06 100644 --- a/web/app/components/workflow/block-selector/featured-triggers.tsx +++ b/web/app/components/workflow/block-selector/featured-triggers.tsx @@ -1,6 +1,7 @@ 'use client' import type { TriggerDefaultValue, TriggerWithProvider } from './types' import type { Plugin } from '@/app/components/plugins/types' +import type { Locale } from '@/i18n-config' import { RiMoreLine } from '@remixicon/react' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' @@ -170,7 +171,7 @@ const FeaturedTriggers = ({ onInstallSuccess={async () => { await onInstallSuccess?.() }} - t={t} + t={t as any} /> ))} @@ -213,7 +214,7 @@ const FeaturedTriggers = ({ type FeaturedTriggerUninstalledItemProps = { plugin: Plugin - language: string + language: Locale onInstallSuccess?: () => Promise | void t: (key: string, options?: Record) => string } diff --git a/web/app/components/workflow/block-selector/hooks.ts b/web/app/components/workflow/block-selector/hooks.ts index 075a0b7d38..462d58df9f 100644 --- a/web/app/components/workflow/block-selector/hooks.ts +++ b/web/app/components/workflow/block-selector/hooks.ts @@ -17,7 +17,7 @@ export const useBlocks = () => { return BLOCKS.map((block) => { return { ...block, - title: t(`workflow.blocks.${block.type}`), + title: t(`workflow.blocks.${block.type}` as any) as string, } }) } @@ -28,7 +28,7 @@ export const useStartBlocks = () => { return START_BLOCKS.map((block) => { return { ...block, - title: t(`workflow.blocks.${block.type}`), + title: t(`workflow.blocks.${block.type}` as any) as string, } }) } diff --git a/web/app/components/workflow/block-selector/start-blocks.tsx b/web/app/components/workflow/block-selector/start-blocks.tsx index 5c4311b805..b6aaef84f9 100644 --- a/web/app/components/workflow/block-selector/start-blocks.tsx +++ b/web/app/components/workflow/block-selector/start-blocks.tsx @@ -43,7 +43,7 @@ const StartBlocks = ({ if (blockType === BlockEnumValues.TriggerWebhook) return t('workflow.customWebhook') - return t(`workflow.blocks.${blockType}`) + return t(`workflow.blocks.${blockType}` as any) as string } return START_BLOCKS.filter((block) => { @@ -83,10 +83,10 @@ const StartBlocks = ({
{block.type === BlockEnumValues.TriggerWebhook ? t('workflow.customWebhook') - : t(`workflow.blocks.${block.type}`)} + : t(`workflow.blocks.${block.type}` as any) as string}
- {t(`workflow.blocksAbout.${block.type}`)} + {t(`workflow.blocksAbout.${block.type}` as any) as string}
{(block.type === BlockEnumValues.TriggerWebhook || block.type === BlockEnumValues.TriggerSchedule) && (
@@ -107,7 +107,7 @@ const StartBlocks = ({ type={block.type} />
- {t(`workflow.blocks.${block.type}`)} + {t(`workflow.blocks.${block.type}` as any) as string} {block.type === BlockEnumValues.Start && ( {t('workflow.blocks.originalStartNode')} )} diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index 6e49bfa0be..7cead40705 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -237,8 +237,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => { list.push({ id: `${type}-need-added`, type, - title: t(`workflow.blocks.${type}`), - errorMessage: t('workflow.common.needAdd', { node: t(`workflow.blocks.${type}`) }), + title: t(`workflow.blocks.${type}` as any) as string, + errorMessage: t('workflow.common.needAdd', { node: t(`workflow.blocks.${type}` as any) as string }), canNavigate: false, }) } @@ -409,7 +409,7 @@ export const useChecklistBeforePublish = () => { const type = isRequiredNodesType[i] if (!filteredNodes.find(node => node.data.type === type)) { - notify({ type: 'error', message: t('workflow.common.needAdd', { node: t(`workflow.blocks.${type}`) }) }) + notify({ type: 'error', message: t('workflow.common.needAdd', { node: t(`workflow.blocks.${type}` as any) as string }) }) return false } } diff --git a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx index 44df18ddf2..91f89b147e 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx @@ -44,7 +44,7 @@ const OutputVarList: FC = ({ if (!isValid) { setToastHandler(Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }), })) return } diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index 2d96baaf28..7b548c6afc 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -57,7 +57,7 @@ const VarList: FC = ({ if (!isValid) { setToastHandle(Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }), })) return } diff --git a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx index fd346e632d..0cc905ae06 100644 --- a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx +++ b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx @@ -72,7 +72,7 @@ const OperationSelector: FC = ({ className={`system-sm-regular overflow-hidden truncate text-ellipsis ${selectedItem ? 'text-components-input-text-filled' : 'text-components-input-text-disabled'}`} > - {selectedItem?.name ? t(`${i18nPrefix}.operations.${selectedItem?.name}`) : t(`${i18nPrefix}.operations.title`)} + {selectedItem?.name ? t(`${i18nPrefix}.operations.${selectedItem?.name}` as any) as string : t(`${i18nPrefix}.operations.title` as any) as string}
@@ -83,7 +83,7 @@ const OperationSelector: FC = ({
-
{t(`${i18nPrefix}.operations.title`)}
+
{t(`${i18nPrefix}.operations.title` as any) as string}
{items.map(item => ( item.value === 'divider' @@ -100,7 +100,7 @@ const OperationSelector: FC = ({ }} >
- {t(`${i18nPrefix}.operations.${item.name}`)} + {t(`${i18nPrefix}.operations.${item.name}` as any) as string}
{item.value === value && (
diff --git a/web/app/components/workflow/nodes/assigner/node.tsx b/web/app/components/workflow/nodes/assigner/node.tsx index be30104242..3eb9f0d620 100644 --- a/web/app/components/workflow/nodes/assigner/node.tsx +++ b/web/app/components/workflow/nodes/assigner/node.tsx @@ -73,7 +73,7 @@ const NodeComponent: FC> = ({ nodeType={node?.data.type} nodeTitle={node?.data.title} rightSlot={ - writeMode && + writeMode && } />
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx index 53df68c337..0bb2245c71 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx @@ -34,7 +34,7 @@ const ConditionValue = ({ const variableSelector = variable_selector as ValueSelector - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}` as any) as string : operator const formatValue = useCallback((c: Condition) => { const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator) if (notHasValue) @@ -59,7 +59,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? (t(`workflow.nodes.ifElse.optionName.${name.i18nKey}` as any) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -91,9 +91,9 @@ const ConditionValue = ({ sub_variable_condition?.conditions.map((c: Condition, index) => (
{c.key}
-
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}
+
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any) as string : c.comparison_operator}
{c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) &&
{isSelect(c) ? selectName(c) : formatValue(c)}
} - {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}
)} + {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any) as string}
)}
)) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index b323e65066..f49b8ef2fc 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -168,13 +168,13 @@ const ConditionItem = ({ if (isSelect) { if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } if (fileAttr?.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx index e2753ba6e7..64c6d35d8f 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx @@ -39,7 +39,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(varType, file).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`) : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any) as string : o, value: o, } }) @@ -65,7 +65,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select`) + : t(`${i18nPrefix}.select` as any) as string } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx index 376c3a670f..c0cd78e4c5 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx @@ -35,7 +35,7 @@ const ConditionValue = ({ const { t } = useTranslation() const nodes = useNodes() const variableName = labelName || (isSystemVar(variableSelector) ? variableSelector.slice(0).join('.') : variableSelector.slice(1).join('.')) - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}` as any) as string : operator const notHasValue = comparisonOperatorNotRequireValue(operator) const node: Node | undefined = nodes.find(n => n.id === variableSelector[0]) as Node const isException = isExceptionVariable(variableName, node?.data.type) @@ -63,7 +63,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? (t(`workflow.nodes.ifElse.optionName.${name.i18nKey}` as any) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` diff --git a/web/app/components/workflow/nodes/iteration/use-interactions.ts b/web/app/components/workflow/nodes/iteration/use-interactions.ts index c6fddff5ad..87a4b8ad5d 100644 --- a/web/app/components/workflow/nodes/iteration/use-interactions.ts +++ b/web/app/components/workflow/nodes/iteration/use-interactions.ts @@ -135,7 +135,7 @@ export const useNodeIterationInteractions = () => { _isBundled: false, _connectedSourceHandleIds: [], _connectedTargetHandleIds: [], - title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${childNodeType}`)} ${childNodeTypeCount[childNodeType]}` : t(`workflow.blocks.${childNodeType}`), + title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${childNodeType}` as any) as string} ${childNodeTypeCount[childNodeType]}` : t(`workflow.blocks.${childNodeType}` as any) as string, iteration_id: newNodeId, type: childNodeType, }, diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx index b3f2701524..5c764cba28 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx @@ -121,7 +121,7 @@ const DatasetItem: FC = ({ payload.provider === 'external' && ( ) } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx index 8f0430b655..d248d96dc0 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx @@ -42,7 +42,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(variableType).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`) : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any) as string : o, value: o, } }) @@ -68,7 +68,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select`) + : t(`${i18nPrefix}.select` as any) as string } diff --git a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx index 8dd817a5ad..1ca931d32c 100644 --- a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx @@ -66,13 +66,13 @@ const FilterCondition: FC = ({ if (isSelect) { if (condition.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } if (condition.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx index 776ad6804c..64d7a24a53 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx @@ -121,7 +121,7 @@ const ConfigPromptItem: FC = ({ {t(`${i18nPrefix}.roleDescription.${payload.role}`)}
+
{t(`${i18nPrefix}.roleDescription.${payload.role}` as any) as string}
} triggerClassName="w-4 h-4" /> diff --git a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx index e13832ed46..dfe16902fd 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx @@ -34,7 +34,7 @@ const ConditionValue = ({ const variableSelector = variable_selector as ValueSelector - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}` as any) as string : operator const formatValue = useCallback((c: Condition) => { const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator) if (notHasValue) @@ -59,7 +59,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? (t(`workflow.nodes.ifElse.optionName.${name.i18nKey}` as any) as string).replace(/\{\{#([^#]*)#\}\}/g, (a: string, b: string) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -91,9 +91,9 @@ const ConditionValue = ({ sub_variable_condition?.conditions.map((c: Condition, index) => (
{c.key}
-
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}
+
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any) as string : c.comparison_operator}
{c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) &&
{isSelect(c) ? selectName(c) : formatValue(c)}
} - {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}
)} + {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any) as string}
)}
)) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx index ea3e2ef5be..95e7b58dd0 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx @@ -145,13 +145,13 @@ const ConditionItem = ({ if (isSelect) { if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } if (fileAttr?.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}`), + name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any) as string, value: item.value, })) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx index a33b2b7727..9943109c2b 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx @@ -39,7 +39,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(varType, file).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`) : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any) as string : o, value: o, } }) @@ -65,7 +65,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select`) + : t(`${i18nPrefix}.select` as any) as string } diff --git a/web/app/components/workflow/nodes/loop/components/condition-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-value.tsx index c24a1a18a6..10fa2cef42 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-value.tsx @@ -27,7 +27,7 @@ const ConditionValue = ({ value, }: ConditionValueProps) => { const { t } = useTranslation() - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}` as any) as string : operator const notHasValue = comparisonOperatorNotRequireValue(operator) const formatValue = useMemo(() => { if (notHasValue) @@ -50,7 +50,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? (t(`workflow.nodes.ifElse.optionName.${name.i18nKey}` as any) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx index 973e78ae73..949c53ca97 100644 --- a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx +++ b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx @@ -30,7 +30,7 @@ const Item = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: t('workflow.env.modal.name') }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('workflow.env.modal.name') }) as string, }) return false } diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx index 288e486ea7..61921296d4 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx @@ -56,7 +56,7 @@ const AddExtractParameter: FC = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }) as string, }) return } diff --git a/web/app/components/workflow/nodes/start/components/var-list.tsx b/web/app/components/workflow/nodes/start/components/var-list.tsx index bda45ca5dd..4d3c6cd871 100644 --- a/web/app/components/workflow/nodes/start/components/var-list.tsx +++ b/web/app/components/workflow/nodes/start/components/var-list.tsx @@ -45,7 +45,7 @@ const VarList: FC = ({ if (errorMsgKey) { Toast.notify({ type: 'error', - message: t(errorMsgKey, { key: t(typeName) }), + message: t(errorMsgKey as any, { key: t(typeName as any) as string }) as string, }) return false } diff --git a/web/app/components/workflow/nodes/start/use-config.ts b/web/app/components/workflow/nodes/start/use-config.ts index 8eed650f98..e563e710ce 100644 --- a/web/app/components/workflow/nodes/start/use-config.ts +++ b/web/app/components/workflow/nodes/start/use-config.ts @@ -99,7 +99,7 @@ const useConfig = (id: string, payload: StartNodeType) => { if (errorMsgKey) { Toast.notify({ type: 'error', - message: t(errorMsgKey, { key: t(typeName) }), + message: t(errorMsgKey as any, { key: t(typeName as any) as string }) as string, }) return false } diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx index 7c1f4e8f9d..de0ac01cb6 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx @@ -15,12 +15,12 @@ const ModeSwitcher = ({ mode, onChange }: ModeSwitcherProps) => { const options = [ { Icon: RiCalendarLine, - text: t('workflow.nodes.triggerSchedule.mode.visual'), + text: t('workflow.nodes.triggerSchedule.modeVisual'), value: 'visual' as const, }, { Icon: RiCodeLine, - text: t('workflow.nodes.triggerSchedule.mode.cron'), + text: t('workflow.nodes.triggerSchedule.modeCron'), value: 'cron' as const, }, ] diff --git a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts index dec79b8eaf..03cc72b237 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts @@ -103,9 +103,9 @@ const useConfig = (id: string, payload: WebhookTriggerNodeType) => { if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('appDebug.variableConfig.varName'), - }), + }) as string, }) return false } diff --git a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx index d722c1d231..d1274ee65f 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx @@ -120,7 +120,7 @@ const NodeVariableItem = ({ {VariableIcon} {VariableName}
- {writeMode && } + {writeMode && }
) } diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx index 8fb1cfba61..f8b6298a9b 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx @@ -96,7 +96,7 @@ const VarGroupItem: FC = ({ if (!isValid) { Toast.notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: errorKey }) as string, }) return } diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx index 33e2e07376..aafeffb54a 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx @@ -127,7 +127,7 @@ const ChatVariableModal = ({ if (!isValid) { notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: t('workflow.env.modal.name') }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('workflow.env.modal.name') }) as string, }) return false } diff --git a/web/app/components/workflow/panel/env-panel/variable-modal.tsx b/web/app/components/workflow/panel/env-panel/variable-modal.tsx index e253d6c27c..383e15f20b 100644 --- a/web/app/components/workflow/panel/env-panel/variable-modal.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-modal.tsx @@ -37,7 +37,7 @@ const VariableModal = ({ if (!isValid) { notify({ type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: t('workflow.env.modal.name') }), + message: t(`appDebug.varKeyError.${errorMessageKey}` as any, { key: t('workflow.env.modal.name') }) as string, }) return false } diff --git a/web/app/components/workflow/run/loop-result-panel.tsx b/web/app/components/workflow/run/loop-result-panel.tsx index 8238be82f3..6e37657057 100644 --- a/web/app/components/workflow/run/loop-result-panel.tsx +++ b/web/app/components/workflow/run/loop-result-panel.tsx @@ -43,7 +43,7 @@ const LoopResultPanel: FC = ({
- {t(`${i18nPrefix}.testRunLoop`)} + {t(`${i18nPrefix}.testRunLoop` as any) as string}
diff --git a/web/app/forgot-password/ForgotPasswordForm.tsx b/web/app/forgot-password/ForgotPasswordForm.tsx index 2b50c1c452..f06b952b68 100644 --- a/web/app/forgot-password/ForgotPasswordForm.tsx +++ b/web/app/forgot-password/ForgotPasswordForm.tsx @@ -108,7 +108,7 @@ const ForgotPasswordForm = () => { {...register('email')} placeholder={t('login.emailPlaceholder') || ''} /> - {errors.email && {t(`${errors.email?.message}`)}} + {errors.email && {t(`${errors.email?.message}` as any) as string}}
)} diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx index 60de8e0501..f0290cca50 100644 --- a/web/app/install/installForm.tsx +++ b/web/app/install/installForm.tsx @@ -138,7 +138,7 @@ const InstallForm = () => { placeholder={t('login.emailPlaceholder') || ''} 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 && {t(`${errors.email?.message}`)}} + {errors.email && {t(`${errors.email?.message}` as any) as string}}
@@ -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" /> - {errors.name && {t(`${errors.name.message}`)}} + {errors.name && {t(`${errors.name.message}` as any) as string}}
diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx index 9abd4366e1..e3bbe420bc 100644 --- a/web/app/signin/invite-settings/page.tsx +++ b/web/app/signin/invite-settings/page.tsx @@ -1,4 +1,5 @@ 'use client' +import type { Locale } from '@/i18n-config' import { RiAccountCircleLine } from '@remixicon/react' import { noop } from 'lodash-es' import Link from 'next/link' @@ -123,7 +124,7 @@ export default function InviteSettingsPage() { defaultValue={LanguagesSupported[0]} items={languages.filter(item => item.supported)} onSelect={(item) => { - setLanguage(item.value as string) + setLanguage(item.value as Locale) }} />
diff --git a/web/hooks/use-format-time-from-now.ts b/web/hooks/use-format-time-from-now.ts index 09d8db7321..970a64e7d5 100644 --- a/web/hooks/use-format-time-from-now.ts +++ b/web/hooks/use-format-time-from-now.ts @@ -1,8 +1,8 @@ -import type { Locale } from '@/i18n-config' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback } from 'react' import { useI18N } from '@/context/i18n' +import { localeMap } from '@/i18n-config/language' import 'dayjs/locale/de' import 'dayjs/locale/es' import 'dayjs/locale/fa' @@ -26,30 +26,6 @@ import 'dayjs/locale/zh-tw' dayjs.extend(relativeTime) -const localeMap: Record = { - 'en-US': 'en', - 'zh-Hans': 'zh-cn', - 'zh-Hant': 'zh-tw', - 'pt-BR': 'pt-br', - 'es-ES': 'es', - 'fr-FR': 'fr', - 'de-DE': 'de', - 'ja-JP': 'ja', - 'ko-KR': 'ko', - 'ru-RU': 'ru', - 'it-IT': 'it', - 'th-TH': 'th', - 'id-ID': 'id', - 'uk-UA': 'uk', - 'vi-VN': 'vi', - 'ro-RO': 'ro', - 'pl-PL': 'pl', - 'hi-IN': 'hi', - 'tr-TR': 'tr', - 'fa-IR': 'fa', - 'sl-SI': 'sl', -} - export const useFormatTimeFromNow = () => { const { locale } = useI18N() const formatTimeFromNow = useCallback((time: number) => { diff --git a/web/hooks/use-knowledge.ts b/web/hooks/use-knowledge.ts index 400d9722de..e3c2cd49d1 100644 --- a/web/hooks/use-knowledge.ts +++ b/web/hooks/use-knowledge.ts @@ -5,14 +5,14 @@ export const useKnowledge = () => { const { t } = useTranslation() const formatIndexingTechnique = useCallback((indexingTechnique: string) => { - return t(`dataset.indexingTechnique.${indexingTechnique}`) + return t(`dataset.indexingTechnique.${indexingTechnique}` as any) as string }, [t]) const formatIndexingMethod = useCallback((indexingMethod: string, isEco?: boolean) => { if (isEco) return t('dataset.indexingMethod.invertedIndex') - return t(`dataset.indexingMethod.${indexingMethod}`) + return t(`dataset.indexingMethod.${indexingMethod}` as any) as string }, [t]) const formatIndexingTechniqueAndMethod = useCallback((indexingTechnique: string, indexingMethod: string) => { diff --git a/web/hooks/use-metadata.ts b/web/hooks/use-metadata.ts index a51e6b150e..6b0946b68d 100644 --- a/web/hooks/use-metadata.ts +++ b/web/hooks/use-metadata.ts @@ -86,7 +86,7 @@ export const useMetadataMap = (): MetadataMap => { }, 'volume/issue/page_numbers': { label: t(`${fieldPrefix}.paper.volumeIssuePage`) }, 'doi': { label: t(`${fieldPrefix}.paper.DOI`) }, - 'topic/keywords': { label: t(`${fieldPrefix}.paper.topicKeywords`) }, + 'topic/keywords': { label: t(`${fieldPrefix}.paper.topicKeywords` as any) as string }, 'abstract': { label: t(`${fieldPrefix}.paper.abstract`), inputType: 'textarea', @@ -160,7 +160,7 @@ export const useMetadataMap = (): MetadataMap => { 'end_date': { label: t(`${fieldPrefix}.IMChat.endDate`) }, 'participants': { label: t(`${fieldPrefix}.IMChat.participants`) }, 'topicKeywords': { - label: t(`${fieldPrefix}.IMChat.topicKeywords`), + label: t(`${fieldPrefix}.IMChat.topicKeywords` as any) as string, inputType: 'textarea', }, 'fileType': { label: t(`${fieldPrefix}.IMChat.fileType`) }, @@ -193,7 +193,7 @@ export const useMetadataMap = (): MetadataMap => { allowEdit: false, subFieldsMap: { 'title': { label: t(`${fieldPrefix}.notion.title`) }, - 'language': { label: t(`${fieldPrefix}.notion.lang`), inputType: 'select' }, + 'language': { label: t(`${fieldPrefix}.notion.lang` as any) as string, inputType: 'select' }, 'author/creator': { label: t(`${fieldPrefix}.notion.author`) }, 'creation_date': { label: t(`${fieldPrefix}.notion.createdTime`) }, 'last_modified_date': { @@ -201,7 +201,7 @@ export const useMetadataMap = (): MetadataMap => { }, 'notion_page_link': { label: t(`${fieldPrefix}.notion.url`) }, 'category/tags': { label: t(`${fieldPrefix}.notion.tag`) }, - 'description': { label: t(`${fieldPrefix}.notion.desc`) }, + 'description': { label: t(`${fieldPrefix}.notion.desc` as any) as string }, }, }, synced_from_github: { @@ -241,7 +241,7 @@ export const useMetadataMap = (): MetadataMap => { }, 'data_source_type': { label: t(`${fieldPrefix}.originInfo.source`), - render: value => t(`datasetDocuments.metadata.source.${value === 'notion_import' ? 'notion' : value}`), + render: value => t(`datasetDocuments.metadata.source.${value === 'notion_import' ? 'notion' : value}` as any) as string, }, }, }, @@ -323,7 +323,7 @@ export const useLanguages = () => { cs: t(`${langPrefix}cs`), th: t(`${langPrefix}th`), id: t(`${langPrefix}id`), - ro: t(`${langPrefix}ro`), + ro: t(`${langPrefix}ro` as any) as string, } } diff --git a/web/i18n-config/README.md b/web/i18n-config/README.md index 0fe8922345..c724d94aa7 100644 --- a/web/i18n-config/README.md +++ b/web/i18n-config/README.md @@ -55,28 +55,9 @@ cp -r en-US id-ID 1. Add type to new language in the `language.ts` file. -```typescript -export type I18nText = { - 'en-US': string - 'zh-Hans': string - 'pt-BR': string - 'es-ES': string - 'fr-FR': string - 'de-DE': string - 'ja-JP': string - 'ko-KR': string - 'ru-RU': string - 'it-IT': string - 'uk-UA': string - 'id-ID': string - 'tr-TR': string - 'fa-IR': string - 'ar-TN': string - 'YOUR_LANGUAGE_CODE': string -} -``` +> Note: `I18nText` type is now automatically derived from `LanguagesSupported`, so you don't need to manually add types. -4. Add the new language to the `language.json` file. +4. Add the new language to the `languages.ts` file. ```typescript export const languages = [ @@ -189,11 +170,10 @@ We have a list of languages that we support in the `language.ts` file. But some ## Utility scripts -- Auto-fill translations: `pnpm run auto-gen-i18n -- --file app common --lang zh-Hans ja-JP [--dry-run]` +- Auto-fill translations: `pnpm run auto-gen-i18n --file app common --lang zh-Hans ja-JP [--dry-run]` - Use space-separated values; repeat `--file` / `--lang` as needed. Defaults to all en-US files and all supported locales except en-US. - Protects placeholders (`{{var}}`, `${var}`, ``) before translation and restores them after. -- Check missing/extra keys: `pnpm run check-i18n -- --file app billing --lang zh-Hans [--auto-remove]` +- Check missing/extra keys: `pnpm run check-i18n --file app billing --lang zh-Hans [--auto-remove]` - Use space-separated values; repeat `--file` / `--lang` as needed. Returns non-zero on missing/extra keys (CI will fail); `--auto-remove` deletes extra keys automatically. -- Generate types: `pnpm run gen:i18n-types`; verify sync: `pnpm run check:i18n-types`. -Workflows: `.github/workflows/translate-i18n-base-on-english.yml` auto-runs the translation generator on en-US changes to main; `.github/workflows/web-tests.yml` checks i18n keys and type sync on web changes. +Workflows: `.github/workflows/translate-i18n-base-on-english.yml` auto-runs the translation generator on en-US changes to main; `.github/workflows/web-tests.yml` checks i18n keys on web changes. diff --git a/web/i18n-config/auto-gen-i18n.js b/web/i18n-config/auto-gen-i18n.js index 561fa95869..6c8cb05bbd 100644 --- a/web/i18n-config/auto-gen-i18n.js +++ b/web/i18n-config/auto-gen-i18n.js @@ -6,11 +6,11 @@ import vm from 'node:vm' import { translate } from 'bing-translate-api' import { generateCode, loadFile, parseModule } from 'magicast' import { transpile } from 'typescript' +import data from './languages' const require = createRequire(import.meta.url) const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -const data = require('./languages.json') const targetLanguage = 'en-US' const i18nFolder = '../i18n' // Path to i18n folder relative to this script @@ -117,8 +117,8 @@ Options: -h, --help Show help Examples: - pnpm run auto-gen-i18n -- --file app common --lang zh-Hans ja-JP - pnpm run auto-gen-i18n -- --dry-run + pnpm run auto-gen-i18n --file app common --lang zh-Hans ja-JP + pnpm run auto-gen-i18n --dry-run `) } diff --git a/web/i18n-config/check-i18n-sync.js b/web/i18n-config/check-i18n-sync.js deleted file mode 100644 index af00d23875..0000000000 --- a/web/i18n-config/check-i18n-sync.js +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env node - -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import lodash from 'lodash' -import ts from 'typescript' - -const { camelCase } = lodash - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -// Import the NAMESPACES array from i18next-config.ts -function getNamespacesFromConfig() { - const configPath = path.join(__dirname, 'i18next-config.ts') - const configContent = fs.readFileSync(configPath, 'utf8') - const sourceFile = ts.createSourceFile(configPath, configContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS) - - const namespaces = [] - - const visit = (node) => { - if ( - ts.isVariableDeclaration(node) - && node.name.getText() === 'NAMESPACES' - && node.initializer - && ts.isArrayLiteralExpression(node.initializer) - ) { - node.initializer.elements.forEach((el) => { - if (ts.isStringLiteral(el)) - namespaces.push(el.text) - }) - } - ts.forEachChild(node, visit) - } - - visit(sourceFile) - - if (!namespaces.length) - throw new Error('Could not find NAMESPACES array in i18next-config.ts') - - return namespaces -} - -function getNamespacesFromTypes() { - const typesPath = path.join(__dirname, '../types/i18n.d.ts') - - if (!fs.existsSync(typesPath)) { - return null - } - - const typesContent = fs.readFileSync(typesPath, 'utf8') - - // Extract namespaces from Messages type - const messagesMatch = typesContent.match(/export type Messages = \{([\s\S]*?)\}/) - if (!messagesMatch) { - return null - } - - // Parse the properties - const propertiesStr = messagesMatch[1] - const properties = propertiesStr - .split('\n') - .map(line => line.trim()) - .filter(line => line.includes(':')) - .map(line => line.split(':')[0].trim()) - .filter(prop => prop.length > 0) - - return properties -} - -function main() { - try { - console.log('🔍 Checking i18n types synchronization...') - - // Get namespaces from config - const configNamespaces = getNamespacesFromConfig() - console.log(`📦 Found ${configNamespaces.length} namespaces in config`) - - // Convert to camelCase for comparison - const configCamelCase = configNamespaces.map(ns => camelCase(ns)).sort() - - // Get namespaces from type definitions - const typeNamespaces = getNamespacesFromTypes() - - if (!typeNamespaces) { - console.error('❌ Type definitions file not found or invalid') - console.error(' Run: pnpm run gen:i18n-types') - process.exit(1) - } - - console.log(`🔧 Found ${typeNamespaces.length} namespaces in types`) - - const typeCamelCase = typeNamespaces.sort() - - // Compare arrays - const configSet = new Set(configCamelCase) - const typeSet = new Set(typeCamelCase) - - // Find missing in types - const missingInTypes = configCamelCase.filter(ns => !typeSet.has(ns)) - - // Find extra in types - const extraInTypes = typeCamelCase.filter(ns => !configSet.has(ns)) - - let hasErrors = false - - if (missingInTypes.length > 0) { - hasErrors = true - console.error('❌ Missing in type definitions:') - missingInTypes.forEach(ns => console.error(` - ${ns}`)) - } - - if (extraInTypes.length > 0) { - hasErrors = true - console.error('❌ Extra in type definitions:') - extraInTypes.forEach(ns => console.error(` - ${ns}`)) - } - - if (hasErrors) { - console.error('\n💡 To fix synchronization issues:') - console.error(' Run: pnpm run gen:i18n-types') - process.exit(1) - } - - console.log('✅ i18n types are synchronized') - } - catch (error) { - console.error('❌ Error:', error.message) - process.exit(1) - } -} - -main() diff --git a/web/i18n-config/check-i18n.js b/web/i18n-config/check-i18n.js index d69885e6f0..d70564556c 100644 --- a/web/i18n-config/check-i18n.js +++ b/web/i18n-config/check-i18n.js @@ -4,13 +4,13 @@ import path from 'node:path' import { fileURLToPath } from 'node:url' import vm from 'node:vm' import { transpile } from 'typescript' +import data from './languages' const require = createRequire(import.meta.url) const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const targetLanguage = 'en-US' -const data = require('./languages.json') const languages = data.languages.filter(language => language.supported).map(language => language.value) @@ -103,8 +103,8 @@ Options: -h, --help Show help Examples: - pnpm run check-i18n -- --file app billing --lang zh-Hans ja-JP - pnpm run check-i18n -- --auto-remove + pnpm run check-i18n --file app billing --lang zh-Hans ja-JP + pnpm run check-i18n --auto-remove `) } diff --git a/web/i18n-config/generate-i18n-types.js b/web/i18n-config/generate-i18n-types.js deleted file mode 100644 index 0b3c0195af..0000000000 --- a/web/i18n-config/generate-i18n-types.js +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env node - -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import lodash from 'lodash' -import ts from 'typescript' - -const { camelCase } = lodash -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -// Import the NAMESPACES array from i18next-config.ts -function getNamespacesFromConfig() { - const configPath = path.join(__dirname, 'i18next-config.ts') - const configContent = fs.readFileSync(configPath, 'utf8') - const sourceFile = ts.createSourceFile(configPath, configContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS) - - const namespaces = [] - - const visit = (node) => { - if ( - ts.isVariableDeclaration(node) - && node.name.getText() === 'NAMESPACES' - && node.initializer - && ts.isArrayLiteralExpression(node.initializer) - ) { - node.initializer.elements.forEach((el) => { - if (ts.isStringLiteral(el)) - namespaces.push(el.text) - }) - } - ts.forEachChild(node, visit) - } - - visit(sourceFile) - - if (!namespaces.length) - throw new Error('Could not find NAMESPACES array in i18next-config.ts') - - return namespaces -} - -function generateTypeDefinitions(namespaces) { - const header = `// TypeScript type definitions for Dify's i18next configuration -// This file is auto-generated. Do not edit manually. -// To regenerate, run: pnpm run gen:i18n-types -import 'react-i18next' - -// Extract types from translation files using typeof import pattern` - - // Generate individual type definitions - const typeDefinitions = namespaces.map((namespace) => { - const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages` - return `type ${typeName} = typeof import('../i18n/en-US/${namespace}').default` - }).join('\n') - - // Generate Messages interface - const messagesInterface = ` -// Complete type structure that matches i18next-config.ts camelCase conversion -export type Messages = { -${namespaces.map((namespace) => { - const camelCased = camelCase(namespace) - const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages` - return ` ${camelCased}: ${typeName};` -}).join('\n')} -}` - - const utilityTypes = ` -// Utility type to flatten nested object keys into dot notation -type FlattenKeys = T extends object - ? { - [K in keyof T]: T[K] extends object - ? \`\${K & string}.\${FlattenKeys & string}\` - : \`\${K & string}\` - }[keyof T] - : never - -export type ValidTranslationKeys = FlattenKeys` - - const moduleDeclarations = ` -// Extend react-i18next with Dify's type structure -declare module 'react-i18next' { - interface CustomTypeOptions { - defaultNS: 'translation'; - resources: { - translation: Messages; - }; - } -} - -// Extend i18next for complete type safety -declare module 'i18next' { - interface CustomTypeOptions { - defaultNS: 'translation'; - resources: { - translation: Messages; - }; - } -}` - - return [header, typeDefinitions, messagesInterface, utilityTypes, moduleDeclarations].join('\n\n') -} - -function main() { - const args = process.argv.slice(2) - const checkMode = args.includes('--check') - - try { - console.log('📦 Generating i18n type definitions...') - - // Get namespaces from config - const namespaces = getNamespacesFromConfig() - console.log(`✅ Found ${namespaces.length} namespaces`) - - // Generate type definitions - const typeDefinitions = generateTypeDefinitions(namespaces) - - const outputPath = path.join(__dirname, '../types/i18n.d.ts') - - if (checkMode) { - // Check mode: compare with existing file - if (!fs.existsSync(outputPath)) { - console.error('❌ Type definitions file does not exist') - process.exit(1) - } - - const existingContent = fs.readFileSync(outputPath, 'utf8') - if (existingContent.trim() !== typeDefinitions.trim()) { - console.error('❌ Type definitions are out of sync') - console.error(' Run: pnpm run gen:i18n-types') - process.exit(1) - } - - console.log('✅ Type definitions are in sync') - } - else { - // Generate mode: write file - fs.writeFileSync(outputPath, typeDefinitions) - console.log(`✅ Generated type definitions: ${outputPath}`) - } - } - catch (error) { - console.error('❌ Error:', error.message) - process.exit(1) - } -} - -main() diff --git a/web/i18n-config/i18next-config.ts b/web/i18n-config/i18next-config.ts index b310d380e2..e82f5f2acb 100644 --- a/web/i18n-config/i18next-config.ts +++ b/web/i18n-config/i18next-config.ts @@ -1,10 +1,10 @@ 'use client' +import type { Locale } from '.' import i18n from 'i18next' -import { camelCase } from 'lodash-es' -import { initReactI18next } from 'react-i18next' +import { camelCase, kebabCase } from 'lodash-es' +import { initReactI18next } from 'react-i18next' import app from '../i18n/en-US/app' -// Static imports for en-US (fallback language) import appAnnotation from '../i18n/en-US/app-annotation' import appApi from '../i18n/en-US/app-api' import appDebug from '../i18n/en-US/app-debug' @@ -35,7 +35,56 @@ import time from '../i18n/en-US/time' import tools from '../i18n/en-US/tools' import workflow from '../i18n/en-US/workflow' -const requireSilent = async (lang: string, namespace: string) => { +// @keep-sorted +export const messagesEN = { + app, + appAnnotation, + appApi, + appDebug, + appLog, + appOverview, + billing, + common, + custom, + dataset, + datasetCreation, + datasetDocuments, + datasetHitTesting, + datasetPipeline, + datasetSettings, + education, + explore, + layout, + login, + oauth, + pipeline, + plugin, + pluginTags, + pluginTrigger, + register, + runLog, + share, + time, + tools, + workflow, +} + +// pluginTrigger -> plugin-trigger + +export type KebabCase = S extends `${infer T}${infer U}` + ? T extends Lowercase + ? `${T}${KebabCase}` + : `-${Lowercase}${KebabCase}` + : S + +export type CamelCase = S extends `${infer T}-${infer U}` + ? `${T}${Capitalize>}` + : S + +export type KeyPrefix = keyof typeof messagesEN +export type Namespace = KebabCase + +const requireSilent = async (lang: Locale, namespace: Namespace) => { let res try { res = (await import(`../i18n/${lang}/${namespace}`)).default @@ -47,40 +96,9 @@ const requireSilent = async (lang: string, namespace: string) => { return res } -const NAMESPACES = [ - 'app-annotation', - 'app-api', - 'app-debug', - 'app-log', - 'app-overview', - 'app', - 'billing', - 'common', - 'custom', - 'dataset-creation', - 'dataset-documents', - 'dataset-hit-testing', - 'dataset-pipeline', - 'dataset-settings', - 'dataset', - 'education', - 'explore', - 'layout', - 'login', - 'oauth', - 'pipeline', - 'plugin-tags', - 'plugin-trigger', - 'plugin', - 'register', - 'run-log', - 'share', - 'time', - 'tools', - 'workflow', -] +const NAMESPACES = Object.keys(messagesEN).map(kebabCase) as Namespace[] -export const loadLangResources = async (lang: string) => { +export const loadLangResources = async (lang: Locale) => { const modules = await Promise.all( NAMESPACES.map(ns => requireSilent(lang, ns)), ) @@ -93,41 +111,9 @@ export const loadLangResources = async (lang: string) => { // Load en-US resources first to make sure fallback works const getInitialTranslations = () => { - const en_USResources: Record = { - appAnnotation, - appApi, - appDebug, - appLog, - appOverview, - app, - billing, - common, - custom, - datasetCreation, - datasetDocuments, - datasetHitTesting, - datasetPipeline, - datasetSettings, - dataset, - education, - explore, - layout, - login, - oauth, - pipeline, - pluginTags, - pluginTrigger, - plugin, - register, - runLog, - share, - time, - tools, - workflow, - } return { 'en-US': { - translation: en_USResources, + translation: messagesEN, }, } } @@ -140,7 +126,7 @@ if (!i18n.isInitialized) { }) } -export const changeLanguage = async (lng?: string) => { +export const changeLanguage = async (lng?: Locale) => { if (!lng) return if (!i18n.hasResourceBundle(lng, 'translation')) { diff --git a/web/i18n-config/index.ts b/web/i18n-config/index.ts index 8a0f712f2a..bb73ef4b71 100644 --- a/web/i18n-config/index.ts +++ b/web/i18n-config/index.ts @@ -1,5 +1,6 @@ -import Cookies from 'js-cookie' +import type { Locale } from '@/i18n-config/language' +import Cookies from 'js-cookie' import { LOCALE_COOKIE_NAME } from '@/config' import { changeLanguage } from '@/i18n-config/i18next-config' import { LanguagesSupported } from '@/i18n-config/language' @@ -9,7 +10,7 @@ export const i18n = { locales: LanguagesSupported, } as const -export type Locale = typeof i18n['locales'][number] +export { Locale } export const setLocaleOnClient = async (locale: Locale, reloadPage = true) => { Cookies.set(LOCALE_COOKIE_NAME, locale, { expires: 365 }) diff --git a/web/i18n-config/language.ts b/web/i18n-config/language.ts index a1fe6e790f..28afd9eabf 100644 --- a/web/i18n-config/language.ts +++ b/web/i18n-config/language.ts @@ -1,4 +1,4 @@ -import data from './languages.json' +import data from './languages' export type Item = { value: number | string @@ -6,40 +6,20 @@ export type Item = { example: string } -export type I18nText = { - 'en-US': string - 'zh-Hans': string - 'zh-Hant': string - 'pt-BR': string - 'es-ES': string - 'fr-FR': string - 'de-DE': string - 'ja-JP': string - 'ko-KR': string - 'ru-RU': string - 'it-IT': string - 'th-TH': string - 'id-ID': string - 'uk-UA': string - 'vi-VN': string - 'ro-RO': string - 'pl-PL': string - 'hi-IN': string - 'tr-TR': string - 'fa-IR': string - 'sl-SI': string - 'ar-TN': string -} +export type I18nText = Record export const languages = data.languages -export const LanguagesSupported = languages.filter(item => item.supported).map(item => item.value) +// for compatibility +export type Locale = 'ja_JP' | 'zh_Hans' | 'en_US' | (typeof languages[number])['value'] -export const getLanguage = (locale: string) => { +export const LanguagesSupported: Locale[] = languages.filter(item => item.supported).map(item => item.value) + +export const getLanguage = (locale: Locale): Locale => { if (['zh-Hans', 'ja-JP'].includes(locale)) - return locale.replace('-', '_') + return locale.replace('-', '_') as Locale - return LanguagesSupported[0].replace('-', '_') + return LanguagesSupported[0].replace('-', '_') as Locale } const DOC_LANGUAGE: Record = { @@ -48,6 +28,34 @@ const DOC_LANGUAGE: Record = { 'en-US': 'en', } +export const localeMap: Record = { + 'en-US': 'en', + 'en_US': 'en', + 'zh-Hans': 'zh-cn', + 'zh_Hans': 'zh-cn', + 'zh-Hant': 'zh-tw', + 'pt-BR': 'pt-br', + 'es-ES': 'es', + 'fr-FR': 'fr', + 'de-DE': 'de', + 'ja-JP': 'ja', + 'ja_JP': 'ja', + 'ko-KR': 'ko', + 'ru-RU': 'ru', + 'it-IT': 'it', + 'th-TH': 'th', + 'id-ID': 'id', + 'uk-UA': 'uk', + 'vi-VN': 'vi', + 'ro-RO': 'ro', + 'pl-PL': 'pl', + 'hi-IN': 'hi', + 'tr-TR': 'tr', + 'fa-IR': 'fa', + 'sl-SI': 'sl', + 'ar-TN': 'ar', +} + export const getDocLanguage = (locale: string) => { return DOC_LANGUAGE[locale] || 'en' } diff --git a/web/i18n-config/languages.json b/web/i18n-config/languages.json deleted file mode 100644 index 6e0025b8de..0000000000 --- a/web/i18n-config/languages.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "languages": [ - { - "value": "en-US", - "name": "English (United States)", - "prompt_name": "English", - "example": "Hello, Dify!", - "supported": true - }, - { - "value": "zh-Hans", - "name": "简体中文", - "prompt_name": "Chinese Simplified", - "example": "你好,Dify!", - "supported": true - }, - { - "value": "zh-Hant", - "name": "繁體中文", - "prompt_name": "Chinese Traditional", - "example": "你好,Dify!", - "supported": true - }, - { - "value": "pt-BR", - "name": "Português (Brasil)", - "prompt_name": "Portuguese", - "example": "Olá, Dify!", - "supported": true - }, - { - "value": "es-ES", - "name": "Español (España)", - "prompt_name": "Spanish", - "example": "¡Hola, Dify!", - "supported": true - }, - { - "value": "fr-FR", - "name": "Français (France)", - "prompt_name": "French", - "example": "Bonjour, Dify!", - "supported": true - }, - { - "value": "de-DE", - "name": "Deutsch (Deutschland)", - "prompt_name": "German", - "example": "Hallo, Dify!", - "supported": true - }, - { - "value": "ja-JP", - "name": "日本語 (日本)", - "prompt_name": "Japanese", - "example": "こんにちは、Dify!", - "supported": true - }, - { - "value": "ko-KR", - "name": "한국어 (대한민국)", - "prompt_name": "Korean", - "example": "안녕하세요, Dify!", - "supported": true - }, - { - "value": "ru-RU", - "name": "Русский (Россия)", - "prompt_name": "Russian", - "example": " Привет, Dify!", - "supported": true - }, - { - "value": "it-IT", - "name": "Italiano (Italia)", - "prompt_name": "Italian", - "example": "Ciao, Dify!", - "supported": true - }, - { - "value": "th-TH", - "name": "ไทย (ประเทศไทย)", - "prompt_name": "Thai", - "example": "สวัสดี Dify!", - "supported": true - }, - { - "value": "uk-UA", - "name": "Українська (Україна)", - "prompt_name": "Ukrainian", - "example": "Привет, Dify!", - "supported": true - }, - { - "value": "vi-VN", - "name": "Tiếng Việt (Việt Nam)", - "prompt_name": "Vietnamese", - "example": "Xin chào, Dify!", - "supported": true - }, - { - "value": "ro-RO", - "name": "Română (România)", - "prompt_name": "Romanian", - "example": "Salut, Dify!", - "supported": true - }, - { - "value": "pl-PL", - "name": "Polski (Polish)", - "prompt_name": "Polish", - "example": "Cześć, Dify!", - "supported": true - }, - { - "value": "hi-IN", - "name": "Hindi (India)", - "prompt_name": "Hindi", - "example": "नमस्ते, Dify!", - "supported": true - }, - { - "value": "tr-TR", - "name": "Türkçe", - "prompt_name": "Türkçe", - "example": "Selam!", - "supported": true - }, - { - "value": "fa-IR", - "name": "Farsi (Iran)", - "prompt_name": "Farsi", - "example": "سلام, دیفای!", - "supported": true - }, - { - "value": "sl-SI", - "name": "Slovensko (Slovenija)", - "prompt_name": "Slovensko", - "example": "Zdravo, Dify!", - "supported": true - }, - { - "value": "id-ID", - "name": "Bahasa Indonesia", - "prompt_name": "Indonesian", - "example": "Halo, Dify!", - "supported": true - }, - { - "value": "ar-TN", - "name": "العربية (تونس)", - "prompt_name": "Tunisian Arabic", - "example": "مرحبا، Dify!", - "supported": true - } - ] -} diff --git a/web/i18n-config/languages.ts b/web/i18n-config/languages.ts new file mode 100644 index 0000000000..5077aee1d2 --- /dev/null +++ b/web/i18n-config/languages.ts @@ -0,0 +1,160 @@ +const data = { + languages: [ + { + value: 'en-US', + name: 'English (United States)', + prompt_name: 'English', + example: 'Hello, Dify!', + supported: true, + }, + { + value: 'zh-Hans', + name: '简体中文', + prompt_name: 'Chinese Simplified', + example: '你好,Dify!', + supported: true, + }, + { + value: 'zh-Hant', + name: '繁體中文', + prompt_name: 'Chinese Traditional', + example: '你好,Dify!', + supported: true, + }, + { + value: 'pt-BR', + name: 'Português (Brasil)', + prompt_name: 'Portuguese', + example: 'Olá, Dify!', + supported: true, + }, + { + value: 'es-ES', + name: 'Español (España)', + prompt_name: 'Spanish', + example: '¡Hola, Dify!', + supported: true, + }, + { + value: 'fr-FR', + name: 'Français (France)', + prompt_name: 'French', + example: 'Bonjour, Dify!', + supported: true, + }, + { + value: 'de-DE', + name: 'Deutsch (Deutschland)', + prompt_name: 'German', + example: 'Hallo, Dify!', + supported: true, + }, + { + value: 'ja-JP', + name: '日本語 (日本)', + prompt_name: 'Japanese', + example: 'こんにちは、Dify!', + supported: true, + }, + { + value: 'ko-KR', + name: '한국어 (대한민국)', + prompt_name: 'Korean', + example: '안녕하세요, Dify!', + supported: true, + }, + { + value: 'ru-RU', + name: 'Русский (Россия)', + prompt_name: 'Russian', + example: ' Привет, Dify!', + supported: true, + }, + { + value: 'it-IT', + name: 'Italiano (Italia)', + prompt_name: 'Italian', + example: 'Ciao, Dify!', + supported: true, + }, + { + value: 'th-TH', + name: 'ไทย (ประเทศไทย)', + prompt_name: 'Thai', + example: 'สวัสดี Dify!', + supported: true, + }, + { + value: 'uk-UA', + name: 'Українська (Україна)', + prompt_name: 'Ukrainian', + example: 'Привет, Dify!', + supported: true, + }, + { + value: 'vi-VN', + name: 'Tiếng Việt (Việt Nam)', + prompt_name: 'Vietnamese', + example: 'Xin chào, Dify!', + supported: true, + }, + { + value: 'ro-RO', + name: 'Română (România)', + prompt_name: 'Romanian', + example: 'Salut, Dify!', + supported: true, + }, + { + value: 'pl-PL', + name: 'Polski (Polish)', + prompt_name: 'Polish', + example: 'Cześć, Dify!', + supported: true, + }, + { + value: 'hi-IN', + name: 'Hindi (India)', + prompt_name: 'Hindi', + example: 'नमस्ते, Dify!', + supported: true, + }, + { + value: 'tr-TR', + name: 'Türkçe', + prompt_name: 'Türkçe', + example: 'Selam!', + supported: true, + }, + { + value: 'fa-IR', + name: 'Farsi (Iran)', + prompt_name: 'Farsi', + example: 'سلام, دیفای!', + supported: true, + }, + { + value: 'sl-SI', + name: 'Slovensko (Slovenija)', + prompt_name: 'Slovensko', + example: 'Zdravo, Dify!', + supported: true, + }, + { + value: 'id-ID', + name: 'Bahasa Indonesia', + prompt_name: 'Indonesian', + example: 'Halo, Dify!', + supported: true, + }, + { + value: 'ar-TN', + name: 'العربية (تونس)', + prompt_name: 'Tunisian Arabic', + example: 'مرحبا، Dify!', + supported: true, + }, + ], +} as const + +export default data diff --git a/web/i18n-config/server.ts b/web/i18n-config/server.ts index c4e008cf84..75a5f3943b 100644 --- a/web/i18n-config/server.ts +++ b/web/i18n-config/server.ts @@ -1,19 +1,20 @@ import type { Locale } from '.' +import type { KeyPrefix, Namespace } from './i18next-config' import { match } from '@formatjs/intl-localematcher' import { createInstance } from 'i18next' - import resourcesToBackend from 'i18next-resources-to-backend' +import { camelCase } from 'lodash-es' import Negotiator from 'negotiator' import { cookies, headers } from 'next/headers' import { initReactI18next } from 'react-i18next/initReactI18next' import { i18n } from '.' // https://locize.com/blog/next-13-app-dir-i18n/ -const initI18next = async (lng: Locale, ns: string) => { +const initI18next = async (lng: Locale, ns: Namespace) => { const i18nInstance = createInstance() await i18nInstance .use(initReactI18next) - .use(resourcesToBackend((language: string, namespace: string) => import(`../i18n/${language}/${namespace}.ts`))) + .use(resourcesToBackend((language: Locale, namespace: Namespace) => import(`../i18n/${language}/${namespace}.ts`))) .init({ lng: lng === 'zh-Hans' ? 'zh-Hans' : lng, ns, @@ -22,10 +23,10 @@ const initI18next = async (lng: Locale, ns: string) => { return i18nInstance } -export async function useTranslation(lng: Locale, ns = '', options: Record = {}) { +export async function getTranslation(lng: Locale, ns: Namespace) { const i18nextInstance = await initI18next(lng, ns) return { - t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix), + t: i18nextInstance.getFixedT(lng, 'translation', camelCase(ns) as KeyPrefix), i18n: i18nextInstance, } } diff --git a/web/i18n/en-US/app-api.ts b/web/i18n/en-US/app-api.ts index 1fba63c977..17f1a06782 100644 --- a/web/i18n/en-US/app-api.ts +++ b/web/i18n/en-US/app-api.ts @@ -79,6 +79,7 @@ const translation = { pathParams: 'Path Params', query: 'Query', toc: 'Contents', + noContent: 'No content', }, } diff --git a/web/i18n/en-US/app-debug.ts b/web/i18n/en-US/app-debug.ts index 815c6d9aeb..5604de3dd1 100644 --- a/web/i18n/en-US/app-debug.ts +++ b/web/i18n/en-US/app-debug.ts @@ -32,6 +32,9 @@ const translation = { cancelDisagree: 'Cancel dislike', userAction: 'User ', }, + code: { + instruction: 'Instruction', + }, notSetAPIKey: { title: 'LLM provider key has not been set', trailFinished: 'Trail finished', diff --git a/web/i18n/en-US/app.ts b/web/i18n/en-US/app.ts index 1f41d3601e..45ebd61aec 100644 --- a/web/i18n/en-US/app.ts +++ b/web/i18n/en-US/app.ts @@ -1,4 +1,9 @@ const translation = { + theme: { + switchDark: 'Switch to dark theme', + switchLight: 'Switch to light theme', + }, + appNamePlaceholder: 'Give your app a name', createApp: 'CREATE APP', types: { all: 'All', @@ -298,6 +303,7 @@ const translation = { commandHint: 'Type @ to browse by category', slashHint: 'Type / to see all available commands', actions: { + slashTitle: 'Commands', searchApplications: 'Search Applications', searchApplicationsDesc: 'Search and navigate to your applications', searchPlugins: 'Search Plugins', diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 92d24b1351..2e378afeda 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -1,4 +1,6 @@ const translation = { + loading: 'Loading', + error: 'Error', theme: { theme: 'Theme', light: 'light', @@ -71,6 +73,8 @@ const translation = { saveAndRegenerate: 'Save & Regenerate Child Chunks', view: 'View', viewMore: 'VIEW MORE', + back: 'Back', + imageDownloaded: 'Image downloaded', regenerate: 'Regenerate', submit: 'Submit', skip: 'Skip', @@ -252,6 +256,7 @@ const translation = { feedbackPlaceholder: 'Optional', editWorkspaceInfo: 'Edit Workspace Info', workspaceName: 'Workspace Name', + workspaceNamePlaceholder: 'Enter workspace name', workspaceIcon: 'Workspace Icon', changeEmail: { title: 'Change Email', @@ -515,6 +520,7 @@ const translation = { emptyProviderTip: 'Please install a model provider first.', auth: { unAuthorized: 'Unauthorized', + credentialRemoved: 'Credential removed', authRemoved: 'Auth removed', apiKeys: 'API Keys', addApiKey: 'Add API Key', diff --git a/web/i18n/en-US/dataset.ts b/web/i18n/en-US/dataset.ts index 6ffd312fff..ee1997f699 100644 --- a/web/i18n/en-US/dataset.ts +++ b/web/i18n/en-US/dataset.ts @@ -245,6 +245,7 @@ const translation = { button: 'Drag and drop file or folder, or', browse: 'Browse', tip: '{{supportTypes}} (Max {{batchCount}}, {{size}}MB each)', + fileSizeLimitExceeded: 'File size exceeds the {{size}}MB limit', }, } diff --git a/web/i18n/en-US/login.ts b/web/i18n/en-US/login.ts index dd923db217..3b0c8bbba1 100644 --- a/web/i18n/en-US/login.ts +++ b/web/i18n/en-US/login.ts @@ -64,6 +64,7 @@ const translation = { passwordInvalid: 'Password must contain letters and numbers, and the length must be greater than 8', registrationNotAllowed: 'Account not found. Please contact the system admin to register.', invalidEmailOrPassword: 'Invalid email or password.', + redirectUrlMissing: 'Redirect URL is missing', }, license: { tip: 'Before starting Dify Community Edition, read the GitHub', diff --git a/web/i18n/en-US/plugin-trigger.ts b/web/i18n/en-US/plugin-trigger.ts index aedd0c6225..f1d697e507 100644 --- a/web/i18n/en-US/plugin-trigger.ts +++ b/web/i18n/en-US/plugin-trigger.ts @@ -158,6 +158,7 @@ const translation = { }, errors: { createFailed: 'Failed to create subscription', + updateFailed: 'Failed to update subscription', verifyFailed: 'Failed to verify credentials', authFailed: 'Authorization failed', networkError: 'Network error, please try again', diff --git a/web/i18n/en-US/tools.ts b/web/i18n/en-US/tools.ts index 86c225c1b2..b2753a5721 100644 --- a/web/i18n/en-US/tools.ts +++ b/web/i18n/en-US/tools.ts @@ -14,6 +14,7 @@ const translation = { }, author: 'By', auth: { + unauthorized: 'Unauthorized', authorized: 'Authorized', setup: 'Set up authorization to use', setupModalTitle: 'Set Up Authorization', diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index a023ac2b91..2122c20aaa 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -1106,6 +1106,9 @@ const translation = { lastDay: 'Last day', lastDayTooltip: 'Not all months have 31 days. Use the \'last day\' option to select each month\'s final day.', mode: 'Mode', + modeVisual: 'Visual', + modeCron: 'Cron', + selectTime: 'Select time', timezone: 'Timezone', visualConfig: 'Visual Configuration', monthlyDay: 'Monthly Day', diff --git a/web/i18n/ja-JP/app-api.ts b/web/i18n/ja-JP/app-api.ts index e344ad04a9..35203e53e0 100644 --- a/web/i18n/ja-JP/app-api.ts +++ b/web/i18n/ja-JP/app-api.ts @@ -78,6 +78,7 @@ const translation = { pathParams: 'パスパラメータ', query: 'クエリ', toc: '内容', + noContent: 'コンテンツなし', }, regenerate: '再生', } diff --git a/web/i18n/ja-JP/app-debug.ts b/web/i18n/ja-JP/app-debug.ts index 77d991974f..9d1deb47eb 100644 --- a/web/i18n/ja-JP/app-debug.ts +++ b/web/i18n/ja-JP/app-debug.ts @@ -32,6 +32,9 @@ const translation = { cancelDisagree: 'いいえをキャンセル', userAction: 'ユーザー', }, + code: { + instruction: '指示', + }, notSetAPIKey: { title: 'LLM プロバイダーキーが設定されていません', trailFinished: 'トライアル終了', diff --git a/web/i18n/ja-JP/app.ts b/web/i18n/ja-JP/app.ts index f084fc3b8c..899405e8e7 100644 --- a/web/i18n/ja-JP/app.ts +++ b/web/i18n/ja-JP/app.ts @@ -1,4 +1,9 @@ const translation = { + theme: { + switchDark: 'ダークテーマに切り替え', + switchLight: 'ライトテーマに切り替え', + }, + appNamePlaceholder: 'アプリに名前を付ける', createApp: 'アプリを作成する', types: { all: '全て', @@ -295,6 +300,7 @@ const translation = { commandHint: '@ を入力してカテゴリ別に参照', slashHint: '/ を入力してすべてのコマンドを表示', actions: { + slashTitle: 'コマンド', searchApplications: 'アプリケーションを検索', searchApplicationsDesc: 'アプリケーションを検索してナビゲート', searchPlugins: 'プラグインを検索', diff --git a/web/i18n/ja-JP/common.ts b/web/i18n/ja-JP/common.ts index bde00cb66b..87d9fa1fb1 100644 --- a/web/i18n/ja-JP/common.ts +++ b/web/i18n/ja-JP/common.ts @@ -1,4 +1,6 @@ const translation = { + loading: '読み込み中', + error: 'エラー', theme: { theme: 'テーマ', light: '明るい', @@ -68,6 +70,8 @@ const translation = { selectAll: 'すべて選択', deSelectAll: 'すべて選択解除', now: '今', + back: '戻る', + imageDownloaded: '画像がダウンロードされました', config: 'コンフィグ', yes: 'はい', no: 'いいえ', @@ -248,6 +252,7 @@ const translation = { sendVerificationButton: '確認コードの送信', editWorkspaceInfo: 'ワークスペース情報を編集', workspaceName: 'ワークスペース名', + workspaceNamePlaceholder: 'ワークスペース名を入力', workspaceIcon: 'ワークスペースアイコン', changeEmail: { title: 'メールアドレスを変更', @@ -512,6 +517,7 @@ const translation = { authorizationError: '認証エラー', apiKeys: 'APIキー', unAuthorized: '無許可', + credentialRemoved: '認証情報が削除されました', configModel: 'モデルを構成する', addApiKey: 'APIキーを追加してください', addCredential: '認証情報を追加する', diff --git a/web/i18n/ja-JP/dataset.ts b/web/i18n/ja-JP/dataset.ts index a880dd4f5a..b6c4e1d40c 100644 --- a/web/i18n/ja-JP/dataset.ts +++ b/web/i18n/ja-JP/dataset.ts @@ -245,6 +245,7 @@ const translation = { button: 'ファイルまたはフォルダをドラッグアンドドロップ、または', browse: '閲覧', tip: '{{supportTypes}}(最大 {{batchCount}}、各 {{size}}MB)', + fileSizeLimitExceeded: 'ファイルサイズが {{size}}MB の制限を超えています', }, } diff --git a/web/i18n/ja-JP/login.ts b/web/i18n/ja-JP/login.ts index 7069315c9d..c9e0fe3e1e 100644 --- a/web/i18n/ja-JP/login.ts +++ b/web/i18n/ja-JP/login.ts @@ -57,6 +57,7 @@ const translation = { passwordInvalid: 'パスワードは文字と数字を含み、長さは 8 以上である必要があります', registrationNotAllowed: 'アカウントが見つかりません。登録するためにシステム管理者に連絡してください。', invalidEmailOrPassword: '無効なメールアドレスまたはパスワードです。', + redirectUrlMissing: 'リダイレクト URL が見つかりません', }, license: { tip: 'GitHub のオープンソースライセンスを確認してから、Dify Community Edition を開始してください。', diff --git a/web/i18n/ja-JP/plugin-trigger.ts b/web/i18n/ja-JP/plugin-trigger.ts index c7453cff42..7dbd861909 100644 --- a/web/i18n/ja-JP/plugin-trigger.ts +++ b/web/i18n/ja-JP/plugin-trigger.ts @@ -165,6 +165,7 @@ const translation = { verifyFailed: '認証情報の検証に失敗しました', authFailed: '認証に失敗しました', networkError: 'ネットワークエラーです。再試行してください', + updateFailed: 'サブスクリプションの更新に失敗しました', }, }, events: { diff --git a/web/i18n/ja-JP/tools.ts b/web/i18n/ja-JP/tools.ts index 30f623575f..41dc30ac30 100644 --- a/web/i18n/ja-JP/tools.ts +++ b/web/i18n/ja-JP/tools.ts @@ -15,6 +15,7 @@ const translation = { author: '著者:', auth: { authorized: '認証済み', + unauthorized: '未認証', setup: '使用するための認証を設定する', setupModalTitle: '認証の設定', setupModalTitleDescription: '資格情報を構成した後、ワークスペース内のすべてのメンバーがアプリケーションのオーケストレーション時にこのツールを使用できます。', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 7f4e7a3009..24f05d6c31 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -1062,6 +1062,9 @@ const translation = { useVisualPicker: 'ビジュアル設定を使用', nodeTitle: 'スケジュールトリガー', mode: 'モード', + modeVisual: 'ビジュアル', + modeCron: 'Cron', + selectTime: '時間を選択', timezone: 'タイムゾーン', visualConfig: 'ビジュアル設定', monthlyDay: '月の日', diff --git a/web/i18n/zh-Hans/app-api.ts b/web/i18n/zh-Hans/app-api.ts index 4fe97f8231..70219e0cc6 100644 --- a/web/i18n/zh-Hans/app-api.ts +++ b/web/i18n/zh-Hans/app-api.ts @@ -79,6 +79,7 @@ const translation = { pathParams: 'Path Params', query: 'Query', toc: '目录', + noContent: '暂无内容', }, } diff --git a/web/i18n/zh-Hans/app-debug.ts b/web/i18n/zh-Hans/app-debug.ts index 33f563af99..e3df0ea9ea 100644 --- a/web/i18n/zh-Hans/app-debug.ts +++ b/web/i18n/zh-Hans/app-debug.ts @@ -32,6 +32,9 @@ const translation = { cancelDisagree: '取消反对', userAction: '用户表示', }, + code: { + instruction: '指令', + }, notSetAPIKey: { title: 'LLM 提供者的密钥未设置', trailFinished: '试用已结束', diff --git a/web/i18n/zh-Hans/app.ts b/web/i18n/zh-Hans/app.ts index 517c41de10..71edaa1629 100644 --- a/web/i18n/zh-Hans/app.ts +++ b/web/i18n/zh-Hans/app.ts @@ -1,4 +1,9 @@ const translation = { + theme: { + switchDark: '切换至深色主题', + switchLight: '切换至浅色主题', + }, + appNamePlaceholder: '给你的应用起个名字', createApp: '创建应用', types: { all: '全部', @@ -297,6 +302,7 @@ const translation = { commandHint: '输入 @ 按类别浏览', slashHint: '输入 / 查看所有可用命令', actions: { + slashTitle: '命令', searchApplications: '搜索应用程序', searchApplicationsDesc: '搜索并导航到您的应用程序', searchPlugins: '搜索插件', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index bd0e0e3ba4..977ffe1919 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -1,4 +1,6 @@ const translation = { + loading: '加载中', + error: '错误', theme: { theme: '主题', light: '浅色', @@ -78,6 +80,8 @@ const translation = { selectAll: '全选', deSelectAll: '取消全选', now: '现在', + back: '返回', + imageDownloaded: '图片已下载', }, errorMsg: { fieldRequired: '{{field}} 为必填项', @@ -252,6 +256,7 @@ const translation = { feedbackPlaceholder: '选填', editWorkspaceInfo: '编辑工作空间信息', workspaceName: '工作空间名称', + workspaceNamePlaceholder: '输入工作空间名称', workspaceIcon: '工作空间图标', changeEmail: { title: '更改邮箱', @@ -509,6 +514,7 @@ const translation = { emptyProviderTip: '请安装模型供应商。', auth: { unAuthorized: '未授权', + credentialRemoved: '凭据已移除', authRemoved: '授权已移除', apiKeys: 'API 密钥', addApiKey: '添加 API 密钥', diff --git a/web/i18n/zh-Hans/dataset.ts b/web/i18n/zh-Hans/dataset.ts index 7399604762..781fb5aa94 100644 --- a/web/i18n/zh-Hans/dataset.ts +++ b/web/i18n/zh-Hans/dataset.ts @@ -245,6 +245,7 @@ const translation = { tip: '支持 {{supportTypes}} (最多 {{batchCount}} 个,每个大小不超过 {{size}}MB)', button: '拖拽文件或文件夹,或', browse: '浏览', + fileSizeLimitExceeded: '文件大小超过 {{size}}MB 限制', }, } diff --git a/web/i18n/zh-Hans/login.ts b/web/i18n/zh-Hans/login.ts index 13a75eaaaa..d79005fbdd 100644 --- a/web/i18n/zh-Hans/login.ts +++ b/web/i18n/zh-Hans/login.ts @@ -64,6 +64,7 @@ const translation = { passwordLengthInValid: '密码必须至少为 8 个字符', registrationNotAllowed: '账户不存在,请联系系统管理员注册账户', invalidEmailOrPassword: '邮箱或密码错误', + redirectUrlMissing: '重定向 URL 缺失', }, license: { tip: '启动 Dify 社区版之前,请阅读 GitHub 上的', diff --git a/web/i18n/zh-Hans/plugin-trigger.ts b/web/i18n/zh-Hans/plugin-trigger.ts index 304cdd47bd..4f31f517eb 100644 --- a/web/i18n/zh-Hans/plugin-trigger.ts +++ b/web/i18n/zh-Hans/plugin-trigger.ts @@ -161,6 +161,7 @@ const translation = { verifyFailed: '验证凭据失败', authFailed: '授权失败', networkError: '网络错误,请重试', + updateFailed: '更新订阅失败', }, }, events: { diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index 624fbb241a..7893a66f66 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -15,6 +15,7 @@ const translation = { author: '作者', auth: { authorized: '已授权', + unauthorized: '未授权', setup: '要使用请先授权', setupModalTitle: '设置授权', setupModalTitleDescription: '配置凭据后,工作区中的所有成员都可以在编排应用程序时使用此工具。', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index fd86292252..a6daa56667 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -1062,6 +1062,9 @@ const translation = { days: '天', notConfigured: '未配置', mode: '模式', + modeVisual: '可视化', + modeCron: 'Cron', + selectTime: '选择时间', timezone: '时区', visualConfig: '可视化配置', monthlyDay: '月份日期', diff --git a/web/package.json b/web/package.json index baecd2c5c7..f3cc095811 100644 --- a/web/package.json +++ b/web/package.json @@ -33,10 +33,8 @@ "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", "gen-icons": "node ./app/components/base/icons/script.mjs", "uglify-embed": "node ./bin/uglify-embed", - "check-i18n": "node ./i18n-config/check-i18n.js", - "auto-gen-i18n": "node ./i18n-config/auto-gen-i18n.js", - "gen:i18n-types": "node ./i18n-config/generate-i18n-types.js", - "check:i18n-types": "node ./i18n-config/check-i18n-sync.js", + "check-i18n": "tsx ./i18n-config/check-i18n.js", + "auto-gen-i18n": "tsx ./i18n-config/auto-gen-i18n.js", "test": "vitest run", "test:coverage": "vitest run --coverage", "test:watch": "vitest --watch", @@ -212,7 +210,7 @@ "sass": "^1.93.2", "storybook": "9.1.17", "tailwindcss": "^3.4.18", - "ts-node": "^10.9.2", + "tsx": "^4.21.0", "typescript": "^5.9.3", "uglify-js": "^3.19.3", "vite": "^7.3.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 95d35c24d8..24e710534f 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -544,9 +544,9 @@ importers: tailwindcss: specifier: ^3.4.18 version: 3.4.18(tsx@4.21.0)(yaml@2.8.2) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@18.15.0)(typescript@5.9.3) + tsx: + specifier: ^4.21.0 + version: 4.21.0 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -1360,10 +1360,6 @@ packages: '@code-inspector/webpack@1.2.9': resolution: {integrity: sha512-9YEykVrOIc0zMV7pyTyZhCprjScjn6gPPmxb4/OQXKCrP2fAm+NB188rg0s95e4sM7U3qRUpPA4NUH5F7Ogo+g==} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -2175,9 +2171,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@lexical/clipboard@0.38.2': resolution: {integrity: sha512-dDShUplCu8/o6BB9ousr3uFZ9bltR+HtleF/Tl8FXFNPpZ4AXhbLKUoJuucRuIr+zqT7RxEv/3M6pk/HEoE6NQ==} @@ -3405,18 +3398,6 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -4094,9 +4075,6 @@ packages: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -4624,9 +4602,6 @@ packages: create-hmac@1.1.7: resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cron-parser@5.4.0: resolution: {integrity: sha512-HxYB8vTvnQFx4dLsZpGRa0uHp6X3qIzS3ZJgJ9v6l/5TJMgeWQbLkR5yiJ5hOxGbc9+jCADDnydIe15ReLZnJA==} engines: {node: '>=18'} @@ -4936,10 +4911,6 @@ packages: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} @@ -6364,9 +6335,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -8159,20 +8127,6 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - ts-pattern@5.9.0: resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} @@ -8407,9 +8361,6 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -8797,10 +8748,6 @@ packages: resolution: {integrity: sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -9954,10 +9901,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -10600,11 +10543,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - '@lexical/clipboard@0.38.2': dependencies: '@lexical/html': 0.38.2 @@ -11923,14 +11861,6 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 - '@tsconfig/node10@1.0.12': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -12742,8 +12672,6 @@ snapshots: are-docs-informative@0.0.2: {} - arg@4.1.3: {} - arg@5.0.2: {} argparse@2.0.1: {} @@ -13291,8 +13219,6 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.12 - create-require@1.1.1: {} - cron-parser@5.4.0: dependencies: luxon: 3.7.2 @@ -13625,8 +13551,6 @@ snapshots: diff-sequences@27.5.1: {} - diff@4.0.2: {} - diffie-hellman@5.0.3: dependencies: bn.js: 4.12.2 @@ -15350,8 +15274,6 @@ snapshots: dependencies: semver: 7.7.3 - make-error@1.3.6: {} - markdown-extensions@2.0.0: {} markdown-table@3.0.4: {} @@ -17655,24 +17577,6 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@18.15.0)(typescript@5.9.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 18.15.0 - acorn: 8.15.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - ts-pattern@5.9.0: {} tsconfck@3.1.6(typescript@5.9.3): @@ -17888,8 +17792,6 @@ snapshots: uuid@11.1.0: {} - v8-compile-cache-lib@3.0.1: {} - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -18312,8 +18214,6 @@ snapshots: dependencies: lib0: 0.2.115 - yn@3.1.1: {} - yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} diff --git a/web/types/i18n.d.ts b/web/types/i18n.d.ts index b5e5b39aa7..3e5b10674a 100644 --- a/web/types/i18n.d.ts +++ b/web/types/i18n.d.ts @@ -1,74 +1,8 @@ -// TypeScript type definitions for Dify's i18next configuration -// This file is auto-generated. Do not edit manually. -// To regenerate, run: pnpm run gen:i18n-types +import type { messagesEN } from '../i18n-config/i18next-config' import 'react-i18next' -// Extract types from translation files using typeof import pattern - -type AppAnnotationMessages = typeof import('../i18n/en-US/app-annotation').default -type AppApiMessages = typeof import('../i18n/en-US/app-api').default -type AppDebugMessages = typeof import('../i18n/en-US/app-debug').default -type AppLogMessages = typeof import('../i18n/en-US/app-log').default -type AppOverviewMessages = typeof import('../i18n/en-US/app-overview').default -type AppMessages = typeof import('../i18n/en-US/app').default -type BillingMessages = typeof import('../i18n/en-US/billing').default -type CommonMessages = typeof import('../i18n/en-US/common').default -type CustomMessages = typeof import('../i18n/en-US/custom').default -type DatasetCreationMessages = typeof import('../i18n/en-US/dataset-creation').default -type DatasetDocumentsMessages = typeof import('../i18n/en-US/dataset-documents').default -type DatasetHitTestingMessages = typeof import('../i18n/en-US/dataset-hit-testing').default -type DatasetPipelineMessages = typeof import('../i18n/en-US/dataset-pipeline').default -type DatasetSettingsMessages = typeof import('../i18n/en-US/dataset-settings').default -type DatasetMessages = typeof import('../i18n/en-US/dataset').default -type EducationMessages = typeof import('../i18n/en-US/education').default -type ExploreMessages = typeof import('../i18n/en-US/explore').default -type LayoutMessages = typeof import('../i18n/en-US/layout').default -type LoginMessages = typeof import('../i18n/en-US/login').default -type OauthMessages = typeof import('../i18n/en-US/oauth').default -type PipelineMessages = typeof import('../i18n/en-US/pipeline').default -type PluginTagsMessages = typeof import('../i18n/en-US/plugin-tags').default -type PluginTriggerMessages = typeof import('../i18n/en-US/plugin-trigger').default -type PluginMessages = typeof import('../i18n/en-US/plugin').default -type RegisterMessages = typeof import('../i18n/en-US/register').default -type RunLogMessages = typeof import('../i18n/en-US/run-log').default -type ShareMessages = typeof import('../i18n/en-US/share').default -type TimeMessages = typeof import('../i18n/en-US/time').default -type ToolsMessages = typeof import('../i18n/en-US/tools').default -type WorkflowMessages = typeof import('../i18n/en-US/workflow').default - // Complete type structure that matches i18next-config.ts camelCase conversion -export type Messages = { - appAnnotation: AppAnnotationMessages - appApi: AppApiMessages - appDebug: AppDebugMessages - appLog: AppLogMessages - appOverview: AppOverviewMessages - app: AppMessages - billing: BillingMessages - common: CommonMessages - custom: CustomMessages - datasetCreation: DatasetCreationMessages - datasetDocuments: DatasetDocumentsMessages - datasetHitTesting: DatasetHitTestingMessages - datasetPipeline: DatasetPipelineMessages - datasetSettings: DatasetSettingsMessages - dataset: DatasetMessages - education: EducationMessages - explore: ExploreMessages - layout: LayoutMessages - login: LoginMessages - oauth: OauthMessages - pipeline: PipelineMessages - pluginTags: PluginTagsMessages - pluginTrigger: PluginTriggerMessages - plugin: PluginMessages - register: RegisterMessages - runLog: RunLogMessages - share: ShareMessages - time: TimeMessages - tools: ToolsMessages - workflow: WorkflowMessages -} +export type Messages = typeof messagesEN // Utility type to flatten nested object keys into dot notation type FlattenKeys = T extends object @@ -81,19 +15,9 @@ type FlattenKeys = T extends object export type ValidTranslationKeys = FlattenKeys -// Extend react-i18next with Dify's type structure -declare module 'react-i18next' { - type CustomTypeOptions = { - defaultNS: 'translation' - resources: { - translation: Messages - } - } -} - -// Extend i18next for complete type safety declare module 'i18next' { - type CustomTypeOptions = { + // eslint-disable-next-line ts/consistent-type-definitions + interface CustomTypeOptions { defaultNS: 'translation' resources: { translation: Messages diff --git a/web/utils/format.ts b/web/utils/format.ts index a2c3ef9751..d087d690a2 100644 --- a/web/utils/format.ts +++ b/web/utils/format.ts @@ -1,5 +1,6 @@ import type { Dayjs } from 'dayjs' import type { Locale } from '@/i18n-config' +import { localeMap } from '@/i18n-config/language' import 'dayjs/locale/de' import 'dayjs/locale/es' import 'dayjs/locale/fa' @@ -21,30 +22,6 @@ import 'dayjs/locale/vi' import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-tw' -const localeMap: Record = { - 'en-US': 'en', - 'zh-Hans': 'zh-cn', - 'zh-Hant': 'zh-tw', - 'pt-BR': 'pt-br', - 'es-ES': 'es', - 'fr-FR': 'fr', - 'de-DE': 'de', - 'ja-JP': 'ja', - 'ko-KR': 'ko', - 'ru-RU': 'ru', - 'it-IT': 'it', - 'th-TH': 'th', - 'id-ID': 'id', - 'uk-UA': 'uk', - 'vi-VN': 'vi', - 'ro-RO': 'ro', - 'pl-PL': 'pl', - 'hi-IN': 'hi', - 'tr-TR': 'tr', - 'fa-IR': 'fa', - 'sl-SI': 'sl', -} - /** * Formats a number with comma separators. * @example formatNumber(1234567) will return '1,234,567' @@ -149,6 +126,6 @@ export const formatNumberAbbreviated = (num: number) => { } } -export const formatToLocalTime = (time: Dayjs, local: string, format: string) => { +export const formatToLocalTime = (time: Dayjs, local: Locale, format: string) => { return time.locale(localeMap[local] ?? 'en').format(format) }