refactor: add admin full i18n support

pull/90/head
moonrailgun 2 years ago
parent 4c168eb81f
commit 7c76344a55

@ -142,7 +142,7 @@ export const ModalCreateGroup: React.FC = React.memo(() => {
<div className="text-center mb-2"> <div className="text-center mb-2">
{/* TODO: upload avatar */} {/* TODO: upload avatar */}
<Avatar size={80} name={name} /> <Avatar className="mx-auto" size={80} name={name} />
</div> </div>
<div> <div>

@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Button, ButtonProps, Confirm } from 'react-admin'; import { Button, ButtonProps, Confirm, useTranslate } from 'react-admin';
interface Props extends Pick<ButtonProps, 'label'> { interface Props extends Pick<ButtonProps, 'label'> {
component?: React.ComponentType<ButtonProps>; component?: React.ComponentType<ButtonProps>;
@ -8,10 +8,12 @@ interface Props extends Pick<ButtonProps, 'label'> {
onConfirm?: () => void; onConfirm?: () => void;
} }
export const ButtonWithConfirm: React.FC<Props> = React.memo((props) => { export const ButtonWithConfirm: React.FC<Props> = React.memo((props) => {
const translate = useTranslate();
const { const {
component: ButtonComponent = Button, component: ButtonComponent = Button,
confirmTitle = '确认要进行该操作么?', confirmTitle = translate('custom.common.confirmTitle'),
confirmContent = '该操作不可撤回', confirmContent = translate('custom.common.confirmContent'),
} = props; } = props;
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);

@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import { useTranslate } from 'react-admin';
import { Card, Box, Typography, CardActions, Button } from '@mui/material'; import { Card, Box, Typography, CardActions, Button } from '@mui/material';
import HomeIcon from '@mui/icons-material/Home'; import HomeIcon from '@mui/icons-material/Home';
import CodeIcon from '@mui/icons-material/Code'; import CodeIcon from '@mui/icons-material/Code';
import logoSvg from './logo.svg'; import logoSvg from './logo.svg';
export const Welcome: React.FC = React.memo(() => { export const Welcome: React.FC = React.memo(() => {
const translate = useTranslate();
return ( return (
<Card <Card
sx={{ sx={{
@ -22,11 +25,11 @@ export const Welcome: React.FC = React.memo(() => {
<Box display="flex"> <Box display="flex">
<Box flex="1"> <Box flex="1">
<Typography variant="h5" component="h2" gutterBottom> <Typography variant="h5" component="h2" gutterBottom>
使 Tailchat {translate('custom.dashboard.welcomeTitle')}
</Typography> </Typography>
<Box maxWidth="40em"> <Box maxWidth="40em">
<Typography variant="body1" component="p" gutterBottom> <Typography variant="body1" component="p" gutterBottom>
Tailchat {translate('custom.dashboard.welcomeDesc')}
</Typography> </Typography>
</Box> </Box>
<CardActions <CardActions
@ -46,7 +49,7 @@ export const Welcome: React.FC = React.memo(() => {
startIcon={<HomeIcon />} startIcon={<HomeIcon />}
target="__blank" target="__blank"
> >
访 {translate('custom.dashboard.welcomeHomepage')}
</Button> </Button>
<Button <Button
variant="contained" variant="contained"
@ -54,7 +57,7 @@ export const Welcome: React.FC = React.memo(() => {
startIcon={<CodeIcon />} startIcon={<CodeIcon />}
target="__blank" target="__blank"
> >
{translate('custom.dashboard.welcomeSourcecode')}
</Button> </Button>
</CardActions> </CardActions>
</Box> </Box>

@ -5,7 +5,7 @@ import PersonIcon from '@mui/icons-material/Person';
import MessageIcon from '@mui/icons-material/Message'; import MessageIcon from '@mui/icons-material/Message';
import GroupIcon from '@mui/icons-material/Group'; import GroupIcon from '@mui/icons-material/Group';
import AttachFileIcon from '@mui/icons-material/AttachFile'; import AttachFileIcon from '@mui/icons-material/AttachFile';
import { useGetList } from 'react-admin'; import { useGetList, useTranslate } from 'react-admin';
import { Grid } from '@mui/material'; import { Grid } from '@mui/material';
export const Dashboard: React.FC = React.memo(() => { export const Dashboard: React.FC = React.memo(() => {
@ -26,6 +26,8 @@ export const Dashboard: React.FC = React.memo(() => {
pagination: { page: 1, perPage: 1 }, pagination: { page: 1, perPage: 1 },
}); });
const translate = useTranslate();
return ( return (
<div> <div>
<Welcome /> <Welcome />
@ -35,7 +37,7 @@ export const Dashboard: React.FC = React.memo(() => {
<CardWithIcon <CardWithIcon
to="/admin/users" to="/admin/users"
icon={PersonIcon} icon={PersonIcon}
title={'用户数'} title={translate('custom.dashboard.userCount')}
subtitle={usersNum} subtitle={usersNum}
/> />
</Grid> </Grid>
@ -43,7 +45,7 @@ export const Dashboard: React.FC = React.memo(() => {
<CardWithIcon <CardWithIcon
to="/admin/users" to="/admin/users"
icon={PersonIcon} icon={PersonIcon}
title={'临时用户数'} title={translate('custom.dashboard.tempUserCount')}
subtitle={tempUsersNum} subtitle={tempUsersNum}
/> />
</Grid> </Grid>
@ -51,7 +53,7 @@ export const Dashboard: React.FC = React.memo(() => {
<CardWithIcon <CardWithIcon
to="/admin/messages" to="/admin/messages"
icon={MessageIcon} icon={MessageIcon}
title={'总消息数'} title={translate('custom.dashboard.messageCount')}
subtitle={messageNum} subtitle={messageNum}
/> />
</Grid> </Grid>
@ -59,7 +61,7 @@ export const Dashboard: React.FC = React.memo(() => {
<CardWithIcon <CardWithIcon
to="/admin/groups" to="/admin/groups"
icon={GroupIcon} icon={GroupIcon}
title={'总群组数'} title={translate('custom.dashboard.groupCount')}
subtitle={groupNum} subtitle={groupNum}
/> />
</Grid> </Grid>
@ -67,7 +69,7 @@ export const Dashboard: React.FC = React.memo(() => {
<CardWithIcon <CardWithIcon
to="/admin/file" to="/admin/file"
icon={AttachFileIcon} icon={AttachFileIcon}
title={'总文件数'} title={translate('custom.dashboard.fileCount')}
subtitle={fileNum} subtitle={fileNum}
/> />
</Grid> </Grid>

@ -5,6 +5,24 @@ export const englishCustom = {
panel: 'Panel', panel: 'Panel',
name: 'Name', name: 'Name',
permission: 'Permission', permission: 'Permission',
confirmTitle: 'Are you sure you want to perform this operation?',
confirmContent: 'This action cannot be undone',
},
menu: {
network: 'Tailchat Network',
socket: 'Socket.IO TCP',
},
dashboard: {
welcomeTitle: 'Welcome to Tailchat Admin',
welcomeDesc:
'Tailchat is a completely open source instant messaging application',
welcomeHomepage: 'Visit the official website',
welcomeSourcecode: 'Browse the source code',
userCount: 'User Count',
tempUserCount: 'Temp User Count',
messageCount: 'Message Count',
groupCount: 'Group Count',
fileCount: 'File Count',
}, },
users: { users: {
search: 'Search nickname or email', search: 'Search nickname or email',
@ -27,6 +45,22 @@ export const englishCustom = {
groupPanel: 'Panel Group', groupPanel: 'Panel Group',
pluginPanel: 'Plugin Panel', pluginPanel: 'Plugin Panel',
}, },
network: {
nodeList: 'Node List',
id: 'ID',
hostname: 'Host Name',
cpuUsage: 'CPU Usage',
ipList: 'IP List',
sdkVersion: 'SDK Version',
serviceList: 'Service List',
actionList: 'Action List',
eventList: 'Event List',
},
socketio: {
tip1: 'The server URL is:',
tip2: 'The account password is the account password of Tailchat Admin',
btn: 'Open the Admin platform',
},
}, },
}; };
@ -37,6 +71,23 @@ export const chineseCustom = {
panel: '面板', panel: '面板',
name: '名称', name: '名称',
permission: '权限', permission: '权限',
confirmTitle: '确认要进行该操作么?',
confirmContent: '该操作不可撤回',
},
menu: {
network: 'Tailchat 网络',
socket: 'Socket.IO 长链接',
},
dashboard: {
welcomeTitle: '欢迎使用 Tailchat 后台管理程序',
welcomeDesc: 'Tailchat 是一个完全开源的即时通讯应用',
welcomeHomepage: '访问官网',
welcomeSourcecode: '浏览源码',
userCount: '用户数',
tempUserCount: '临时用户数',
messageCount: '总消息数',
groupCount: '总群组数',
fileCount: '总文件数',
}, },
users: { users: {
search: '搜索昵称或邮箱', search: '搜索昵称或邮箱',
@ -58,5 +109,21 @@ export const chineseCustom = {
groupPanel: '面板分组', groupPanel: '面板分组',
pluginPanel: '插件面板', pluginPanel: '插件面板',
}, },
network: {
nodeList: '节点列表',
id: 'ID',
hostname: '主机名',
cpuUsage: 'CPU占用',
ipList: 'IP地址列表',
sdkVersion: 'SDK版本',
serviceList: '服务列表',
actionList: '操作列表',
eventList: '事件列表',
},
socketio: {
tip1: '服务器URL为:',
tip2: '账号密码为Tailchat后台的账号密码',
btn: '打开管理平台',
},
}, },
}; };

@ -4,12 +4,14 @@ import {
MenuProps, MenuProps,
ResourceMenuItem, ResourceMenuItem,
useResourceDefinitions, useResourceDefinitions,
useTranslate,
} from 'react-admin'; } from 'react-admin';
import FilterDramaIcon from '@mui/icons-material/FilterDrama'; import FilterDramaIcon from '@mui/icons-material/FilterDrama';
import LinkIcon from '@mui/icons-material/Link'; import LinkIcon from '@mui/icons-material/Link';
export const TailchatMenu: React.FC<MenuProps> = React.memo((props) => { export const TailchatMenu: React.FC<MenuProps> = React.memo((props) => {
const resources = useResourceDefinitions(); const resources = useResourceDefinitions();
const translate = useTranslate();
return ( return (
<Menu {...props}> <Menu {...props}>
@ -21,13 +23,13 @@ export const TailchatMenu: React.FC<MenuProps> = React.memo((props) => {
<Menu.Item <Menu.Item
to="/admin/network" to="/admin/network"
primaryText="Tailchat 网络" primaryText={translate('custom.menu.network')}
leftIcon={<FilterDramaIcon />} leftIcon={<FilterDramaIcon />}
/> />
<Menu.Item <Menu.Item
to="/admin/socketio" to="/admin/socketio"
primaryText="Socket.IO 长链接" primaryText={translate('custom.menu.socket')}
leftIcon={<LinkIcon />} leftIcon={<LinkIcon />}
/> />
</Menu> </Menu>

@ -13,11 +13,13 @@ import {
} from '@mui/material'; } from '@mui/material';
import _uniq from 'lodash/uniq'; import _uniq from 'lodash/uniq';
import { ChipItems } from '../../components/ChipItems'; import { ChipItems } from '../../components/ChipItems';
import { useTranslate } from 'react-admin';
/** /**
* Tailchat * Tailchat
*/ */
export const TailchatNetwork: React.FC = React.memo(() => { export const TailchatNetwork: React.FC = React.memo(() => {
const translate = useTranslate();
const { data, loading } = useRequest(async () => { const { data, loading } = useRequest(async () => {
const { data } = await request('/network/all'); const { data } = await request('/network/all');
@ -37,16 +39,16 @@ export const TailchatNetwork: React.FC = React.memo(() => {
}} }}
> >
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
{translate('custom.network.nodeList')}
</Typography> </Typography>
<Table sx={{ minWidth: 650 }} aria-label="simple table"> <Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell>ID</TableCell> <TableCell>{translate('custom.network.id')}</TableCell>
<TableCell></TableCell> <TableCell>{translate('custom.network.hostname')}</TableCell>
<TableCell>CPU</TableCell> <TableCell>{translate('custom.network.cpuUsage')}</TableCell>
<TableCell>IP</TableCell> <TableCell>{translate('custom.network.ipList')}</TableCell>
<TableCell>SDK</TableCell> <TableCell>{translate('custom.network.sdkVersion')}</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@ -71,21 +73,21 @@ export const TailchatNetwork: React.FC = React.memo(() => {
</Table> </Table>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
{translate('custom.network.serviceList')}
</Typography> </Typography>
<Box flexWrap="wrap" overflow="hidden"> <Box flexWrap="wrap" overflow="hidden">
<ChipItems items={_uniq<string>(data.services ?? [])} /> <ChipItems items={_uniq<string>(data.services ?? [])} />
</Box> </Box>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
{translate('custom.network.actionList')}
</Typography> </Typography>
<Box flexWrap="wrap" overflow="hidden"> <Box flexWrap="wrap" overflow="hidden">
<ChipItems items={_uniq<string>(data.actions ?? [])} /> <ChipItems items={_uniq<string>(data.actions ?? [])} />
</Box> </Box>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
{translate('custom.network.eventList')}
</Typography> </Typography>
<Box flexWrap="wrap" overflow="hidden"> <Box flexWrap="wrap" overflow="hidden">
<ChipItems items={_uniq<string>(data.events ?? [])} /> <ChipItems items={_uniq<string>(data.events ?? [])} />

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { useTranslate } from 'react-admin';
import { Typography, CardActions, Button, Box } from '@mui/material'; import { Typography, CardActions, Button, Box } from '@mui/material';
import { Card, CardContent } from '@mui/material'; import { Card, CardContent } from '@mui/material';
@ -6,6 +7,7 @@ import { Card, CardContent } from '@mui/material';
* SocketIO * SocketIO
*/ */
export const SocketIOAdmin: React.FC = React.memo(() => { export const SocketIOAdmin: React.FC = React.memo(() => {
const translate = useTranslate();
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
return ( return (
@ -13,13 +15,13 @@ export const SocketIOAdmin: React.FC = React.memo(() => {
<Card> <Card>
<CardContent> <CardContent>
<Typography component="div"> <Typography component="div">
URL:{' '} {translate('custom.socketio.tip1')}{' '}
<strong> <strong>
{protocol}://{window.location.host} {protocol}://{window.location.host}
</strong> </strong>
</Typography> </Typography>
<Typography component="div"> <Typography component="div">
Tailchat {translate('custom.socketio.tip2')}
</Typography> </Typography>
</CardContent> </CardContent>
<CardActions> <CardActions>
@ -29,7 +31,7 @@ export const SocketIOAdmin: React.FC = React.memo(() => {
window.open('https://admin.socket.io/'); window.open('https://admin.socket.io/');
}} }}
> >
{translate('custom.socketio.btn')}
</Button> </Button>
</CardActions> </CardActions>
</Card> </Card>

Loading…
Cancel
Save