From 2dec8942512e7f7d65fb7b12a9b04adc72f3dc58 Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Wed, 4 Feb 2026 08:51:58 +0800 Subject: [PATCH 1/6] feature: complete the internationalization of data management pages --- frontend/src/i18n/locales/en/common.json | 299 ++++++++++++++++ frontend/src/i18n/locales/zh/common.json | 299 ++++++++++++++++ .../Create/components/CreateTaskStepOne.tsx | 5 +- .../DataManagement/Create/CreateDataset.tsx | 16 +- .../DataManagement/Create/EditDataset.tsx | 12 +- .../Create/components/BasicInformation.tsx | 36 +- .../DataManagement/Detail/DatasetDetail.tsx | 71 ++-- .../Detail/components/DataQuality.tsx | 50 +-- .../Detail/components/ImportConfiguration.tsx | 82 +++-- .../Detail/components/Overview.tsx | 205 ++++++----- .../DataManagement/Home/DataManagement.tsx | 76 ++-- .../pages/DataManagement/dataset.const.tsx | 332 ++++++++++-------- 12 files changed, 1089 insertions(+), 394 deletions(-) diff --git a/frontend/src/i18n/locales/en/common.json b/frontend/src/i18n/locales/en/common.json index 791fc98ce..89bb85bcc 100644 --- a/frontend/src/i18n/locales/en/common.json +++ b/frontend/src/i18n/locales/en/common.json @@ -254,6 +254,305 @@ } } }, + "dataManagement": { + "title": "Data Management", + "actions": { + "createDataset": "Create Dataset", + "edit": "Edit", + "import": "Import", + "importData": "Import Data", + "download": "Download", + "delete": "Delete", + "refresh": "Refresh", + "export": "Export", + "batchExport": "Batch Export", + "batchDelete": "Batch Delete", + "rename": "Rename", + "createFolder": "New Folder", + "cancel": "Cancel", + "confirm": "Confirm" + }, + "stats": { + "totalDatasets": "Total Datasets", + "totalFiles": "Total Files", + "totalSize": "Total Size", + "text": "Text", + "image": "Image", + "audio": "Audio", + "video": "Video" + }, + "filters": { + "type": "Type", + "status": "Status", + "tags": "Tags" + }, + "search": { + "placeholder": "Search dataset name or description" + }, + "columns": { + "name": "Name", + "type": "Type", + "status": "Status", + "size": "Size", + "fileCount": "File Count", + "storagePath": "Storage Path", + "createdAt": "Created At", + "updatedAt": "Updated At", + "actions": "Actions", + "fileName": "File Name", + "fileSize": "Size", + "filesInFolder": "Files in Folder", + "uploadTime": "Upload Time", + "tags": "Tags", + "tagsUpdatedAt": "Tags Updated At" + }, + "formLabels": { + "name": "Name", + "description": "Description", + "type": "Type", + "tags": "Tags", + "collectionTask": "Collection Task", + "dataSource": "Data Source", + "autoExtract": "Auto-extract uploaded archives", + "uploadFiles": "Upload Files / Folder", + "databaseType": "Database Type", + "tableName": "Table Name", + "connectionString": "Connection String", + "obsEndpoint": "Endpoint", + "obsBucket": "Bucket", + "obsAccessKey": "Access Key", + "obsSecretKey": "Secret Key" + }, + "placeholders": { + "datasetName": "Enter dataset name", + "datasetDescription": "Describe dataset purpose and content", + "selectTags": "Select tags", + "selectCollectionTask": "Select collection task", + "selectDataSource": "Select data source", + "selectType": "Select dataset type", + "obsEndpoint": "obs.cn-north-4.myhuaweicloud.com", + "obsBucket": "my-bucket", + "databaseTable": "dataset_table", + "databaseConnection": "Database connection string", + "fileName": "Enter file name", + "folderName": "Enter folder name" + }, + "validation": { + "nameRequired": "Please enter dataset name", + "typeRequired": "Please select dataset type", + "filesRequired": "Please upload file or folder" + }, + "messages": { + "createSuccess": "Dataset created successfully", + "createFailed": "Dataset creation failed, please try again", + "updateSuccess": "Dataset updated successfully", + "updateFailed": "Dataset update failed, please try again", + "deleteSuccess": "Dataset deleted", + "downloadSuccess": "Dataset downloaded successfully", + "refreshSuccess": "Data refreshed", + "fileDownloadSuccess": "File downloaded successfully" + }, + "confirm": { + "deleteDatasetTitle": "Confirm delete this dataset?", + "deleteDatasetDesc": "This dataset cannot be recovered after deletion. Proceed with caution.", + "deleteFolderTitle": "Confirm delete folder?", + "deleteFolderDesc": "Deleting folder \"{name}\" will remove all files and subfolders. This action cannot be undone.", + "deleteConfirm": "Delete", + "deleteCancel": "Cancel" + }, + "detail": { + "breadcrumb": "Data Management", + "title": "Dataset Detail", + "tabOverview": "Overview", + "tabLineage": "Data Lineage", + "tabQuality": "Data Quality", + "sectionBasicInfo": "Basic Information", + "sectionFileList": "File List", + "selectedFiles": "Selected {count} files", + "goUp": "Go up", + "currentPath": "Current path: {path}", + "totalItems": "Total {total} items", + "previewTitle": "File Preview: {name}", + "previewEmpty": "No preview available or unsupported file type.", + "previewInfoTitle": "File Info", + "editTitle": "Edit Dataset - {name}" + }, + "labels": { + "id": "ID", + "name": "Name", + "fileCount": "File Count", + "dataSize": "Data Size", + "type": "Type", + "status": "Status", + "creator": "Creator", + "storagePath": "Storage Path", + "storageName": "Storage Name", + "createdAt": "Created At", + "updatedAt": "Updated At", + "description": "Description", + "fileName": "File Name", + "originalName": "Original Name", + "fileType": "File Type", + "fileSize": "File Size", + "tags": "Tags", + "tagsUpdatedAt": "Tags Updated At", + "uploadTime": "Upload Time", + "uploadedBy": "Uploaded By", + "filePath": "File Path", + "annotation": "Annotation" + }, + "defaults": { + "unknown": "Unknown", + "none": "None", + "empty": "-", + "unnamedFile": "Unnamed File" + }, + "lineage": { + "title": "Data Lineage", + "stats": "{nodes} nodes · {edges} edges", + "legendDatasource": "Data Source", + "legendDataset": "Dataset", + "legendModel": "Model", + "legendKnowledge": "Knowledge Base", + "emptyTitle": "No lineage data", + "emptyDesc": "Lineage will appear after collection or workflow linkage", + "edgeTypeCollection": "Data Collection", + "edgeTypeCleaning": "Data Processing", + "edgeTypeLabeling": "Data Annotation", + "edgeTypeSynthesis": "Data Synthesis", + "edgeTypeRatio": "Data Ratio", + "edgeTypeDefault": "Processing Flow", + "detailBasicInfo": "Basic Information", + "detailId": "ID:", + "detailName": "Name:", + "detailStatus": "Status:", + "detailFileCount": "File Count:", + "detailDataSize": "Data Size:", + "detailUpdateTime": "Updated At:", + "detailDescription": "Description", + "detailUpstream": "Upstream", + "detailDownstream": "Downstream", + "detailNoUpstream": "No upstream dependencies", + "detailNoDownstream": "No downstream impacts", + "processDetail": "Process Detail", + "processId": "Process ID:", + "processUpstream": "Upstream:", + "processDownstream": "Downstream:", + "nodeTypeDatasource": "Data Source", + "nodeTypeDataset": "Dataset", + "nodeTypeModel": "Model", + "nodeTypeKnowledge": "Knowledge Base", + "filesCount": "{count} files" + }, + "quality": { + "titleDistribution": "Quality Distribution", + "titleIntegrity": "Data Integrity", + "metricImageClarity": "Image Clarity", + "metricColorConsistency": "Color Consistency", + "metricAnnotationCompleteness": "Annotation Completeness", + "metricTokenQuality": "Token Quality", + "metricLabelConsistency": "Label Consistency", + "metricMetadataCompleteness": "Metadata Completeness", + "metricMissingValueControl": "Missing Value Control", + "metricTypeConsistency": "Type Consistency", + "metricUniqueness": "Uniqueness/Dedup", + "metricFileIntegrity": "File Integrity", + "metricFieldIntegrity": "Field Integrity", + "metricColumnIntegrity": "Column Integrity", + "metricDuplicateRate": "Duplicate Rate", + "recommendationTitle": "Quality Improvement Suggestions", + "recommendationReviewLowQuality": "Review or recollect {count} low-quality samples", + "recommendationSupplementMetadata": "Check and supplement missing metadata fields (missing: {missing})", + "recommendationBalanceDistribution": "Consider adding more underrepresented samples to balance distribution", + "imageClarity": "Image Clarity", + "colorConsistency": "Color Consistency", + "annotationCompleteness": "Annotation Completeness", + "tokenQuality": "Token Quality", + "labelConsistency": "Label Consistency", + "metadataCompleteness": "Metadata Completeness", + "missingValueControl": "Missing Value Control", + "typeConsistency": "Type Consistency", + "uniqueness": "Uniqueness/Dedup", + "fileIntegrity": "File Integrity", + "fieldIntegrity": "Field Integrity", + "columnIntegrity": "Column Integrity", + "duplicateRate": "Duplicate Rate" + }, + "import": { + "title": "Import Data", + "uploadFileTitle": "Local File Upload", + "uploadFileHint": "Drag files here or click to select files", + "uploadFolderTitle": "Local Folder Upload", + "uploadFolderHint": "Drag folder here or click to select folder", + "fileLabel": "File:", + "folderLabel": "Folder:", + "warningTypeMismatch": "Dataset type is {type}. Some selected files may not match; please confirm.", + "warningUseFolderUpload": "To upload a folder, use the \"Local Folder Upload\" area on the right.", + "warningUseFileUpload": "To upload a single file, use the \"Local File Upload\" area on the left.", + "obsEndpoint": "Endpoint", + "obsBucket": "Bucket", + "obsAccessKey": "Access Key", + "obsSecretKey": "Secret Key", + "dbType": "Database Type", + "dbTableName": "Table Name", + "dbConnectionString": "Connection String", + "dbTypeMysql": "MySQL", + "dbTypePostgres": "PostgreSQL", + "dbTypeMongo": "MongoDB", + "obsAccessKeyPlaceholder": "Access Key", + "obsSecretKeyPlaceholder": "Secret Key", + "validation": { + "selectDataSource": "Please select data source" + }, + "autoExtract": "Auto-extract uploaded archives", + "uploadFiles": "Upload Files / Folder", + "unnamedFile": "Unnamed File" + }, + "datasetTypes": { + "text": "Text", + "image": "Image", + "audio": "Audio", + "video": "Video" + }, + "datasetTypeDesc": { + "text": "Dataset for processing and analyzing text data", + "image": "Dataset for processing and analyzing image data", + "audio": "Dataset for processing and analyzing audio data", + "video": "Dataset for processing and analyzing video data" + }, + "datasetSubTypes": { + "textDocument": "Document", + "textWeb": "Web", + "textDialog": "Dialog", + "imageImage": "Image", + "imageCaption": "Image + Caption", + "audioAudio": "Audio", + "audioJsonl": "Audio + JSONL", + "videoVideo": "Video", + "videoJsonl": "Video + JSONL" + }, + "datasetSubTypeDesc": { + "textDocument": "Dataset for storing and processing document text", + "textWeb": "Dataset for storing and processing web data", + "textDialog": "Dataset for storing and processing dialog data", + "imageImage": "Dataset for large-scale image pretraining", + "imageCaption": "Dataset for image captioning", + "audioAudio": "Dataset for large-scale audio pretraining", + "audioJsonl": "Dataset for large-scale audio pretraining", + "videoVideo": "Dataset for large-scale video pretraining", + "videoJsonl": "Dataset for large-scale video pretraining" + }, + "datasetStatus": { + "active": "Active", + "processing": "Processing", + "inactive": "Inactive", + "draft": "Draft" + }, + "dataSources": { + "upload": "Local Upload", + "collection": "Collection Task Import" + } + }, "common": { "actions": { "createTask": "Create Task" diff --git a/frontend/src/i18n/locales/zh/common.json b/frontend/src/i18n/locales/zh/common.json index a091f84b6..135ac83b2 100644 --- a/frontend/src/i18n/locales/zh/common.json +++ b/frontend/src/i18n/locales/zh/common.json @@ -254,6 +254,305 @@ } } }, + "dataManagement": { + "title": "数据管理", + "actions": { + "createDataset": "创建数据集", + "edit": "编辑", + "import": "导入", + "importData": "导入数据", + "download": "下载", + "delete": "删除", + "refresh": "刷新", + "export": "导出", + "batchExport": "批量导出", + "batchDelete": "批量删除", + "rename": "重命名", + "createFolder": "新建文件夹", + "cancel": "取消", + "confirm": "确定" + }, + "stats": { + "totalDatasets": "数据集总数", + "totalFiles": "文件总数", + "totalSize": "总大小", + "text": "文本", + "image": "图像", + "audio": "音频", + "video": "视频" + }, + "filters": { + "type": "类型", + "status": "状态", + "tags": "标签" + }, + "search": { + "placeholder": "搜索数据集名称、描述" + }, + "columns": { + "name": "名称", + "type": "类型", + "status": "状态", + "size": "大小", + "fileCount": "文件数", + "storagePath": "存储路径", + "createdAt": "创建时间", + "updatedAt": "更新时间", + "actions": "操作", + "fileName": "文件名", + "fileSize": "大小", + "filesInFolder": "包含文件数", + "uploadTime": "上传时间", + "tags": "标签", + "tagsUpdatedAt": "标签更新时间" + }, + "formLabels": { + "name": "名称", + "description": "描述", + "type": "类型", + "tags": "标签", + "collectionTask": "关联归集任务", + "dataSource": "数据源", + "autoExtract": "自动解压上传的压缩包", + "uploadFiles": "上传文件 / 文件夹", + "databaseType": "数据库类型", + "tableName": "表名", + "connectionString": "连接字符串", + "obsEndpoint": "Endpoint", + "obsBucket": "Bucket", + "obsAccessKey": "Access Key", + "obsSecretKey": "Secret Key" + }, + "placeholders": { + "datasetName": "输入数据集名称", + "datasetDescription": "描述数据集的用途和内容", + "selectTags": "请选择标签", + "selectCollectionTask": "请选择归集任务", + "selectDataSource": "请选择数据源", + "selectType": "请选择数据集类型", + "obsEndpoint": "obs.cn-north-4.myhuaweicloud.com", + "obsBucket": "my-bucket", + "databaseTable": "dataset_table", + "databaseConnection": "数据库连接字符串", + "fileName": "请输入文件名称", + "folderName": "请输入文件夹名称" + }, + "validation": { + "nameRequired": "请输入数据集名称", + "typeRequired": "请选择数据集类型", + "filesRequired": "请上传文件或文件夹" + }, + "messages": { + "createSuccess": "数据集创建成功", + "createFailed": "数据集创建失败,请重试", + "updateSuccess": "数据集更新成功", + "updateFailed": "数据集更新失败,请重试", + "deleteSuccess": "数据删除成功", + "downloadSuccess": "数据集下载成功", + "refreshSuccess": "数据已刷新", + "fileDownloadSuccess": "文件下载成功" + }, + "confirm": { + "deleteDatasetTitle": "确认删除该数据集?", + "deleteDatasetDesc": "删除后该数据集将无法恢复,请谨慎操作。", + "deleteFolderTitle": "确认删除文件夹?", + "deleteFolderDesc": "删除文件夹 \"{name}\" 将同时删除其中的所有文件和子文件夹,此操作不可恢复。", + "deleteConfirm": "删除", + "deleteCancel": "取消" + }, + "detail": { + "breadcrumb": "数据管理", + "title": "数据集详情", + "tabOverview": "概览", + "tabLineage": "数据血缘", + "tabQuality": "数据质量", + "sectionBasicInfo": "基本信息", + "sectionFileList": "文件列表", + "selectedFiles": "已选择 {count} 个文件", + "goUp": "返回上一级", + "currentPath": "当前路径: {path}", + "totalItems": "共 {total} 条", + "previewTitle": "文件预览:{name}", + "previewEmpty": "暂无预览内容,或当前文件类型暂不支持预览。", + "previewInfoTitle": "文件信息", + "editTitle": "编辑数据集 - {name}" + }, + "labels": { + "id": "ID", + "name": "名称", + "fileCount": "文件数", + "dataSize": "数据大小", + "type": "类型", + "status": "状态", + "creator": "创建者", + "storagePath": "存储路径", + "storageName": "存储名称", + "createdAt": "创建时间", + "updatedAt": "更新时间", + "description": "描述", + "fileName": "文件名", + "originalName": "原始文件名", + "fileType": "文件类型", + "fileSize": "文件大小", + "tags": "标签", + "tagsUpdatedAt": "标签更新时间", + "uploadTime": "上传时间", + "uploadedBy": "上传者", + "filePath": "文件路径", + "annotation": "标注信息" + }, + "defaults": { + "unknown": "未知", + "none": "无", + "empty": "-", + "unnamedFile": "未命名文件" + }, + "lineage": { + "title": "数据血缘图", + "stats": "{nodes} 个节点 · {edges} 条边", + "legendDatasource": "数据源", + "legendDataset": "数据集", + "legendModel": "模型", + "legendKnowledge": "知识库", + "emptyTitle": "暂无血缘数据", + "emptyDesc": "完成归集或关联流程后将自动生成血缘图", + "edgeTypeCollection": "数据归集", + "edgeTypeCleaning": "数据处理", + "edgeTypeLabeling": "数据标注", + "edgeTypeSynthesis": "数据合成", + "edgeTypeRatio": "数据配比", + "edgeTypeDefault": "处理流程", + "detailBasicInfo": "基本信息", + "detailId": "ID:", + "detailName": "名称:", + "detailStatus": "状态:", + "detailFileCount": "文件数:", + "detailDataSize": "数据大小:", + "detailUpdateTime": "更新时间:", + "detailDescription": "描述", + "detailUpstream": "上游依赖", + "detailDownstream": "下游影响", + "detailNoUpstream": "无上游依赖", + "detailNoDownstream": "无下游影响", + "processDetail": "流程详情", + "processId": "流程ID:", + "processUpstream": "上游:", + "processDownstream": "下游:", + "nodeTypeDatasource": "数据源", + "nodeTypeDataset": "数据集", + "nodeTypeModel": "模型", + "nodeTypeKnowledge": "知识库", + "filesCount": "{count} 个文件" + }, + "quality": { + "titleDistribution": "质量分布", + "titleIntegrity": "数据完整性", + "metricImageClarity": "图像清晰度", + "metricColorConsistency": "色彩一致性", + "metricAnnotationCompleteness": "标注完整性", + "metricTokenQuality": "分词/Token质量", + "metricLabelConsistency": "标签一致性", + "metricMetadataCompleteness": "元数据完整性", + "metricMissingValueControl": "缺失值比例控制", + "metricTypeConsistency": "类型一致性", + "metricUniqueness": "唯一性/去重", + "metricFileIntegrity": "文件完整性", + "metricFieldIntegrity": "字段完整性", + "metricColumnIntegrity": "列完整性", + "metricDuplicateRate": "重复率", + "recommendationTitle": "质量改进建议", + "recommendationReviewLowQuality": "建议对{count}项低质量样本进行复查或重新采集", + "recommendationSupplementMetadata": "检查并补充缺失的元数据字段(现有缺失:{missing})", + "recommendationBalanceDistribution": "考虑增加更多低代表性样本以平衡数据分布", + "imageClarity": "图像清晰度", + "colorConsistency": "色彩一致性", + "annotationCompleteness": "标注完整性", + "tokenQuality": "分词/Token质量", + "labelConsistency": "标签一致性", + "metadataCompleteness": "元数据完整性", + "missingValueControl": "缺失值比例控制", + "typeConsistency": "类型一致性", + "uniqueness": "唯一性/去重", + "fileIntegrity": "文件完整性", + "fieldIntegrity": "字段完整性", + "columnIntegrity": "列完整性", + "duplicateRate": "重复率" + }, + "import": { + "title": "导入数据", + "uploadFileTitle": "本地文件上传", + "uploadFileHint": "拖拽文件到此处或点击选择文件", + "uploadFolderTitle": "本地文件夹上传", + "uploadFolderHint": "拖拽文件夹到此处或点击选择文件夹", + "fileLabel": "文件:", + "folderLabel": "文件夹:", + "warningTypeMismatch": "当前数据集类型为 {type},本次选择的部分文件格式与该类型不太匹配,建议确认是否为预期数据。", + "warningUseFolderUpload": "如需上传文件夹,请使用右侧\"本地文件夹上传\"区域。", + "warningUseFileUpload": "如需上传单个文件,请使用左侧\"本地文件上传\"区域。", + "obsEndpoint": "Endpoint", + "obsBucket": "Bucket", + "obsAccessKey": "Access Key", + "obsSecretKey": "Secret Key", + "dbType": "数据库类型", + "dbTableName": "表名", + "dbConnectionString": "连接字符串", + "dbTypeMysql": "MySQL", + "dbTypePostgres": "PostgreSQL", + "dbTypeMongo": "MongoDB", + "obsAccessKeyPlaceholder": "Access Key", + "obsSecretKeyPlaceholder": "Secret Key", + "validation": { + "selectDataSource": "请选择数据源" + }, + "autoExtract": "自动解压上传的压缩包", + "uploadFiles": "上传文件 / 文件夹", + "unnamedFile": "未命名文件" + }, + "datasetTypes": { + "text": "文本", + "image": "图像", + "audio": "音频", + "video": "视频" + }, + "datasetTypeDesc": { + "text": "用于处理和分析文本数据的数据集", + "image": "用于处理和分析图像数据的数据集", + "audio": "用于处理和分析音频数据的数据集", + "video": "用于处理和分析视频数据的数据集" + }, + "datasetSubTypes": { + "textDocument": "文档", + "textWeb": "网页", + "textDialog": "对话", + "imageImage": "图像", + "imageCaption": "图像+caption", + "audioAudio": "音频", + "audioJsonl": "音频+JSONL", + "videoVideo": "视频", + "videoJsonl": "视频+JSONL" + }, + "datasetSubTypeDesc": { + "textDocument": "用于存储和处理各种文档格式的文本数据集", + "textWeb": "用于存储和处理网页数据集", + "textDialog": "用于存储和处理对话数据的数据集", + "imageImage": "用于大规模图像预训练模型的数据集", + "imageCaption": "用于图像标题生成的数据集", + "audioAudio": "用于大规模音频预训练模型的数据集", + "audioJsonl": "用于大规模音频预训练模型的数据集", + "videoVideo": "用于大规模视频预训练模型的数据集", + "videoJsonl": "用于大规模视频预训练模型的数据集" + }, + "datasetStatus": { + "active": "活跃", + "processing": "处理中", + "inactive": "未激活", + "draft": "草稿" + }, + "dataSources": { + "upload": "本地上传", + "collection": "归集任务导入 " + } + }, "common": { "actions": { "createTask": "创建任务" diff --git a/frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx b/frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx index 5cd7929fc..2c570d033 100644 --- a/frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx +++ b/frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx @@ -1,6 +1,6 @@ import RadioCard from "@/components/RadioCard"; import { queryDatasetsUsingGet } from "@/pages/DataManagement/dataset.api"; -import { datasetTypes, mapDataset } from "@/pages/DataManagement/dataset.const"; +import { getDatasetTypeMap, mapDataset } from "@/pages/DataManagement/dataset.const"; import { Dataset, DatasetSubType, @@ -9,6 +9,7 @@ import { import { Input, Select, Form, AutoComplete } from "antd"; import TextArea from "antd/es/input/TextArea"; import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; export default function CreateTaskStepOne({ form, @@ -26,7 +27,9 @@ export default function CreateTaskStepOne({ }; setTaskConfig: (config: any) => void; }) { + const { t } = useTranslation(); const [datasets, setDatasets] = useState([]); + const datasetTypes = getDatasetTypeMap(t); const fetchDatasets = async () => { const { data } = await queryDatasetsUsingGet({ page: 1, size: 1000 }); diff --git a/frontend/src/pages/DataManagement/Create/CreateDataset.tsx b/frontend/src/pages/DataManagement/Create/CreateDataset.tsx index cd0109d91..2416dede7 100644 --- a/frontend/src/pages/DataManagement/Create/CreateDataset.tsx +++ b/frontend/src/pages/DataManagement/Create/CreateDataset.tsx @@ -6,11 +6,13 @@ import { Link, useNavigate } from "react-router"; import { createDatasetUsingPost } from "../dataset.api"; import { DatasetType } from "../dataset.model"; import BasicInformation from "./components/BasicInformation"; +import { useTranslation } from "react-i18next"; export default function DatasetCreate() { const navigate = useNavigate(); const { message } = App.useApp(); const [form] = Form.useForm(); + const { t } = useTranslation(); const [newDataset, setNewDataset] = useState({ name: "", @@ -28,11 +30,11 @@ export default function DatasetCreate() { }; try { const { data } = await createDatasetUsingPost(params); - message.success(`数据集创建成功`); + message.success(t("dataManagement.messages.createSuccess")); navigate("/data/management/detail/" + data.id); } catch (error) { console.error(error); - message.error("数据集创建失败,请重试"); + message.error(t("dataManagement.messages.createFailed")); return; } }; @@ -51,7 +53,9 @@ export default function DatasetCreate() { -

创建数据集

+

+ {t("dataManagement.actions.createDataset")} +

@@ -68,13 +72,15 @@ export default function DatasetCreate() {
- +
diff --git a/frontend/src/pages/DataManagement/Create/EditDataset.tsx b/frontend/src/pages/DataManagement/Create/EditDataset.tsx index 843f8e528..8d6eeb3ab 100644 --- a/frontend/src/pages/DataManagement/Create/EditDataset.tsx +++ b/frontend/src/pages/DataManagement/Create/EditDataset.tsx @@ -6,6 +6,7 @@ import { import { useEffect, useState } from "react"; import { Dataset, DatasetType } from "../dataset.model"; import { App, Button, Form, Modal } from "antd"; +import { useTranslation } from "react-i18next"; export default function EditDataset({ open, @@ -20,6 +21,7 @@ export default function EditDataset({ }) { const [form] = Form.useForm(); const { message } = App.useApp(); + const { t } = useTranslation(); const [newDataset, setNewDataset] = useState({ name: "", @@ -60,27 +62,27 @@ export default function EditDataset({ try { await updateDatasetByIdUsingPut(data?.id, params); onClose(); - message.success("数据集更新成功"); + message.success(t("dataManagement.messages.updateSuccess")); onRefresh?.(false); } catch (error) { console.error(error); - message.error("数据集更新失败,请重试"); + message.error(t("dataManagement.messages.updateFailed")); return; } }; return ( - + } diff --git a/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx b/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx index 363aa49cd..b283694fd 100644 --- a/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx +++ b/frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx @@ -1,9 +1,10 @@ import RadioCard from "@/components/RadioCard"; import { Input, Select, Form } from "antd"; -import { datasetTypes } from "../../dataset.const"; +import { getDatasetTypes } from "../../dataset.const"; import { useEffect, useState } from "react"; import { queryDatasetTagsUsingGet } from "../../dataset.api"; import {queryTasksUsingGet} from "@/pages/DataCollection/collection.apis.ts"; +import { useTranslation } from "react-i18next"; export default function BasicInformation({ data, @@ -14,6 +15,8 @@ export default function BasicInformation({ setData: any; hidden?: string[]; }) { + const { t } = useTranslation(); + const datasetTypes = getDatasetTypes(t); const [tagOptions, setTagOptions] = useState< { label: JSX.Element; @@ -59,24 +62,27 @@ export default function BasicInformation({ return ( <> - + {!hidden.includes("description") && ( - - + + )} {/* 数据集类型选择 - 使用卡片形式 */} {!hidden.includes("datasetType") && ( )} {!hidden.includes("tags") && ( - + + + + + - + - + @@ -481,7 +485,7 @@ export default function ImportConfiguration({ {importConfig?.source === DataSource.UPLOAD && ( <> @@ -510,8 +514,8 @@ export default function ImportConfiguration({

-

本地文件上传

-

拖拽文件到此处或点击选择文件

+

{t("dataManagement.import.uploadFileTitle")}

+

{t("dataManagement.import.uploadFileHint")}

@@ -527,8 +531,8 @@ export default function ImportConfiguration({

-

本地文件夹上传

-

拖拽文件夹到此处或点击选择文件夹

+

{t("dataManagement.import.uploadFolderTitle")}

+

{t("dataManagement.import.uploadFolderHint")}

@@ -538,9 +542,9 @@ export default function ImportConfiguration({
{pureFileList.length > 0 && (
- 文件: + {t("dataManagement.import.fileLabel")} {pureFileList.map((file) => { - const name = file.name || "未命名文件"; + const name = file.name || t("dataManagement.defaults.unnamedFile"); const displayName = name.length > 20 ? `${name.slice(0, 20)}...` : name; return ( - 文件夹: + {t("dataManagement.import.folderLabel")} {folderNames.map((name) => { const displayName = name.length > 20 ? `${name.slice(0, 20)}...` : name; return ( @@ -594,32 +598,32 @@ export default function ImportConfiguration({ - +
diff --git a/frontend/src/pages/DataManagement/Detail/components/Overview.tsx b/frontend/src/pages/DataManagement/Detail/components/Overview.tsx index 8ed179e2b..cc71bca26 100644 --- a/frontend/src/pages/DataManagement/Detail/components/Overview.tsx +++ b/frontend/src/pages/DataManagement/Detail/components/Overview.tsx @@ -1,10 +1,13 @@ import { App, Button, Descriptions, DescriptionsProps, Modal, Table, Input, Spin } from "antd"; import { formatBytes, formatDateTime } from "@/utils/unit"; import { Download, Trash2, Folder, File } from "lucide-react"; -import { datasetTypeMap } from "../../dataset.const"; +import { getDatasetTypeMap } from "../../dataset.const"; +import { useTranslation } from "react-i18next"; export default function Overview({ dataset, filesOperation, fetchDataset }) { const { modal, message } = App.useApp(); + const { t } = useTranslation(); + const datasetTypeMap = getDatasetTypeMap(t); const { fileList, pagination, @@ -44,71 +47,71 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { const items: DescriptionsProps["items"] = [ { key: "id", - label: "ID", + label: t("dataManagement.labels.id"), children: dataset.id, }, { key: "name", - label: "名称", + label: t("dataManagement.labels.name"), children: dataset.name, }, { key: "fileCount", - label: "文件数", + label: t("dataManagement.labels.fileCount"), children: dataset.fileCount || 0, }, { key: "size", - label: "数据大小", + label: t("dataManagement.labels.dataSize"), children: dataset.size || "0 B", }, { key: "datasetType", - label: "类型", - children: datasetTypeMap[dataset?.datasetType]?.label || "未知", + label: t("dataManagement.labels.type"), + children: datasetTypeMap[dataset?.datasetType]?.label || t("dataManagement.defaults.unknown"), }, { key: "status", - label: "状态", - children: dataset?.status?.label || "未知", + label: t("dataManagement.labels.status"), + children: dataset?.status?.label || t("dataManagement.defaults.unknown"), }, { key: "createdBy", - label: "创建者", - children: dataset.createdBy || "未知", + label: t("dataManagement.labels.creator"), + children: dataset.createdBy || t("dataManagement.defaults.unknown"), }, { key: "targetLocation", - label: "存储路径", - children: dataset.targetLocation || "未知", + label: t("dataManagement.labels.storagePath"), + children: dataset.targetLocation || t("dataManagement.defaults.unknown"), }, { key: "pvcName", - label: "存储名称", - children: dataset.pvcName || "未知", + label: t("dataManagement.labels.storageName"), + children: dataset.pvcName || t("dataManagement.defaults.unknown"), }, { key: "createdAt", - label: "创建时间", + label: t("dataManagement.labels.createdAt"), children: dataset.createdAt, }, { key: "updatedAt", - label: "更新时间", + label: t("dataManagement.labels.updatedAt"), children: dataset.updatedAt, }, { key: "description", - label: "描述", - children: dataset.description || "无", + label: t("dataManagement.labels.description"), + children: dataset.description || t("dataManagement.defaults.none"), }, ]; // 文件列表列定义 const columns = [ { - title: "文件名", + title: t("dataManagement.columns.fileName"), dataIndex: "fileName", key: "fileName", fixed: "left", @@ -157,7 +160,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { }, }, { - title: "大小", + title: t("dataManagement.columns.fileSize"), dataIndex: "fileSize", key: "fileSize", width: 150, @@ -170,36 +173,36 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { }, }, { - title: "包含文件数", + title: t("dataManagement.columns.filesInFolder"), dataIndex: "fileCount", key: "fileCount", width: 120, render: (text: number, record: any) => { const isDirectory = record.id.startsWith('directory-'); if (!isDirectory) { - return "-"; + return t("dataManagement.defaults.empty"); } return record.fileCount ?? 0; }, }, { - title: "上传时间", + title: t("dataManagement.columns.uploadTime"), dataIndex: "uploadTime", key: "uploadTime", width: 200, render: (text) => formatDateTime(text), }, { - title: "标签", + title: t("dataManagement.columns.tags"), dataIndex: "tags", key: "tags", width: 220, render: (value: any, record: any) => { const isDirectory = typeof record.id === "string" && record.id.startsWith("directory-"); - if (isDirectory) return "-"; + if (isDirectory) return t("dataManagement.defaults.empty"); let raw = value; - if (!raw) return "-"; + if (!raw) return t("dataManagement.defaults.empty"); // 后端目前将 tags 作为 JSON 字符串存储在 t_dm_dataset_files.tags 中 // 这里尝试解析为 FileTag 数组结构 [{ type, from_name, values: { [type]: [...] } }] @@ -212,7 +215,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { } } - if (!Array.isArray(raw) || raw.length === 0) return "-"; + if (!Array.isArray(raw) || raw.length === 0) return t("dataManagement.defaults.empty"); const labels: string[] = []; raw.forEach((tag: any) => { @@ -231,24 +234,24 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { } }); - if (!labels.length) return "-"; + if (!labels.length) return t("dataManagement.defaults.empty"); return labels.join(", "); }, }, { - title: "标签更新时间", + title: t("dataManagement.columns.tagsUpdatedAt"), dataIndex: "tagsUpdatedAt", key: "tagsUpdatedAt", width: 200, render: (text: any, record: any) => { const isDirectory = typeof record.id === "string" && record.id.startsWith("directory-"); - if (isDirectory) return "-"; - if (!text) return "-"; + if (isDirectory) return t("dataManagement.defaults.empty"); + if (!text) return t("dataManagement.defaults.empty"); return formatDateTime(text); }, }, { - title: "操作", + title: t("dataManagement.columns.actions"), key: "action", width: 180, fixed: "right", @@ -266,7 +269,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { type="link" onClick={() => handleDownloadDirectory(fullPath, record.fileName)} > - 下载 + {t("dataManagement.actions.download")}
); @@ -329,7 +332,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { type="link" onClick={() => handleDownloadFile(record)} > - 下载 + {t("dataManagement.actions.download")} )}, @@ -391,7 +394,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) {
{/* 基本信息 */} -

文件列表

+

+ {t("dataManagement.detail.sectionFileList")} +

{selectedFiles.length > 0 && (
- 已选择 {selectedFiles.length} 个文件 + {t("dataManagement.detail.selectedFiles", { count: selectedFiles.length })}
)} @@ -487,12 +492,16 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { d="M10 19l-7-7m0 0l7-7m-7 7h18" /> - 返回上一级 + {t("dataManagement.detail.goUp")} )} {filesOperation.pagination.prefix && ( - 当前路径: {filesOperation.pagination.prefix} + + {t("dataManagement.detail.currentPath", { + path: filesOperation.pagination.prefix, + })} + )} `共 ${total} 条`, + showTotal: (total) => t("dataManagement.detail.totalItems", { total }), onChange: (page, pageSize) => { filesOperation.fetchFiles(filesOperation.pagination.prefix, page, pageSize); } @@ -514,7 +523,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { {/* 文件预览弹窗 */} setPreviewVisible(false)} footer={null} @@ -549,50 +558,64 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { ) : ( - 暂无预览内容,或当前文件类型暂不支持预览。 + {t("dataManagement.detail.previewEmpty")} )} {/* 右侧文件信息(来自 t_dm_dataset_files) */}
-

文件信息

+

+ {t("dataManagement.detail.previewInfoTitle")} +

- 文件名: + + {t("dataManagement.labels.fileName")}: + {previewFileDetail?.fileName || previewFileName}
{previewFileDetail?.originalName && (
- 原始文件名: + + {t("dataManagement.labels.originalName")}: + {previewFileDetail.originalName}
)} {previewFileDetail?.fileType && (
- 文件类型: + + {t("dataManagement.labels.fileType")}: + {previewFileDetail.fileType}
)} {typeof previewFileDetail?.fileSize === "number" && (
- 文件大小: + + {t("dataManagement.labels.fileSize")}: + {formatBytes(previewFileDetail.fileSize || 0)}
)} {previewFileDetail?.status && (
- 状态: + + {t("dataManagement.labels.status")}: + {previewFileDetail.status}
)} {previewFileDetail?.tags && (
- 标签: + + {t("dataManagement.labels.tags")}: + {(() => { let raw = previewFileDetail.tags as any; - if (!raw) return "-"; + if (!raw) return t("dataManagement.defaults.empty"); if (typeof raw === "string") { try { @@ -602,7 +625,9 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { } } - if (!Array.isArray(raw) || raw.length === 0) return "-"; + if (!Array.isArray(raw) || raw.length === 0) { + return t("dataManagement.defaults.empty"); + } const labels: string[] = []; raw.forEach((tag: any) => { @@ -621,7 +646,7 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { } }); - if (!labels.length) return "-"; + if (!labels.length) return t("dataManagement.defaults.empty"); return labels.join(", "); })()} @@ -629,36 +654,48 @@ export default function Overview({ dataset, filesOperation, fetchDataset }) { )} {previewFileDetail?.tagsUpdatedAt && (
- 标签更新时间: + + {t("dataManagement.labels.tagsUpdatedAt")}: + {formatDateTime(previewFileDetail.tagsUpdatedAt)}
)} {previewFileDetail?.uploadTime && (
- 上传时间: + + {t("dataManagement.labels.uploadTime")}: + {formatDateTime(previewFileDetail.uploadTime)}
)} {previewFileDetail?.uploadedBy && (
- 上传者: + + {t("dataManagement.labels.uploadedBy")}: + {previewFileDetail.uploadedBy}
)} {previewFileDetail?.filePath && (
- 文件路径: + + {t("dataManagement.labels.filePath")}: + {previewFileDetail.filePath}
)} {previewFileDetail?.description && (
- 描述: + + {t("dataManagement.labels.description")}: + {previewFileDetail.description}
)}
- 标注信息: + + {t("dataManagement.labels.annotation")}: + {(() => { const raw = (previewFileDetail?.annotation diff --git a/frontend/src/pages/DataManagement/Home/DataManagement.tsx b/frontend/src/pages/DataManagement/Home/DataManagement.tsx index 61c037f54..55cdda5d7 100644 --- a/frontend/src/pages/DataManagement/Home/DataManagement.tsx +++ b/frontend/src/pages/DataManagement/Home/DataManagement.tsx @@ -12,7 +12,7 @@ import { useEffect, useMemo, useState } from "react"; import { SearchControls } from "@/components/SearchControls"; import CardView from "@/components/CardView"; import type { Dataset } from "@/pages/DataManagement/dataset.model"; -import { datasetStatusMap, datasetTypeMap, mapDataset } from "../dataset.const"; +import { getDatasetStatusMap, getDatasetTypeMap, mapDataset } from "../dataset.const"; import useFetchData from "@/hooks/useFetchData"; import { downloadDatasetUsingGet, @@ -27,10 +27,14 @@ import { import { formatBytes } from "@/utils/unit"; import EditDataset from "../Create/EditDataset"; import ImportConfiguration from "../Detail/components/ImportConfiguration"; +import { useTranslation } from "react-i18next"; export default function DatasetManagementPage() { const navigate = useNavigate(); const { message } = App.useApp(); + const { t } = useTranslation(); + const datasetStatusMap = getDatasetStatusMap(t); + const datasetTypeMap = getDatasetTypeMap(t); const [viewMode, setViewMode] = useState<"card" | "list">("card"); const [editDatasetOpen, setEditDatasetOpen] = useState(false); const [currentDataset, setCurrentDataset] = useState(null); @@ -46,33 +50,33 @@ export default function DatasetManagementPage() { const statistics = { size: [ { - title: "数据集总数", + title: t("dataManagement.stats.totalDatasets"), value: data?.totalDatasets || 0, }, { - title: "文件总数", + title: t("dataManagement.stats.totalFiles"), value: data?.totalFiles || 0, }, { - title: "总大小", + title: t("dataManagement.stats.totalSize"), value: formatBytes(data?.totalSize) || '0 B', }, ], count: [ { - title: "文本", + title: t("dataManagement.stats.text"), value: data?.count?.text || 0, }, { - title: "图像", + title: t("dataManagement.stats.image"), value: data?.count?.image || 0, }, { - title: "音频", + title: t("dataManagement.stats.audio"), value: data?.count?.audio || 0, }, { - title: "视频", + title: t("dataManagement.stats.video"), value: data?.count?.video || 0, }, ], @@ -94,22 +98,22 @@ export default function DatasetManagementPage() { () => [ { key: "type", - label: "类型", + label: t("dataManagement.filters.type"), options: [...Object.values(datasetTypeMap)], }, { key: "status", - label: "状态", + label: t("dataManagement.filters.status"), options: [...Object.values(datasetStatusMap)], }, { key: "tags", - label: "标签", + label: t("dataManagement.filters.tags"), mode: "multiple", options: tags.map((tag) => ({ label: tag, value: tag })), }, ], - [tags] + [tags, datasetStatusMap, datasetTypeMap, t] ); const { @@ -123,7 +127,7 @@ export default function DatasetManagementPage() { handleKeywordChange, } = useFetchData( queryDatasetsUsingGet, - mapDataset, + (dataset) => mapDataset(dataset, t), 30000, // 30秒轮询间隔 true, // 自动刷新 [fetchStatistics], // 额外的轮询函数 @@ -132,14 +136,14 @@ export default function DatasetManagementPage() { const handleDownloadDataset = async (dataset: Dataset) => { await downloadDatasetUsingGet(dataset.id, dataset.name); - message.success("数据集下载成功"); + message.success(t("dataManagement.messages.downloadSuccess")); }; const handleDeleteDataset = async (id: number) => { if (!id) return; await deleteDatasetByIdUsingDelete(id); fetchData({ pageOffset: 0 }); - message.success("数据删除成功"); + message.success(t("dataManagement.messages.deleteSuccess")); }; const handleImportData = (dataset: Dataset) => { @@ -150,14 +154,14 @@ export default function DatasetManagementPage() { const handleRefresh = async (showMessage = true) => { await fetchData({ pageOffset: 0 }); if (showMessage) { - message.success("数据已刷新"); + message.success(t("dataManagement.messages.refreshSuccess")); } }; const operations = [ { key: "edit", - label: "编辑", + label: t("dataManagement.actions.edit"), icon: , onClick: (item: Dataset) => { setCurrentDataset(item); @@ -166,7 +170,7 @@ export default function DatasetManagementPage() { }, { key: "import", - label: "导入", + label: t("dataManagement.actions.import"), icon: , onClick: (item: Dataset) => { handleImportData(item); @@ -174,7 +178,7 @@ export default function DatasetManagementPage() { }, { key: "download", - label: "下载", + label: t("dataManagement.actions.download"), icon: , onClick: (item: Dataset) => { if (!item.id) return; @@ -183,13 +187,13 @@ export default function DatasetManagementPage() { }, { key: "delete", - label: "删除", + label: t("dataManagement.actions.delete"), danger: true, confirm: { - title: "确认删除该数据集?", - description: "删除后该数据集将无法恢复,请谨慎操作。", - okText: "删除", - cancelText: "取消", + title: t("dataManagement.confirm.deleteDatasetTitle"), + description: t("dataManagement.confirm.deleteDatasetDesc"), + okText: t("dataManagement.confirm.deleteConfirm"), + cancelText: t("dataManagement.confirm.deleteCancel"), okType: "danger", }, icon: , @@ -199,7 +203,7 @@ export default function DatasetManagementPage() { const columns = [ { - title: "名称", + title: t("dataManagement.columns.name"), dataIndex: "name", key: "name", fixed: "left", @@ -213,13 +217,13 @@ export default function DatasetManagementPage() { ), }, { - title: "类型", + title: t("dataManagement.columns.type"), dataIndex: "type", key: "type", width: 100, }, { - title: "状态", + title: t("dataManagement.columns.status"), dataIndex: "status", key: "status", render: (status: any) => { @@ -232,13 +236,13 @@ export default function DatasetManagementPage() { width: 120, }, { - title: "大小", + title: t("dataManagement.columns.size"), dataIndex: "size", key: "size", width: 120, }, { - title: "文件数", + title: t("dataManagement.columns.fileCount"), dataIndex: "fileCount", key: "fileCount", width: 100, @@ -250,26 +254,26 @@ export default function DatasetManagementPage() { // width: 120, // }, { - title: "存储路径", + title: t("dataManagement.columns.storagePath"), dataIndex: "targetLocation", key: "targetLocation", width: 200, ellipsis: true, }, { - title: "创建时间", + title: t("dataManagement.columns.createdAt"), dataIndex: "createdAt", key: "createdAt", width: 180, }, { - title: "更新时间", + title: t("dataManagement.columns.updatedAt"), dataIndex: "updatedAt", key: "updatedAt", width: 180, }, { - title: "操作", + title: t("dataManagement.columns.actions"), key: "actions", width: 200, fixed: "right", @@ -327,7 +331,7 @@ export default function DatasetManagementPage() {
{/* Header */}
-

数据管理

+

{t("dataManagement.title")}

{/* tasks */} } > - 创建数据集 + {t("dataManagement.actions.createDataset")}
@@ -364,7 +368,7 @@ export default function DatasetManagementPage() { setSearchParams({ ...searchParams, filter: {} })} diff --git a/frontend/src/pages/DataManagement/dataset.const.tsx b/frontend/src/pages/DataManagement/dataset.const.tsx index d5b70901e..ae7debbe2 100644 --- a/frontend/src/pages/DataManagement/dataset.const.tsx +++ b/frontend/src/pages/DataManagement/dataset.const.tsx @@ -29,7 +29,7 @@ import { ScanText, } from "lucide-react"; -export const datasetTypeMap: Record< +export function getDatasetTypeMap(t: (key: string) => string): Record< string, { value: DatasetType; @@ -40,50 +40,52 @@ export const datasetTypeMap: Record< iconColor?: string; children: DatasetSubType[]; } -> = { - [DatasetType.TEXT]: { - value: DatasetType.TEXT, - label: "文本", - order: 1, - icon: ScanText, - iconColor: "#A78BFA", - children: [ - DatasetSubType.TEXT_DOCUMENT, - DatasetSubType.TEXT_WEB, - DatasetSubType.TEXT_DIALOG, - ], - description: "用于处理和分析文本数据的数据集", - }, - [DatasetType.IMAGE]: { - value: DatasetType.IMAGE, - label: "图像", - order: 2, - icon: Image, - iconColor: "#38BDF8", - children: [DatasetSubType.IMAGE_IMAGE, DatasetSubType.IMAGE_CAPTION], - description: "用于处理和分析图像数据的数据集", - }, - [DatasetType.AUDIO]: { - value: DatasetType.AUDIO, - label: "音频", - order: 3, - icon: Music, - iconColor: "#F59E0B", - children: [DatasetSubType.AUDIO_AUDIO, DatasetSubType.AUDIO_JSONL], - description: "用于处理和分析音频数据的数据集", - }, - [DatasetType.VIDEO]: { - value: DatasetType.VIDEO, - label: "视频", - order: 3, - icon: Film, - iconColor: "#22D3EE", - children: [DatasetSubType.VIDEO_VIDEO, DatasetSubType.VIDEO_JSONL], - description: "用于处理和分析视频数据的数据集", - }, -}; +> { + return { + [DatasetType.TEXT]: { + value: DatasetType.TEXT, + label: t("dataManagement.datasetTypes.text"), + order: 1, + icon: ScanText, + iconColor: "#A78BFA", + children: [ + DatasetSubType.TEXT_DOCUMENT, + DatasetSubType.TEXT_WEB, + DatasetSubType.TEXT_DIALOG, + ], + description: t("dataManagement.datasetTypeDesc.text"), + }, + [DatasetType.IMAGE]: { + value: DatasetType.IMAGE, + label: t("dataManagement.datasetTypes.image"), + order: 2, + icon: Image, + iconColor: "#38BDF8", + children: [DatasetSubType.IMAGE_IMAGE, DatasetSubType.IMAGE_CAPTION], + description: t("dataManagement.datasetTypeDesc.image"), + }, + [DatasetType.AUDIO]: { + value: DatasetType.AUDIO, + label: t("dataManagement.datasetTypes.audio"), + order: 3, + icon: Music, + iconColor: "#F59E0B", + children: [DatasetSubType.AUDIO_AUDIO, DatasetSubType.AUDIO_JSONL], + description: t("dataManagement.datasetTypeDesc.audio"), + }, + [DatasetType.VIDEO]: { + value: DatasetType.VIDEO, + label: t("dataManagement.datasetTypes.video"), + order: 3, + icon: Film, + iconColor: "#22D3EE", + children: [DatasetSubType.VIDEO_VIDEO, DatasetSubType.VIDEO_JSONL], + description: t("dataManagement.datasetTypeDesc.video"), + }, + }; +} -export const datasetSubTypeMap: Record< +export function getDatasetSubTypeMap(t: (key: string) => string): Record< string, { value: DatasetSubType; @@ -93,133 +95,153 @@ export const datasetSubTypeMap: Record< icon?: any; color?: string; } -> = { - [DatasetSubType.TEXT_DOCUMENT]: { - value: DatasetSubType.TEXT_DOCUMENT, - label: "文档", - color: "blue", - icon: FileText, - description: "用于存储和处理各种文档格式的文本数据集", - }, - [DatasetSubType.TEXT_WEB]: { - value: DatasetSubType.TEXT_WEB, - label: "网页", - color: "cyan", - icon: FileCode, - description: "用于存储和处理网页数据集", - }, - [DatasetSubType.TEXT_DIALOG]: { - value: DatasetSubType.TEXT_DIALOG, - label: "对话", - color: "teal", - icon: MessageCircleMore, - description: "用于存储和处理对话数据的数据集", - }, - [DatasetSubType.IMAGE_IMAGE]: { - value: DatasetSubType.IMAGE_IMAGE, - label: "图像", - color: "green", - icon: FileImage, - description: "用于大规模图像预训练模型的数据集", - }, - [DatasetSubType.IMAGE_CAPTION]: { - value: DatasetSubType.IMAGE_CAPTION, - label: "图像+caption", - color: "lightgreen", - icon: ImagePlus, - description: "用于图像标题生成的数据集", - }, - [DatasetSubType.AUDIO_AUDIO]: { - value: DatasetSubType.AUDIO_AUDIO, - label: "音频", - color: "purple", - icon: Music, - description: "用于大规模音频预训练模型的数据集", - }, - [DatasetSubType.AUDIO_JSONL]: { - value: DatasetSubType.AUDIO_JSONL, - label: "音频+JSONL", - color: "purple", - icon: FileMusic, - description: "用于大规模音频预训练模型的数据集", - }, - [DatasetSubType.VIDEO_VIDEO]: { - value: DatasetSubType.VIDEO_VIDEO, - label: "视频", - color: "orange", - icon: Video, - description: "用于大规模视频预训练模型的数据集", - }, - [DatasetSubType.VIDEO_JSONL]: { - value: DatasetSubType.VIDEO_JSONL, - label: "视频+JSONL", - color: "orange", - icon: Videotape, - description: "用于大规模视频预训练模型的数据集", - }, -}; +> { + return { + [DatasetSubType.TEXT_DOCUMENT]: { + value: DatasetSubType.TEXT_DOCUMENT, + label: t("dataManagement.datasetSubTypes.textDocument"), + color: "blue", + icon: FileText, + description: t("dataManagement.datasetSubTypeDesc.textDocument"), + }, + [DatasetSubType.TEXT_WEB]: { + value: DatasetSubType.TEXT_WEB, + label: t("dataManagement.datasetSubTypes.textWeb"), + color: "cyan", + icon: FileCode, + description: t("dataManagement.datasetSubTypeDesc.textWeb"), + }, + [DatasetSubType.TEXT_DIALOG]: { + value: DatasetSubType.TEXT_DIALOG, + label: t("dataManagement.datasetSubTypes.textDialog"), + color: "teal", + icon: MessageCircleMore, + description: t("dataManagement.datasetSubTypeDesc.textDialog"), + }, + [DatasetSubType.IMAGE_IMAGE]: { + value: DatasetSubType.IMAGE_IMAGE, + label: t("dataManagement.datasetSubTypes.imageImage"), + color: "green", + icon: FileImage, + description: t("dataManagement.datasetSubTypeDesc.imageImage"), + }, + [DatasetSubType.IMAGE_CAPTION]: { + value: DatasetSubType.IMAGE_CAPTION, + label: t("dataManagement.datasetSubTypes.imageCaption"), + color: "lightgreen", + icon: ImagePlus, + description: t("dataManagement.datasetSubTypeDesc.imageCaption"), + }, + [DatasetSubType.AUDIO_AUDIO]: { + value: DatasetSubType.AUDIO_AUDIO, + label: t("dataManagement.datasetSubTypes.audioAudio"), + color: "purple", + icon: Music, + description: t("dataManagement.datasetSubTypeDesc.audioAudio"), + }, + [DatasetSubType.AUDIO_JSONL]: { + value: DatasetSubType.AUDIO_JSONL, + label: t("dataManagement.datasetSubTypes.audioJsonl"), + color: "purple", + icon: FileMusic, + description: t("dataManagement.datasetSubTypeDesc.audioJsonl"), + }, + [DatasetSubType.VIDEO_VIDEO]: { + value: DatasetSubType.VIDEO_VIDEO, + label: t("dataManagement.datasetSubTypes.videoVideo"), + color: "orange", + icon: Video, + description: t("dataManagement.datasetSubTypeDesc.videoVideo"), + }, + [DatasetSubType.VIDEO_JSONL]: { + value: DatasetSubType.VIDEO_JSONL, + label: t("dataManagement.datasetSubTypes.videoJsonl"), + color: "orange", + icon: Videotape, + description: t("dataManagement.datasetSubTypeDesc.videoJsonl"), + }, + }; +} -export const datasetStatusMap = { - [DatasetStatus.ACTIVE]: { - label: "活跃", - value: DatasetStatus.ACTIVE, - color: "#409f17ff", - icon: , - }, - [DatasetStatus.PROCESSING]: { - label: "处理中", - value: DatasetStatus.PROCESSING, - color: "#2673e5", - icon: , - }, - [DatasetStatus.INACTIVE]: { - label: "未激活", - value: DatasetStatus.INACTIVE, - color: "#4f4444ff", - icon: , - }, - [DatasetStatus.DRAFT]: { - label: "草稿", - value: DatasetStatus.DRAFT, - color: "#a1a1a1ff", - icon: , - }, -}; +export function getDatasetStatusMap(t: (key: string) => string) { + return { + [DatasetStatus.ACTIVE]: { + label: t("dataManagement.datasetStatus.active"), + value: DatasetStatus.ACTIVE, + color: "#409f17ff", + icon: , + }, + [DatasetStatus.PROCESSING]: { + label: t("dataManagement.datasetStatus.processing"), + value: DatasetStatus.PROCESSING, + color: "#2673e5", + icon: , + }, + [DatasetStatus.INACTIVE]: { + label: t("dataManagement.datasetStatus.inactive"), + value: DatasetStatus.INACTIVE, + color: "#4f4444ff", + icon: , + }, + [DatasetStatus.DRAFT]: { + label: t("dataManagement.datasetStatus.draft"), + value: DatasetStatus.DRAFT, + color: "#a1a1a1ff", + icon: , + }, + }; +} -export const dataSourceMap: Record = { - [DataSource.UPLOAD]: { label: "本地上传", value: DataSource.UPLOAD }, - [DataSource.COLLECTION]: { label: "归集任务导入 ", value: DataSource.COLLECTION }, - // [DataSource.DATABASE]: { label: "数据库导入", value: DataSource.DATABASE }, - // [DataSource.NAS]: { label: "NAS导入", value: DataSource.NAS }, - // [DataSource.OBS]: { label: "OBS导入", value: DataSource.OBS }, -}; +export function getDataSourceMap(t: (key: string) => string): Record< + string, + { label: string; value: string } +> { + return { + [DataSource.UPLOAD]: { + label: t("dataManagement.dataSources.upload"), + value: DataSource.UPLOAD, + }, + [DataSource.COLLECTION]: { + label: t("dataManagement.dataSources.collection"), + value: DataSource.COLLECTION, + }, + }; +} -export const dataSourceOptions = Object.values(dataSourceMap); +export function getDataSourceOptions(t: (key: string) => string) { + return Object.values(getDataSourceMap(t)); +} -export function mapDataset(dataset: AnyObject): Dataset { +export function mapDataset(dataset: AnyObject, t: (key: string) => string): Dataset { + const datasetTypeMap = getDatasetTypeMap(t); + const datasetStatusMap = getDatasetStatusMap(t); const { icon: IconComponent, iconColor } = datasetTypeMap[dataset?.datasetType] || {}; return { ...dataset, key: dataset.id, - type: datasetTypeMap[dataset.datasetType]?.label || "未知", + type: datasetTypeMap[dataset.datasetType]?.label || t("dataManagement.defaults.unknown"), size: formatBytes(dataset.totalSize || 0), - createdAt: formatDateTime(dataset.createdAt) || "--", - updatedAt: formatDateTime(dataset?.updatedAt) || "--", + createdAt: formatDateTime(dataset.createdAt) || t("dataManagement.defaults.empty"), + updatedAt: formatDateTime(dataset?.updatedAt) || t("dataManagement.defaults.empty"), icon: IconComponent ? : , iconColor: iconColor, status: datasetStatusMap[dataset.status], statistics: [ - { label: "文件数", value: dataset.fileCount || 0 }, - { label: "大小", value: formatBytes(dataset.totalSize || 0) }, + { label: t("dataManagement.labels.fileCount"), value: dataset.fileCount || 0 }, + { label: t("dataManagement.labels.dataSize"), value: formatBytes(dataset.totalSize || 0) }, ], lastModified: dataset.updatedAt, }; } -export const datasetTypes = Object.values(datasetTypeMap).map((type) => ({ - ...type, - options: type.children?.map( - (subType) => datasetSubTypeMap[subType as keyof typeof datasetSubTypeMap] - ), -})); +export function getDatasetTypes(t: (key: string) => string) { + const datasetTypeMap = getDatasetTypeMap(t); + const datasetSubTypeMap = getDatasetSubTypeMap(t); + return Object.values(datasetTypeMap).map((type) => ({ + ...type, + options: type.children?.map( + (subType) => datasetSubTypeMap[subType as keyof typeof datasetSubTypeMap] + ), + })); +} From db5dc18fa01214bede9e88d60ee69247b8f02936 Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Fri, 6 Feb 2026 16:54:51 +0800 Subject: [PATCH 2/6] fix: resolve the bug that the Statistics does not change after changed the language --- frontend/src/pages/DataManagement/Home/DataManagement.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/DataManagement/Home/DataManagement.tsx b/frontend/src/pages/DataManagement/Home/DataManagement.tsx index 55cdda5d7..fea877b70 100644 --- a/frontend/src/pages/DataManagement/Home/DataManagement.tsx +++ b/frontend/src/pages/DataManagement/Home/DataManagement.tsx @@ -92,7 +92,8 @@ export default function DatasetManagementPage() { setTags(data.map((tag) => tag.name)); }; fetchTags(); - }, []); + fetchStatistics(); + }, [t]); const filterOptions = useMemo( () => [ From bec45ad5da99053e49f913dd29fac68ffd74e353 Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Fri, 6 Feb 2026 16:57:57 +0800 Subject: [PATCH 3/6] fix: solve the bug of not initializing the lineage graph when only creating a dataset --- .../datamanagement/application/DatasetApplicationService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java index ae5c9b742..f9e3a55c8 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java @@ -111,8 +111,10 @@ private void addDatasetToGraph(Dataset dataset, CollectionTaskDetailResponse col collectionEdge.setDescription(dataset.getDescription()); collectionEdge.setFromNodeId(collectionNode.getId()); collectionEdge.setToNodeId(datasetNode.getId()); + lineageService.generateGraph(collectionNode, collectionEdge, datasetNode); + } else { + lineageService.generateGraph(datasetNode, null, null); } - lineageService.generateGraph(collectionNode, collectionEdge, datasetNode); } public DatasetLineage getDatasetLineage(String datasetId) { From 926898a24902e130febdec2e4827df5eb7dbecc4 Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Fri, 6 Feb 2026 17:05:57 +0800 Subject: [PATCH 4/6] fix: solve the bug of data quality --- .../pages/DataManagement/Detail/components/DataQuality.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx b/frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx index b29ef3fcc..3e0bd50ee 100644 --- a/frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx +++ b/frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx @@ -1,10 +1,6 @@ -// typescript -// File: `frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx` import React, { useMemo } from "react"; -// Run `npm install antd lucide-react` if your editor reports "Module is not installed" import { Card, Table, Progress } from "antd"; import { AlertTriangle, Tags, BarChart3 } from "lucide-react"; -import DevelopmentInProgress from "@/components/DevelopmentInProgress"; import { Dataset } from "@/pages/DataManagement/dataset.model.ts"; import { useTranslation } from "react-i18next"; @@ -34,6 +30,7 @@ function randInt(min: number, max: number) { } function getMockMetrics(datasetType: DatasetType, stats: FileStats) { + const { t } = useTranslation(); const total = Math.max(1, stats.totalFiles || 1); const corrupted = stats.corrupted || 0; const unlabeled = stats.unlabeled || 0; From 8205e8ae3ad87b38d80e391cfcfa1e0e0d0bffc8 Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Mon, 9 Feb 2026 10:59:23 +0800 Subject: [PATCH 5/6] fix: fixed display issue of labels on data set cards --- frontend/src/components/CardView.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/CardView.tsx b/frontend/src/components/CardView.tsx index ec74f957b..84a29f6c9 100644 --- a/frontend/src/components/CardView.tsx +++ b/frontend/src/components/CardView.tsx @@ -7,6 +7,7 @@ import ActionDropdown from "./ActionDropdown"; import { Database } from "lucide-react"; interface BadgeItem { + name: string; label: string; color?: string; background?: string; @@ -84,7 +85,7 @@ const TagsRenderer = ({ tags }: { tags?: Array }) => { if (typeof tag === "string") { tagElement.textContent = tag; } else { - tagElement.textContent = tag.label; + tagElement.textContent = tag.label ? tag.label : tag.name; } tempDiv.appendChild(tagElement); tagElements.push(tagElement); @@ -145,7 +146,7 @@ const TagsRenderer = ({ tags }: { tags?: Array }) => { : { background: tag.background, color: tag.color } } > - {typeof tag === "string" ? tag : tag.label} + {typeof tag === "string" ? tag : (tag.label ? tag.label : tag.name)} ))}
@@ -164,7 +165,7 @@ const TagsRenderer = ({ tags }: { tags?: Array }) => { : { background: tag.background, color: tag.color } } > - {typeof tag === "string" ? tag : tag.label} + {typeof tag === "string" ? tag : (tag.label ? tag.label : tag.name)} ))} {hiddenTags.length > 0 && ( @@ -255,7 +256,7 @@ function CardView(props: CardViewProps) { color: item.tags[0].color, }} > - {item.tags[0].label} + {item.tags[0].label ? item.tags[0].label : item.tags[0].name} )} {item?.status && ( From 915f06a806b4ea9ac5ce49b2852b861df6f3addf Mon Sep 17 00:00:00 2001 From: uname <2986773479@qq.com> Date: Mon, 9 Feb 2026 15:18:42 +0800 Subject: [PATCH 6/6] feature: Improve the internationalization of dataset pages --- frontend/src/components/AddTagPopover.tsx | 18 ++-- .../src/components/business/TagManagement.tsx | 24 +++--- frontend/src/i18n/locales/en/common.json | 79 ++++++++++++++---- frontend/src/i18n/locales/zh/common.json | 78 ++++++++++++++---- .../Detail/components/DataLineageFlow.tsx | 82 ++++++++++--------- .../Detail/components/DataQuality.tsx | 24 +++--- .../DataManagement/Home/DataManagement.tsx | 27 +++--- 7 files changed, 212 insertions(+), 120 deletions(-) diff --git a/frontend/src/components/AddTagPopover.tsx b/frontend/src/components/AddTagPopover.tsx index 92d53047f..8ce7370c5 100644 --- a/frontend/src/components/AddTagPopover.tsx +++ b/frontend/src/components/AddTagPopover.tsx @@ -1,6 +1,7 @@ import { Button, Input, Popover, theme, Tag, Empty } from "antd"; import { PlusOutlined } from "@ant-design/icons"; import { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; interface Tag { id: number; @@ -22,6 +23,7 @@ export default function AddTagPopover({ onCreateAndTag, }: AddTagPopoverProps) { const { token } = theme.useToken(); + const { t } = useTranslation(); const [showPopover, setShowPopover] = useState(false); const [newTag, setNewTag] = useState(""); @@ -68,12 +70,12 @@ export default function AddTagPopover({ content={

- 添加标签 + {t("tagManagement.addTag")}

{/* Available Tags */} {availableTags?.length ? (
-
选择现有标签
+
{t("tagManagement.selectExistingTags")}
{availableTags.map((tag) => (
) : ( - + )} {/* Create New Tag */}
-
创建新标签
+
{t("tagManagement.createNewTag")}
setNewTag(e.target.value)} className="h-8 text-sm" @@ -109,13 +111,13 @@ export default function AddTagPopover({ disabled={!newTag.trim()} type="primary" > - 添加 + {t("tagManagement.createTag")}
} @@ -126,7 +128,7 @@ export default function AddTagPopover({ className="cursor-pointer" onClick={() => setShowPopover(true)} > - 添加标签 + {t("tagManagement.addTag")} diff --git a/frontend/src/components/business/TagManagement.tsx b/frontend/src/components/business/TagManagement.tsx index 42913b372..51c55bcb1 100644 --- a/frontend/src/components/business/TagManagement.tsx +++ b/frontend/src/components/business/TagManagement.tsx @@ -3,6 +3,7 @@ import { Drawer, Input, Button, App } from "antd"; import { PlusOutlined } from "@ant-design/icons"; import { Edit, Save, TagIcon, X, Trash } from "lucide-react"; import { TagItem } from "@/pages/DataManagement/dataset.model"; +import { useTranslation } from "react-i18next"; interface CustomTagProps { isEditable?: boolean; @@ -105,6 +106,7 @@ const TagManager: React.FC = ({ }) => { const [showTagManager, setShowTagManager] = useState(false); const { message } = App.useApp(); + const { t } = useTranslation(); const [tags, setTags] = useState<{ id: number; name: string }[]>([]); const [newTag, setNewTag] = useState(""); const [editingTag, setEditingTag] = useState(null); @@ -117,7 +119,7 @@ const TagManager: React.FC = ({ const { data } = await onFetch?.(); setTags(data || []); } catch (e) { - message.error("获取标签失败"); + message.error(t("tagManagement.messages.fetchFailed")); } }; @@ -129,9 +131,9 @@ const TagManager: React.FC = ({ }); fetchTags(); setNewTag(""); - message.success("标签添加成功"); + message.success(t("tagManagement.messages.addSuccess")); } catch (error) { - message.error("添加标签失败"); + message.error(t("tagManagement.messages.addFailed")); } }; @@ -140,9 +142,9 @@ const TagManager: React.FC = ({ try { await onDelete?.(tag.id); fetchTags(); - message.success("标签删除成功"); + message.success(t("tagManagement.messages.deleteSuccess")); } catch (error) { - message.error("删除标签失败"); + message.error(t("tagManagement.messages.deleteFailed")); } }; @@ -150,9 +152,9 @@ const TagManager: React.FC = ({ try { await onUpdate?.({ ...oldTag, name: newTag }); fetchTags(); - message.success("标签更新成功"); + message.success(t("tagManagement.messages.updateSuccess")); } catch (error) { - message.error("更新标签失败"); + message.error(t("tagManagement.messages.updateFailed")); } }; @@ -192,19 +194,19 @@ const TagManager: React.FC = ({ icon={} onClick={() => setShowTagManager(true)} > - 标签管理 + {t("tagManagement.manageTags")} setShowTagManager(false)} - title="标签管理" + title={t("tagManagement.manageTags")} width={500} >
{/* Add New Tag */}
setNewTag(e.target.value)} @@ -220,7 +222,7 @@ const TagManager: React.FC = ({ disabled={!newTag.trim()} icon={} > - 添加 + {t("tagManagement.createTag")}
diff --git a/frontend/src/i18n/locales/en/common.json b/frontend/src/i18n/locales/en/common.json index 89bb85bcc..33ddca7bf 100644 --- a/frontend/src/i18n/locales/en/common.json +++ b/frontend/src/i18n/locales/en/common.json @@ -206,17 +206,17 @@ "messages": { "loadTemplatesFailed": "Failed to load collection templates", "createSuccess": "Task created successfully", - "errorWithDetail": "{message}: {detail}", + "errorWithDetail": "{{message}}: {{detail}}", "jsonObjectRequired": "Must be a JSON object", "jsonObjectInvalid": "Please enter a valid JSON object", "jsonFormatError": "Invalid JSON format", - "jsonFormatErrorWithMessage": "Invalid JSON format: {message}" + "jsonFormatErrorWithMessage": "Invalid JSON format: {{message}}" }, "placeholders": { "enter": "Please enter", "select": "Please select", - "enterWithLabel": "Please enter {label}", - "selectWithLabel": "Please select {label}" + "enterWithLabel": "Please enter {{label}}", + "selectWithLabel": "Please select {{label}}" } }, "scheduler": { @@ -356,7 +356,7 @@ "deleteDatasetTitle": "Confirm delete this dataset?", "deleteDatasetDesc": "This dataset cannot be recovered after deletion. Proceed with caution.", "deleteFolderTitle": "Confirm delete folder?", - "deleteFolderDesc": "Deleting folder \"{name}\" will remove all files and subfolders. This action cannot be undone.", + "deleteFolderDesc": "Deleting folder \"{{name}}\" will remove all files and subfolders. This action cannot be undone.", "deleteConfirm": "Delete", "deleteCancel": "Cancel" }, @@ -368,14 +368,14 @@ "tabQuality": "Data Quality", "sectionBasicInfo": "Basic Information", "sectionFileList": "File List", - "selectedFiles": "Selected {count} files", + "selectedFiles": "Selected {{count}} files", "goUp": "Go up", - "currentPath": "Current path: {path}", - "totalItems": "Total {total} items", - "previewTitle": "File Preview: {name}", + "currentPath": "Current path: {{path}}", + "totalItems": "Total {{total}} items", + "previewTitle": "File Preview: {{name}}", "previewEmpty": "No preview available or unsupported file type.", "previewInfoTitle": "File Info", - "editTitle": "Edit Dataset - {name}" + "editTitle": "Edit Dataset - {{name}}" }, "labels": { "id": "ID", @@ -409,7 +409,7 @@ }, "lineage": { "title": "Data Lineage", - "stats": "{nodes} nodes · {edges} edges", + "stats": "{{nodes}} nodes · {{edges}} edges", "legendDatasource": "Data Source", "legendDataset": "Dataset", "legendModel": "Model", @@ -422,6 +422,7 @@ "edgeTypeSynthesis": "Data Synthesis", "edgeTypeRatio": "Data Ratio", "edgeTypeDefault": "Processing Flow", + "edgeTypeDefaultLabel": "Processing Flow", "detailBasicInfo": "Basic Information", "detailId": "ID:", "detailName": "Name:", @@ -436,13 +437,21 @@ "detailNoDownstream": "No downstream impacts", "processDetail": "Process Detail", "processId": "Process ID:", + "relationships": "Upstream and Downstream", "processUpstream": "Upstream:", "processDownstream": "Downstream:", + "processDescription": "Description", + "processEmpty": "No process description", "nodeTypeDatasource": "Data Source", "nodeTypeDataset": "Dataset", "nodeTypeModel": "Model", "nodeTypeKnowledge": "Knowledge Base", - "filesCount": "{count} files" + "filesCount": "{{count}} files · {{size}}", + "statusRunning": "Running", + "statusStopped": "Stopped", + "statusActive": "Active", + "statusInactive": "Inactive", + "statusDraft": "Draft" }, "quality": { "titleDistribution": "Quality Distribution", @@ -461,8 +470,8 @@ "metricColumnIntegrity": "Column Integrity", "metricDuplicateRate": "Duplicate Rate", "recommendationTitle": "Quality Improvement Suggestions", - "recommendationReviewLowQuality": "Review or recollect {count} low-quality samples", - "recommendationSupplementMetadata": "Check and supplement missing metadata fields (missing: {missing})", + "recommendationReviewLowQuality": "Review or recollect {{count}} low-quality samples", + "recommendationSupplementMetadata": "Check and supplement missing metadata fields (missing: {{missing}})", "recommendationBalanceDistribution": "Consider adding more underrepresented samples to balance distribution", "imageClarity": "Image Clarity", "colorConsistency": "Color Consistency", @@ -476,7 +485,21 @@ "fileIntegrity": "File Integrity", "fieldIntegrity": "Field Integrity", "columnIntegrity": "Column Integrity", - "duplicateRate": "Duplicate Rate" + "duplicateRate": "Duplicate Rate", + "labelDistribution": { + "title": "Label Distribution Details", + "category": "Category", + "labelName": "Label Name", + "count": "Count", + "percentage": "Percentage", + "noData": "No label distribution data", + "noDataSubtitle": "No label distribution data available", + "statisticsTitle": "Dataset Label Statistics", + "statisticsSummary": "{{categoryCount}} categories, {{totalLabels}} label samples", + "detailsCardTitle": "Label Distribution Details", + "totalLabels": "Total: {{count}} labels", + "moreLabels": "And {{count}} more labels..." + } }, "import": { "title": "Import Data", @@ -486,7 +509,7 @@ "uploadFolderHint": "Drag folder here or click to select folder", "fileLabel": "File:", "folderLabel": "Folder:", - "warningTypeMismatch": "Dataset type is {type}. Some selected files may not match; please confirm.", + "warningTypeMismatch": "Dataset type is {{type}}. Some selected files may not match; please confirm.", "warningUseFolderUpload": "To upload a folder, use the \"Local Folder Upload\" area on the right.", "warningUseFileUpload": "To upload a single file, use the \"Local File Upload\" area on the left.", "obsEndpoint": "Endpoint", @@ -553,7 +576,7 @@ "collection": "Collection Task Import" } }, - "common": { + "common": { "actions": { "createTask": "Create Task" }, @@ -570,5 +593,27 @@ "pending": "Ready" } } + }, + "tagManagement": { + "manageTags": "Manage Tags", + "addTag": "Add Tag", + "tagName": "Tag Name", + "tagNamePlaceholder": "Enter tag name...", + "createTag": "Create", + "selectExistingTags": "Select existing tags", + "createNewTag": "Create new tag", + "newTagNamePlaceholder": "Enter new tag name...", + "noAvailableTags": "No available tags. Please create a tag first.", + "cancel": "Cancel", + "messages": { + "fetchFailed": "Failed to fetch tags", + "addSuccess": "Tag added successfully", + "addFailed": "Failed to add tag", + "deleteSuccess": "Tag deleted successfully", + "deleteFailed": "Failed to delete tag", + "updateSuccess": "Tag updated successfully", + "updateFailed": "Failed to update tag" + }, + "uncategorized": "Uncategorized" } } diff --git a/frontend/src/i18n/locales/zh/common.json b/frontend/src/i18n/locales/zh/common.json index 135ac83b2..72f890bd2 100644 --- a/frontend/src/i18n/locales/zh/common.json +++ b/frontend/src/i18n/locales/zh/common.json @@ -206,17 +206,17 @@ "messages": { "loadTemplatesFailed": "加载归集模板失败", "createSuccess": "任务创建成功", - "errorWithDetail": "{message}:{detail}", + "errorWithDetail": "{{message}}:{{detail}}", "jsonObjectRequired": "必须是JSON对象", "jsonObjectInvalid": "请输入合法的JSON对象", "jsonFormatError": "JSON格式错误", - "jsonFormatErrorWithMessage": "JSON格式错误:{message}" + "jsonFormatErrorWithMessage": "JSON格式错误:{{message}}" }, "placeholders": { "enter": "请输入", "select": "请选择", - "enterWithLabel": "请输入{label}", - "selectWithLabel": "请选择{label}" + "enterWithLabel": "请输入{{label}}", + "selectWithLabel": "请选择{{label}}" } }, "scheduler": { @@ -356,7 +356,7 @@ "deleteDatasetTitle": "确认删除该数据集?", "deleteDatasetDesc": "删除后该数据集将无法恢复,请谨慎操作。", "deleteFolderTitle": "确认删除文件夹?", - "deleteFolderDesc": "删除文件夹 \"{name}\" 将同时删除其中的所有文件和子文件夹,此操作不可恢复。", + "deleteFolderDesc": "删除文件夹 \"{{name}}\" 将同时删除其中的所有文件和子文件夹,此操作不可恢复。", "deleteConfirm": "删除", "deleteCancel": "取消" }, @@ -368,14 +368,14 @@ "tabQuality": "数据质量", "sectionBasicInfo": "基本信息", "sectionFileList": "文件列表", - "selectedFiles": "已选择 {count} 个文件", + "selectedFiles": "已选择 {{count}} 个文件", "goUp": "返回上一级", - "currentPath": "当前路径: {path}", - "totalItems": "共 {total} 条", - "previewTitle": "文件预览:{name}", + "currentPath": "当前路径: {{path}}", + "totalItems": "共 {{total}} 条", + "previewTitle": "文件预览:{{name}}", "previewEmpty": "暂无预览内容,或当前文件类型暂不支持预览。", "previewInfoTitle": "文件信息", - "editTitle": "编辑数据集 - {name}" + "editTitle": "编辑数据集 - {{name}}" }, "labels": { "id": "ID", @@ -409,7 +409,7 @@ }, "lineage": { "title": "数据血缘图", - "stats": "{nodes} 个节点 · {edges} 条边", + "stats": "{{nodes}} 个节点 · {{edges}} 条边", "legendDatasource": "数据源", "legendDataset": "数据集", "legendModel": "模型", @@ -436,13 +436,21 @@ "detailNoDownstream": "无下游影响", "processDetail": "流程详情", "processId": "流程ID:", + "relationships": "上下游关系", "processUpstream": "上游:", "processDownstream": "下游:", + "processDescription": "描述", + "processEmpty": "无流程描述", "nodeTypeDatasource": "数据源", "nodeTypeDataset": "数据集", "nodeTypeModel": "模型", "nodeTypeKnowledge": "知识库", - "filesCount": "{count} 个文件" + "filesCount": "{{count}} 个文件 · {{size}}", + "statusRunning": "运行中", + "statusStopped": "已停止", + "statusActive": "活跃", + "statusInactive": "未激活", + "statusDraft": "草稿" }, "quality": { "titleDistribution": "质量分布", @@ -461,8 +469,8 @@ "metricColumnIntegrity": "列完整性", "metricDuplicateRate": "重复率", "recommendationTitle": "质量改进建议", - "recommendationReviewLowQuality": "建议对{count}项低质量样本进行复查或重新采集", - "recommendationSupplementMetadata": "检查并补充缺失的元数据字段(现有缺失:{missing})", + "recommendationReviewLowQuality": "建议对{{count}}项低质量样本进行复查或重新采集", + "recommendationSupplementMetadata": "检查并补充缺失的元数据字段(现有缺失:{{missing}})", "recommendationBalanceDistribution": "考虑增加更多低代表性样本以平衡数据分布", "imageClarity": "图像清晰度", "colorConsistency": "色彩一致性", @@ -476,7 +484,21 @@ "fileIntegrity": "文件完整性", "fieldIntegrity": "字段完整性", "columnIntegrity": "列完整性", - "duplicateRate": "重复率" + "duplicateRate": "重复率", + "labelDistribution": { + "title": "标签分布明细", + "category": "类别", + "labelName": "标签名称", + "count": "数量", + "percentage": "占比", + "noData": "暂无标签分布数据", + "noDataSubtitle": "暂无标签分布数据可用", + "statisticsTitle": "数据集标签统计", + "statisticsSummary": "共 {{categoryCount}} 个类别,{{totalLabels}} 个标签样本", + "detailsCardTitle": "标签分布明细", + "totalLabels": "总计: {{count}} 个标签", + "moreLabels": "还有 {{count}} 个标签..." + } }, "import": { "title": "导入数据", @@ -486,7 +508,7 @@ "uploadFolderHint": "拖拽文件夹到此处或点击选择文件夹", "fileLabel": "文件:", "folderLabel": "文件夹:", - "warningTypeMismatch": "当前数据集类型为 {type},本次选择的部分文件格式与该类型不太匹配,建议确认是否为预期数据。", + "warningTypeMismatch": "当前数据集类型为 {{type}},本次选择的部分文件格式与该类型不太匹配,建议确认是否为预期数据。", "warningUseFolderUpload": "如需上传文件夹,请使用右侧\"本地文件夹上传\"区域。", "warningUseFileUpload": "如需上传单个文件,请使用左侧\"本地文件上传\"区域。", "obsEndpoint": "Endpoint", @@ -553,7 +575,7 @@ "collection": "归集任务导入 " } }, - "common": { + "common": { "actions": { "createTask": "创建任务" }, @@ -570,5 +592,27 @@ "pending": "就绪" } } + }, + "tagManagement": { + "manageTags": "标签管理", + "addTag": "添加标签", + "tagName": "标签名称", + "tagNamePlaceholder": "输入标签名称...", + "createTag": "添加", + "selectExistingTags": "选择现有标签", + "createNewTag": "创建新标签", + "newTagNamePlaceholder": "输入新标签名称...", + "noAvailableTags": "没有可用标签,请先创建标签。", + "cancel": "取消", + "messages": { + "fetchFailed": "获取标签失败", + "addSuccess": "标签添加成功", + "addFailed": "添加标签失败", + "deleteSuccess": "标签删除成功", + "deleteFailed": "删除标签失败", + "updateSuccess": "标签更新成功", + "updateFailed": "更新标签失败" + }, + "uncategorized": "未分类" } } diff --git a/frontend/src/pages/DataManagement/Detail/components/DataLineageFlow.tsx b/frontend/src/pages/DataManagement/Detail/components/DataLineageFlow.tsx index 5e778d4c5..e5d47b602 100644 --- a/frontend/src/pages/DataManagement/Detail/components/DataLineageFlow.tsx +++ b/frontend/src/pages/DataManagement/Detail/components/DataLineageFlow.tsx @@ -4,6 +4,7 @@ import { Card, Badge } from "antd" import { Database, Table, Brain, BookOpen, X } from "lucide-react" import {Dataset} from "@/pages/DataManagement/dataset.model.ts"; import { queryDatasetLineageByIdUsingGet } from "@/pages/DataManagement/dataset.api.ts"; +import { useTranslation } from "react-i18next"; interface Node { id: string @@ -79,11 +80,11 @@ const nodeConfig = { } const edgeTypeLabels: Record = { - DATA_COLLECTION: "数据归集", - DATA_CLEANING: "数据处理", - DATA_LABELING: "数据标注", - DATA_SYNTHESIS: "数据合成", - DATA_RATIO: "数据配比", + DATA_COLLECTION: "dataManagement.lineage.edgeTypeCollection", + DATA_CLEANING: "dataManagement.lineage.edgeTypeCleaning", + DATA_LABELING: "dataManagement.lineage.edgeTypeLabeling", + DATA_SYNTHESIS: "dataManagement.lineage.edgeTypeSynthesis", + DATA_RATIO: "dataManagement.lineage.edgeTypeRatio", } const nodeTypeToUi: Record = { @@ -113,6 +114,7 @@ const layoutConfig = { } export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { + const { t } = useTranslation(); const [graphNodes, setGraphNodes] = useState([]) const [graphEdges, setGraphEdges] = useState([]) const [selectedNode, setSelectedNode] = useState(null) @@ -158,7 +160,7 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { id: edge.id || `${edge.fromNodeId}-${edge.toNodeId}-${index}`, from: edge.fromNodeId, to: edge.toNodeId, - label: edge.name || (edge.edgeType ? edgeTypeLabels[edge.edgeType] || edge.edgeType : "处理流程"), + label: edge.name || (edge.edgeType ? t(edgeTypeLabels[edge.edgeType]) || edge.edgeType : t("dataManagement.lineage.edgeTypeDefault")), edgeType: edge.edgeType, processId: edge.processId, description: edge.description, @@ -424,32 +426,32 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { [graphNodes.length, graphEdges.length] ) - return ( + return (
-
数据血缘图
+
{t("dataManagement.lineage.title")}
- {stats.nodes} 个节点 · {stats.edges} 条边 + {t("dataManagement.lineage.stats", { nodes: stats.nodes, edges: stats.edges })}
- 数据源 + {t("dataManagement.lineage.legendDatasource")} - 数据集 + {t("dataManagement.lineage.legendDataset")} - 模型 + {t("dataManagement.lineage.legendModel")} - 知识库 + {t("dataManagement.lineage.legendKnowledge")}
@@ -486,8 +488,8 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { {graphNodes.length === 0 && (
-
暂无血缘数据
-
完成归集或关联流程后将自动生成血缘图
+
{t("dataManagement.lineage.emptyTitle")}
+
{t("dataManagement.lineage.emptyDesc")}
)} @@ -564,7 +566,7 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { )} {node.fileCount !== undefined && (
- {node.fileCount} 个文件 · {node.size} + {t("dataManagement.lineage.filesCount", { count: node.fileCount, size: node.size })}
)}
@@ -605,10 +607,10 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { color: nodeConfig[selectedNode.type].color, }} > - {selectedNode.type === "datasource" && "数据源"} - {selectedNode.type === "dataset" && "数据集"} - {selectedNode.type === "model" && "模型"} - {selectedNode.type === "knowledge" && "知识库"} + {selectedNode.type === "datasource" && t("dataManagement.lineage.nodeTypeDatasource")} + {selectedNode.type === "dataset" && t("dataManagement.lineage.nodeTypeDataset")} + {selectedNode.type === "model" && t("dataManagement.lineage.nodeTypeModel")} + {selectedNode.type === "knowledge" && t("dataManagement.lineage.nodeTypeKnowledge")}
@@ -629,12 +631,12 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { {selectedNode.id}
- 名称: + {t("dataManagement.lineage.detailName")} {selectedNode.label}
{selectedNode.status && (
- 状态: + {t("dataManagement.lineage.detailStatus")}
- 文件数: + {t("dataManagement.lineage.detailFileCount")} {selectedNode.fileCount}
)} {selectedNode.size && (
- 数据大小: + {t("dataManagement.lineage.detailDataSize")} {selectedNode.size}
)} {selectedNode.updateTime && (
- 更新时间: + {t("dataManagement.lineage.detailUpdateTime")} {selectedNode.updateTime}
)} @@ -669,12 +671,12 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) {
-

描述

+

{t("dataManagement.lineage.detailDescription")}

{selectedNode.description}

-

上游依赖

+

{t("dataManagement.lineage.detailUpstream")}

{graphEdges .filter((e) => e.to === selectedNode.id) @@ -694,13 +696,13 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { ) : null })} {graphEdges.filter((e) => e.to === selectedNode.id).length === 0 && ( -

无上游依赖

+

{t("dataManagement.lineage.detailNoUpstream")}

)}
-

下游影响

+

{t("dataManagement.lineage.detailDownstream")}

{graphEdges .filter((e) => e.from === selectedNode.id) @@ -720,7 +722,7 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) { ) : null })} {graphEdges.filter((e) => e.from === selectedNode.id).length === 0 && ( -

无下游影响

+

{t("dataManagement.lineage.detailNoDownstream")}

)}
@@ -740,11 +742,11 @@ export default function DataLineageFlow({ dataset }: { dataset: Dataset }) {
-

流程详情

+

{t("dataManagement.lineage.processDetail")}

{selectedEdge.edgeType - ? edgeTypeLabels[selectedEdge.edgeType] || selectedEdge.edgeType - : "处理流程"} + ? t(edgeTypeLabels[selectedEdge.edgeType]) || selectedEdge.edgeType + : t("dataManagement.lineage.edgeTypeDefault")}
@@ -213,7 +215,7 @@ function LabelDistributionStats({ distribution }: LabelDistributionProps) { title={
- 标签分布明细 + {t("dataManagement.quality.labelDistribution.detailsCardTitle")}
} > @@ -244,7 +246,7 @@ function LabelDistributionStats({ distribution }: LabelDistributionProps) { >
- 总计: {categoryTotal} 个标签 + {t("dataManagement.quality.labelDistribution.totalLabels", { count: categoryTotal })}
{topLabels.map((item) => (
@@ -266,7 +268,7 @@ function LabelDistributionStats({ distribution }: LabelDistributionProps) { ))} {items.length > 5 && (
- 还有 {items.length - 5} 个标签... + {t("dataManagement.quality.labelDistribution.moreLabels", { count: items.length - 5 })}
)}
diff --git a/frontend/src/pages/DataManagement/Home/DataManagement.tsx b/frontend/src/pages/DataManagement/Home/DataManagement.tsx index fea877b70..1b47b80c5 100644 --- a/frontend/src/pages/DataManagement/Home/DataManagement.tsx +++ b/frontend/src/pages/DataManagement/Home/DataManagement.tsx @@ -86,15 +86,6 @@ export default function DatasetManagementPage() { const [tags, setTags] = useState([]); - useEffect(() => { - const fetchTags = async () => { - const { data } = await queryDatasetTagsUsingGet(); - setTags(data.map((tag) => tag.name)); - }; - fetchTags(); - fetchStatistics(); - }, [t]); - const filterOptions = useMemo( () => [ { @@ -135,8 +126,18 @@ export default function DatasetManagementPage() { 0 ); + useEffect(() => { + const fetchTags = async () => { + const { data } = await queryDatasetTagsUsingGet(); + setTags(data.map((tag) => tag.name)); + }; + fetchTags(); + fetchData(); + fetchStatistics(); + }, [t]); + const handleDownloadDataset = async (dataset: Dataset) => { - await downloadDatasetUsingGet(dataset.id, dataset.name); + await downloadDatasetUsingGet(dataset.id); message.success(t("dataManagement.messages.downloadSuccess")); }; @@ -248,12 +249,6 @@ export default function DatasetManagementPage() { key: "fileCount", width: 100, }, - // { - // title: "创建者", - // dataIndex: "createdBy", - // key: "createdBy", - // width: 120, - // }, { title: t("dataManagement.columns.storagePath"), dataIndex: "targetLocation",