feat(i18n): add internationalization support across multiple components
This commit is contained in:
@@ -36,6 +36,7 @@ import {
|
||||
SelectAll as SelectAllIcon,
|
||||
Clear as ClearIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { IChunk } from '@/interfaces/database/knowledge';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
|
||||
@@ -54,6 +55,7 @@ interface ChunkListResultProps {
|
||||
|
||||
function ChunkListResult(props: ChunkListResultProps) {
|
||||
const { doc_id, chunks, total, loading, error, page, pageSize, onPageChange, onRefresh, docName } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
// 选择状态
|
||||
const [selectedChunks, setSelectedChunks] = useState<string[]>([]);
|
||||
@@ -140,7 +142,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
<Paper sx={{ p: 3, textAlign: 'center' }}>
|
||||
<CircularProgress />
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
||||
正在加载chunk数据...
|
||||
{t('chunkPage.loadingChunkData')}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
@@ -160,10 +162,10 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
return (
|
||||
<Paper sx={{ p: 3, textAlign: 'center' }}>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
暂无chunk数据
|
||||
{t('chunkPage.noChunkData')}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
|
||||
该文档还没有生成chunk数据,请检查文档是否已完成解析
|
||||
{t('chunkPage.noChunkDataDescription')}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
@@ -189,7 +191,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
onChange={(e) => handleSelectAll(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label={`全选 (已选择 ${selectedChunks.length} 个)`}
|
||||
label={`${t('chunkPage.selectAll')} (${t('chunkPage.selected')} ${selectedChunks.length} ${t('chunkPage.items')})`}
|
||||
/>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
@@ -205,7 +207,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
启用 ({selectedDisabledCount})
|
||||
{t('chunkPage.enable')} ({selectedDisabledCount})
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -218,7 +220,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
禁用 ({selectedEnabledCount})
|
||||
{t('chunkPage.disable')} ({selectedEnabledCount})
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -230,7 +232,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
删除 ({selectedChunks.length})
|
||||
{t('common.delete')} ({selectedChunks.length})
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@@ -239,7 +241,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
清空选择
|
||||
{t('chunkPage.clearSelection')}
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -250,10 +252,10 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Typography variant="h6">
|
||||
Chunk列表 (第 {page} 页,共 {totalPages} 页)
|
||||
{t('chunkPage.chunkList')} ({t('chunkPage.page')} {page} {t('chunkPage.pageOf')} {totalPages} {t('chunkPage.pages')})
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
共 {total} 个chunk
|
||||
{t('chunkPage.total')} {total} {t('chunkPage.chunks')}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -307,7 +309,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
{chunk.image_id && (
|
||||
<Tooltip title="包含图片">
|
||||
<Tooltip title={t('chunkPage.containsImage')}>
|
||||
<Avatar sx={{ bgcolor: 'info.main', width: 24, height: 24 }}>
|
||||
<ImageIcon fontSize="small" />
|
||||
</Avatar>
|
||||
@@ -315,7 +317,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
)}
|
||||
<Chip
|
||||
icon={chunk.available_int === 1 ? <VisibilityIcon /> : <VisibilityOffIcon />}
|
||||
label={chunk.available_int === 1 ? '已启用' : '未启用'}
|
||||
label={chunk.available_int === 1 ? t('chunkPage.enabled') : t('chunkPage.disabled')}
|
||||
size="small"
|
||||
color={chunk.available_int === 1 ? 'success' : 'default'}
|
||||
variant={chunk.available_int === 1 ? 'filled' : 'outlined'}
|
||||
@@ -326,7 +328,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
{/* 内容区域 */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
内容预览
|
||||
{t('chunkPage.contentPreview')}
|
||||
</Typography>
|
||||
<Paper
|
||||
variant="outlined"
|
||||
@@ -347,7 +349,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
color: 'text.primary',
|
||||
}}
|
||||
>
|
||||
{chunk.content_with_weight || '无内容'}
|
||||
{chunk.content_with_weight || t('chunkPage.noContent')}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
@@ -356,7 +358,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
{chunk.image_id && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
相关图片
|
||||
{t('chunkPage.relatedImage')}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -370,7 +372,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
<Box
|
||||
component="img"
|
||||
src={`${import.meta.env.VITE_API_BASE_URL}/v1/document/image/${chunk.image_id}`}
|
||||
alt="Chunk相关图片"
|
||||
alt={t('chunkPage.chunkRelatedImage')}
|
||||
sx={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '200px',
|
||||
@@ -390,14 +392,14 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
{((chunk.important_kwd ?? []).length > 0 || (chunk.question_kwd ?? []).length > 0 || (chunk.tag_kwd ?? []).length > 0) && (
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
关键词信息
|
||||
{t('chunkPage.keywordInfo')}
|
||||
</Typography>
|
||||
|
||||
<Stack spacing={1}>
|
||||
{chunk.important_kwd && chunk.important_kwd.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mr: 1 }}>
|
||||
重要:
|
||||
{t('chunkPage.important')}:
|
||||
</Typography>
|
||||
{chunk.important_kwd.map((keyword, kwdIndex) => (
|
||||
<Chip
|
||||
@@ -415,7 +417,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
{chunk.question_kwd && chunk.question_kwd.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mr: 1 }}>
|
||||
问题:
|
||||
{t('chunkPage.question')}:
|
||||
</Typography>
|
||||
{chunk.question_kwd.map((keyword, kwdIndex) => (
|
||||
<Chip
|
||||
@@ -433,7 +435,7 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
{chunk.tag_kwd && chunk.tag_kwd.length > 0 && (
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mr: 1 }}>
|
||||
标签:
|
||||
{t('chunkPage.tag')}:
|
||||
</Typography>
|
||||
{chunk.tag_kwd.map((keyword, kwdIndex) => (
|
||||
<Chip
|
||||
@@ -476,22 +478,22 @@ function ChunkListResult(props: ChunkListResultProps) {
|
||||
open={deleteDialogOpen}
|
||||
onClose={() => setDeleteDialogOpen(false)}
|
||||
>
|
||||
<DialogTitle>确认删除</DialogTitle>
|
||||
<DialogTitle>{t('dialog.confirmDelete')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
确定要删除选中的 {selectedChunks.length} 个chunk吗?此操作不可撤销。
|
||||
{t('chunkPage.confirmDeleteChunks', { count: selectedChunks.length })}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setDeleteDialogOpen(false)}>
|
||||
取消
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleDeleteChunks}
|
||||
color="error"
|
||||
disabled={operationLoading}
|
||||
>
|
||||
{operationLoading ? '删除中...' : '确认删除'}
|
||||
{operationLoading ? t('chunkPage.deleting') : t('dialog.confirmDelete')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
Download as DownloadIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
import type { IKnowledge, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import KnowledgeBreadcrumbs from '@/pages/knowledge/components/KnowledgeBreadcrumbs';
|
||||
@@ -25,6 +26,7 @@ interface DocumentPreviewProps {}
|
||||
function DocumentPreview(props: DocumentPreviewProps) {
|
||||
const { kb_id, doc_id } = useParams<{ kb_id: string; doc_id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [kb, setKb] = useState<IKnowledge | null>(null);
|
||||
const [documentObj, setDocumentObj] = useState<IKnowledgeFile | null>(null);
|
||||
@@ -41,7 +43,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!kb_id || !doc_id) {
|
||||
setError('缺少必要的参数');
|
||||
setError(t('chunkPage.missingParams'));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -67,7 +69,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
loadDocumentFile();
|
||||
} catch (err) {
|
||||
console.error('获取数据失败:', err);
|
||||
setError('获取数据失败,请稍后重试');
|
||||
setError(t('chunkPage.fetchDataFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -103,13 +105,13 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
const url = URL.createObjectURL(fileResponse.data);
|
||||
setFileUrl(url);
|
||||
} else {
|
||||
setError('文件格式不支持预览');
|
||||
setError(t('chunkPage.fileFormatNotSupported'));
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log('err', err);
|
||||
if (err.name !== 'AbortError' && err.name !== 'CanceledError') {
|
||||
console.error('获取文档文件失败:', err);
|
||||
setError('获取文档文件失败,请稍后重试');
|
||||
setError(t('chunkPage.getDocumentFileFailed'));
|
||||
}
|
||||
} finally {
|
||||
setFileLoading(false);
|
||||
@@ -168,7 +170,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
<CardMedia
|
||||
component="img"
|
||||
image={fileUrl}
|
||||
alt={documentObj?.name || '文档预览'}
|
||||
alt={documentObj?.name || t('chunkPage.documentPreview')}
|
||||
sx={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '80vh',
|
||||
@@ -207,7 +209,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
} else {
|
||||
return (
|
||||
<Alert severity="info" sx={{ mt: 2 }}>
|
||||
此文件类型不支持在线预览,请下载后查看。
|
||||
{t('chunkPage.fileTypeNotSupportedPreview')}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -230,7 +232,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
onClick={handleGoBack}
|
||||
sx={{ mt: 2 }}
|
||||
>
|
||||
返回
|
||||
{t('common.back')}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
@@ -242,21 +244,21 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
<KnowledgeBreadcrumbs
|
||||
kbItems={[
|
||||
{
|
||||
label: '知识库',
|
||||
label: t('chunkPage.knowledgeBase'),
|
||||
path: '/knowledge'
|
||||
},
|
||||
{
|
||||
label: kb?.name || '知识库详情',
|
||||
label: kb?.name || t('chunkPage.knowledgeBaseDetail'),
|
||||
path: `/knowledge/${kb_id}`
|
||||
}
|
||||
]}
|
||||
extraItems={[
|
||||
{
|
||||
label: documentObj?.name || '文档详情',
|
||||
label: documentObj?.name || t('chunkPage.documentDetail'),
|
||||
path: `/chunk/parsed-result?kb_id=${kb_id}&doc_id=${doc_id}`
|
||||
},
|
||||
{
|
||||
label: '文件预览'
|
||||
label: t('chunkPage.filePreview')
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -265,7 +267,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Box>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
文件预览
|
||||
{t('chunkPage.filePreview')}
|
||||
</Typography>
|
||||
{documentObj && (
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
@@ -280,7 +282,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
onClick={handleGoBack}
|
||||
variant="outlined"
|
||||
>
|
||||
返回
|
||||
{t('common.back')}
|
||||
</Button>
|
||||
|
||||
{fileUrl && (
|
||||
@@ -289,7 +291,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
onClick={handleDownload}
|
||||
variant="contained"
|
||||
>
|
||||
下载文件
|
||||
{t('chunkPage.downloadFile')}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
@@ -306,7 +308,7 @@ function DocumentPreview(props: DocumentPreviewProps) {
|
||||
{fileLoading && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '200px' }}>
|
||||
<CircularProgress />
|
||||
<Typography sx={{ ml: 2 }}>正在加载文件...</Typography>
|
||||
<Typography sx={{ ml: 2 }}>{t('chunkPage.loadingFile')}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CardContent
|
||||
} from "@mui/material";
|
||||
import { Search as SearchIcon, Visibility as VisibilityIcon } from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useChunkList } from '@/hooks/chunk-hooks';
|
||||
import ChunkListResult from './components/ChunkListResult';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
@@ -19,6 +20,7 @@ import type { IKnowledge, IKnowledgeFile } from '@/interfaces/database/knowledge
|
||||
import KnowledgeBreadcrumbs from '@/pages/knowledge/components/KnowledgeBreadcrumbs';
|
||||
|
||||
function ChunkParsedResult() {
|
||||
const { t } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const kb_id = searchParams.get('kb_id');
|
||||
@@ -93,7 +95,7 @@ function ChunkParsedResult() {
|
||||
return (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Alert severity="error">
|
||||
缺少必要的参数:知识库ID或文档ID
|
||||
{t('chunkPage.missingParams')}
|
||||
</Alert>
|
||||
</Box>
|
||||
);
|
||||
@@ -105,17 +107,17 @@ function ChunkParsedResult() {
|
||||
<KnowledgeBreadcrumbs
|
||||
kbItems={[
|
||||
{
|
||||
label: '知识库',
|
||||
label: t('chunkPage.knowledgeBase'),
|
||||
path: '/knowledge'
|
||||
},
|
||||
{
|
||||
label: knowledgeBase?.name || '知识库详情',
|
||||
label: knowledgeBase?.name || t('chunkPage.knowledgeBaseDetail'),
|
||||
path: `/knowledge/${kb_id}`
|
||||
}
|
||||
]}
|
||||
extraItems={[
|
||||
{
|
||||
label: document?.name || '文档详情'
|
||||
label: document?.name || t('chunkPage.documentDetail')
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -124,10 +126,10 @@ function ChunkParsedResult() {
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
|
||||
<Box>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
文档Chunk解析结果
|
||||
{t('chunkPage.documentChunkResult')}
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
查看文档 "{document?.name}" 的所有chunk数据
|
||||
{t('chunkPage.viewDocument')} "{document?.name}" {t('chunkPage.allChunkData')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Card>
|
||||
@@ -136,7 +138,7 @@ function ChunkParsedResult() {
|
||||
{total}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
总Chunk数量
|
||||
{t('chunkPage.totalChunkCount')}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -146,7 +148,7 @@ function ChunkParsedResult() {
|
||||
onClick={handleViewFile}
|
||||
sx={{ ml: 2 }}
|
||||
>
|
||||
查看文件
|
||||
{t('chunkPage.viewFile')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
@@ -155,7 +157,7 @@ function ChunkParsedResult() {
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="搜索chunk内容..."
|
||||
placeholder={t('chunkPage.searchChunkPlaceholder')}
|
||||
value={searchKeyword}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
InputProps={{
|
||||
|
||||
Reference in New Issue
Block a user