feat: i18n support

This commit is contained in:
JustSong
2025-02-01 17:00:24 +08:00
parent 60f2776795
commit ae20aea555
13 changed files with 156 additions and 63 deletions

View File

@@ -18,6 +18,7 @@ import {
showWarning,
timestamp2string,
} from '../helpers';
import { useTranslation } from 'react-i18next';
import { ITEMS_PER_PAGE } from '../constants';
import { renderColorLabel, renderQuota } from '../helpers/render';
@@ -137,6 +138,7 @@ function renderDetail(log) {
}
const LogsTable = () => {
const { t } = useTranslation();
const [logs, setLogs] = useState([]);
const [showStat, setShowStat] = useState(false);
const [loading, setLoading] = useState(true);
@@ -309,14 +311,14 @@ const LogsTable = () => {
<>
<>
<Header as='h3'>
使用明细总消耗额度
{showStat && renderQuota(stat.quota)}
{t('log.usage_details')}{t('log.total_quota')}
{showStat && renderQuota(stat.quota, t)}
{!showStat && (
<span
onClick={handleEyeClick}
style={{ cursor: 'pointer', color: 'gray' }}
>
点击查看
{t('log.click_to_view')}
</span>
)}
@@ -554,7 +556,7 @@ const LogsTable = () => {
{log.completion_tokens ? log.completion_tokens : ''}
</Table.Cell>
<Table.Cell>
{log.quota ? renderQuota(log.quota, 6) : ''}
{log.quota ? renderQuota(log.quota, t, 6) : ''}
</Table.Cell>
</>
)}

View File

@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
Button,
Form,
@@ -25,39 +26,37 @@ function renderTimestamp(timestamp) {
return <>{timestamp2string(timestamp)}</>;
}
function renderStatus(status) {
function renderStatus(status, t) {
switch (status) {
case 1:
return (
<Label basic color='green'>
未使用
{t('redemption.status.unused')}
</Label>
);
case 2:
return (
<Label basic color='red'>
{' '}
已禁用{' '}
{t('redemption.status.disabled')}
</Label>
);
case 3:
return (
<Label basic color='grey'>
{' '}
已使用{' '}
{t('redemption.status.used')}
</Label>
);
default:
return (
<Label basic color='black'>
{' '}
未知状态{' '}
{t('redemption.status.unknown')}
</Label>
);
}
}
const RedemptionsTable = () => {
const { t } = useTranslation();
const [redemptions, setRedemptions] = useState([]);
const [loading, setLoading] = useState(true);
const [activePage, setActivePage] = useState(1);
@@ -260,8 +259,8 @@ const RedemptionsTable = () => {
<Table.Cell>
{redemption.name ? redemption.name : '无'}
</Table.Cell>
<Table.Cell>{renderStatus(redemption.status)}</Table.Cell>
<Table.Cell>{renderQuota(redemption.quota)}</Table.Cell>
<Table.Cell>{renderStatus(redemption.status, t)}</Table.Cell>
<Table.Cell>{renderQuota(redemption.quota, t)}</Table.Cell>
<Table.Cell>
{renderTimestamp(redemption.created_time)}
</Table.Cell>

View File

@@ -69,14 +69,14 @@ const TokensTable = () => {
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' },
];
const OPEN_LINK_OPTIONS = [
{ key: 'next', text: t('token.copy_options.next'), value: 'next' },
{ key: 'ama', text: t('token.copy_options.ama'), value: 'ama' },
{ key: 'opencat', text: t('token.copy_options.opencat'), value: 'opencat' },
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' }
{ key: 'lobe', text: t('token.copy_options.lobe'), value: 'lobechat' },
];
const [tokens, setTokens] = useState([]);
@@ -135,7 +135,8 @@ const TokensTable = () => {
let nextUrl;
if (nextLink) {
nextUrl = nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
nextUrl =
nextLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
} else {
nextUrl = `https://app.nextchat.dev/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
}
@@ -152,7 +153,9 @@ const TokensTable = () => {
url = nextUrl;
break;
case 'lobechat':
url = nextLink + `/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}/v1"}}}`;
url =
nextLink +
`/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}/v1"}}}`;
break;
default:
url = `sk-${key}`;
@@ -376,19 +379,21 @@ const TokensTable = () => {
.map((token, idx) => {
if (token.deleted) return <></>;
const copyOptionsWithHandlers = COPY_OPTIONS.map(option => ({
const copyOptionsWithHandlers = COPY_OPTIONS.map((option) => ({
...option,
onClick: async () => {
await onCopy(option.value, token.key);
}
},
}));
const openLinkOptionsWithHandlers = OPEN_LINK_OPTIONS.map(option => ({
...option,
onClick: async () => {
await onOpenLink(option.value, token.key);
}
}));
const openLinkOptionsWithHandlers = OPEN_LINK_OPTIONS.map(
(option) => ({
...option,
onClick: async () => {
await onOpenLink(option.value, token.key);
},
})
);
return (
<Table.Row key={token.id}>
@@ -473,7 +478,11 @@ const TokensTable = () => {
? t('token.buttons.disable')
: t('token.buttons.enable')}
</Button>
<Button size={'small'} as={Link} to={'/token/edit/' + token.id}>
<Button
size={'small'}
as={Link}
to={'/token/edit/' + token.id}
>
{t('token.buttons.edit')}
</Button>
</div>

View File

@@ -10,6 +10,7 @@ import {
} from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import { API, showError, showSuccess } from '../helpers';
import { useTranslation } from 'react-i18next';
import { ITEMS_PER_PAGE } from '../constants';
import {
@@ -33,6 +34,7 @@ function renderRole(role) {
}
const UsersTable = () => {
const { t } = useTranslation();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [activePage, setActivePage] = useState(1);
@@ -266,12 +268,12 @@ const UsersTable = () => {
<Table.Cell>
<Popup
content='剩余额度'
trigger={<Label basic>{renderQuota(user.quota)}</Label>}
trigger={<Label basic>{renderQuota(user.quota, t)}</Label>}
/>
<Popup
content='已用额度'
trigger={
<Label basic>{renderQuota(user.used_quota)}</Label>
<Label basic>{renderQuota(user.used_quota, t)}</Label>
}
/>
<Popup