Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 63 additions & 63 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,75 +1,75 @@
# 端口号
# PORT=3000
# 前端基础URL
# FRONTEND_BASE_URL=https://your-frontend-url.com
# Port number (Số cổng)
PORT=3000
# Frontend base URL (URL cơ sở giao diện người dùng)
FRONTEND_BASE_URL=https://your-frontend-url.com


# 调试相关配置
# 启用pprof
# ENABLE_PPROF=true
# 启用调试模式
# DEBUG=true
# Debug related configuration (Cấu hình liên quan đến gỡ lỗi)
# Enable pprof (Bật pprof)
ENABLE_PPROF=true
# Enable debug mode (Bật chế độ gỡ lỗi)
DEBUG=true

# 数据库相关配置
# 数据库连接字符串
# SQL_DSN=user:password@tcp(127.0.0.1:3306)/dbname?parseTime=true
# 日志数据库连接字符串
# LOG_SQL_DSN=user:password@tcp(127.0.0.1:3306)/logdb?parseTime=true
# SQLite数据库路径
# SQLITE_PATH=/path/to/sqlite.db
# 数据库最大空闲连接数
# SQL_MAX_IDLE_CONNS=100
# 数据库最大打开连接数
# SQL_MAX_OPEN_CONNS=1000
# 数据库连接最大生命周期(秒)
# SQL_MAX_LIFETIME=60
# # Database related configuration (Cấu hình liên quan đến cơ sở dữ liệu)
# Database connection string (Chuỗi kết nối cơ sở dữ liệu)
SQL_DSN=user:password@tcp(127.0.0.1:3306)/dbname?parseTime=true
# Log database connection string (Chuỗi kết nối cơ sở dữ liệu log)
LOG_SQL_DSN=user:password@tcp(127.0.0.1:3306)/logdb?parseTime=true
# SQLite database path (Đường dẫn cơ sở dữ liệu SQLite)
SQLITE_PATH=/path/to/sqlite.db
# Database maximum idle connections (Số kết nối rảnh tối đa của cơ sở dữ liệu)
SQL_MAX_IDLE_CONNS=100
# Database maximum open connections (Số kết nối mở tối đa của cơ sở dữ liệu)
SQL_MAX_OPEN_CONNS=1000
# Database connection maximum lifetime in seconds (Thời gian sống tối đa của kết nối cơ sở dữ liệu tính bằng giây)
SQL_MAX_LIFETIME=60


# 缓存相关配置
# Redis连接字符串
# REDIS_CONN_STRING=redis://user:password@localhost:6379/0
# 同步频率(单位:秒)
# SYNC_FREQUENCY=60
# 内存缓存启用
# MEMORY_CACHE_ENABLED=true
# 渠道更新频率(单位:秒)
# CHANNEL_UPDATE_FREQUENCY=30
# 批量更新启用
# BATCH_UPDATE_ENABLED=true
# 批量更新间隔(单位:秒)
# BATCH_UPDATE_INTERVAL=5
# Cache related configuration (Cấu hình liên quan đến bộ nhớ đệm)
# Redis connection string (Chuỗi kết nối Redis)
REDIS_CONN_STRING=redis://user:password@localhost:6379/0
# Sync frequency in seconds (Tần suất đồng bộ tính bằng giây)
SYNC_FREQUENCY=60
# Memory cache enabled (Bật bộ nhớ đệm)
MEMORY_CACHE_ENABLED=true
# Channel update frequency in seconds (Tần suất cập nhật kênh tính bằng giây)
CHANNEL_UPDATE_FREQUENCY=30
# Batch update enabled (Bật cập nhật hàng loạt)
BATCH_UPDATE_ENABLED=true
# Batch update interval in seconds (Khoảng thời gian cập nhật hàng loạt tính bằng giây)
BATCH_UPDATE_INTERVAL=5

# 任务和功能配置
# 更新任务启用
# UPDATE_TASK=true
# # Task and function configuration (Cấu hình tác vụ và chức năng)
# Update task enabled (Bật tác vụ cập nhật)
UPDATE_TASK=true

# 对话超时设置
# 所有请求超时时间,单位秒,默认为0,表示不限制
# RELAY_TIMEOUT=0
# 流模式无响应超时时间,单位秒,如果出现空补全可以尝试改为更大值
# STREAMING_TIMEOUT=120
# Conversation timeout settings (Cài đặt thời gian chờ cuộc hội thoại)
# All request timeout in seconds, default is 0 means no limit (Thời gian chờ tất cả yêu cầu tính bằng giây, mặc định là 0 có nghĩa là không giới hạn)
RELAY_TIMEOUT=0
# Stream mode no response timeout in seconds, try larger value if empty completion occurs (Thời gian chờ không phản hồi trong chế độ stream tính bằng giây, thử giá trị lớn hơn nếu xảy ra hoàn thành trống)
STREAMING_TIMEOUT=120

# Gemini 识别图片 最大图片数量
# GEMINI_VISION_MAX_IMAGE_NUM=16
# Gemini image recognition maximum image count (Số lượng hình ảnh tối đa cho nhận dạng hình ảnh Gemini)
GEMINI_VISION_MAX_IMAGE_NUM=16

# 会话密钥
# SESSION_SECRET=random_string
# # Session secret (Khóa phiên)
SESSION_SECRET=random_string

# 其他配置
# 渠道测试频率(单位:秒)
# CHANNEL_TEST_FREQUENCY=10
# 生成默认token
# GENERATE_DEFAULT_TOKEN=false
# Cohere 安全设置
# COHERE_SAFETY_SETTING=NONE
# 是否统计图片token
# GET_MEDIA_TOKEN=true
# 是否在非流(stream=false)情况下统计图片token
# GET_MEDIA_TOKEN_NOT_STREAM=true
# 设置 Dify 渠道是否输出工作流和节点信息到客户端
# DIFY_DEBUG=true
# Other configuration (Cấu hình khác)
# Channel test frequency in seconds (Tần suất kiểm tra kênh tính bằng giây)
CHANNEL_TEST_FREQUENCY=10
# Generate default token (Tạo token mặc định)
GENERATE_DEFAULT_TOKEN=false
# Cohere safety setting (Cài đặt bảo mật Cohere)
COHERE_SAFETY_SETTING=NONE
# Whether to count image tokens (Có đếm token hình ảnh hay không)
GET_MEDIA_TOKEN=true
# Whether to count image tokens in non-stream mode (stream=false) (Có đếm token hình ảnh trong chế độ không stream (stream=false) hay không)
GET_MEDIA_TOKEN_NOT_STREAM=true
# Set whether Dify channel outputs workflow and node information to client (Thiết lập kênh Dify có xuất thông tin quy trình làm việc và nút tới client hay không)
DIFY_DEBUG=true


# 节点类型
# 如果是主节点则为master
# NODE_TYPE=master
# Node type (Loại nút)
# If master node then set to master (Nếu là nút chính thì đặt thành master)
NODE_TYPE=master
13 changes: 7 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
version: '3.4'

services:
new-api:
image: calciumion/new-api:latest
build:
context: .
dockerfile: Dockerfile
image: new-api:latest
container_name: new-api
restart: always
command: --log-dir /app/logs
Expand All @@ -15,9 +16,9 @@ services:
- SQL_DSN=root:123456@tcp(mysql:3306)/new-api # Point to the mysql service
- REDIS_CONN_STRING=redis://redis
- TZ=Asia/Shanghai
- ERROR_LOG_ENABLED=true # 是否启用错误日志记录
# - STREAMING_TIMEOUT=120 # 流模式无响应超时时间,单位秒,默认120秒,如果出现空补全可以尝试改为更大值
# - SESSION_SECRET=random_string # 多机部署时设置,必须修改这个随机字符串!!!!!!!
- ERROR_LOG_ENABLED=true # Whether to enable error log recording (Có bật ghi log lỗi hay không)
# - STREAMING_TIMEOUT=120 # Stream mode no response timeout in seconds, default 120 seconds, try larger value if empty completion occurs (Thời gian chờ không phản hồi trong chế độ stream tính bằng giây, mặc định 120 giây, thử giá trị lớn hơn nếu xảy ra hoàn thành trống)
# - SESSION_SECRET=random_string # Set for multi-machine deployment, must modify this random string!!! (Thiết lập cho triển khai đa máy, phải sửa đổi chuỗi ngẫu nhiên này!!!)
Comment on lines 16 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Don’t commit plaintext DB creds; use .env and secrets.

Root password and DSN are hard-coded. Parameterize via env and document in .env.example.

-      - SQL_DSN=root:123456@tcp(mysql:3306)/new-api  # Point to the mysql service
+      - SQL_DSN=${SQL_DSN}  # e.g. root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/new-api
@@
-      MYSQL_ROOT_PASSWORD: 123456  # Ensure this matches the password in SQL_DSN
+      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}  # Set in .env

Optional: add env_file: [.env] on the service.

Also applies to: 41-47

🤖 Prompt for AI Agents
In docker-compose.yml around lines 16-21 (and similarly 41-47) the SQL_DSN and
other secrets are hard-coded; remove plaintext credentials and replace them with
environment variables loaded from an env file or Docker secrets (e.g., reference
${SQL_DSN}, ${REDIS_CONN_STRING}, ${SESSION_SECRET}, ${ERROR_LOG_ENABLED},
etc.), add env_file: [.env] to the service or configure secrets for production,
create a .env.example documenting required vars with placeholders (no real
credentials), and ensure the committed compose file and repo contain only the
example / instructions while actual secret values live in an untracked .env or
secret store.

# - NODE_TYPE=slave # Uncomment for slave node in multi-node deployment
# - SYNC_FREQUENCY=60 # Uncomment if regular database syncing is needed
# - FRONTEND_BASE_URL=https://openai.justsong.cn # Uncomment for multi-node deployment with front-end URL
Expand Down
10 changes: 5 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ func main() {
go model.SyncChannelCache(common.SyncFrequency)
}

// 热更新配置
// Hot reload configuration (Cập nhật cấu hình nóng)
go model.SyncOptions(common.SyncFrequency)

// 数据看板
// Data dashboard (Bảng điều khiển dữ liệu)
go model.UpdateQuotaData()

if os.Getenv("CHANNEL_UPDATE_FREQUENCY") != "" {
Expand Down Expand Up @@ -164,11 +164,11 @@ func InitResources() error {
// This is a placeholder function for future resource initialization
err := godotenv.Load(".env")
if err != nil {
common.SysLog("未找到 .env 文件,使用默认环境变量,如果需要,请创建 .env 文件并设置相关变量")
common.SysLog("No .env file found, using default environment variables. If needed, please create a .env file and set the relevant variables. (.env file không tìm thấy, sử dụng biến môi trường mặc định. Nếu cần, vui lòng tạo file .env và thiết lập các biến liên quan.)")
common.SysLog("No .env file found, using default environment variables. If needed, please create a .env file and set the relevant variables.")
}

// 加载环境变量
// Load environment variables (Tải biến môi trường)
common.InitEnv()

common.SetupLogger()
Expand All @@ -192,7 +192,7 @@ func InitResources() error {
// Initialize options, should after model.InitDB()
model.InitOptionMap()

// 初始化模型
// Initialize models (Khởi tạo các mô hình)
model.GetPricing()

// Initialize SQL Database
Expand Down
4 changes: 2 additions & 2 deletions setting/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ var Chats = []map[string]string{
"Cherry Studio": "cherrystudio://providers/api-keys?v=1&data={cherryConfig}",
},
{
"Lobe Chat 官方示例": "https://chat-preview.lobehub.com/?settings={\"keyVaults\":{\"openai\":{\"apiKey\":\"{key}\",\"baseURL\":\"{address}/v1\"}}}",
"Lobe Chat": "https://chat-preview.lobehub.com/?settings={\"keyVaults\":{\"openai\":{\"apiKey\":\"{key}\",\"baseURL\":\"{address}/v1\"}}}",
},
{
"AI as Workspace": "https://aiaw.app/set-provider?provider={\"type\":\"openai\",\"settings\":{\"apiKey\":\"{key}\",\"baseURL\":\"{address}/v1\",\"compatibility\":\"strict\"}}",
},
{
"AMA 问天": "ama://set-api-key?server={address}&key={key}",
"AMA": "ama://set-api-key?server={address}&key={key}",
},
{
"OpenCat": "opencat://team/join?domain={address}&token={key}",
Expand Down
4 changes: 2 additions & 2 deletions setting/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ var USDExchangeRate = 7.3

var PayMethods = []map[string]string{
{
"name": "支付宝",
"name": "Alipay",
"color": "rgba(var(--semi-blue-5), 1)",
"type": "alipay",
},
{
"name": "微信",
"name": "WeChat Pay",
"color": "rgba(var(--semi-green-5), 1)",
"type": "wxpay",
},
Expand Down
4 changes: 2 additions & 2 deletions setting/user_usable_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

var userUsableGroups = map[string]string{
"default": "默认分组",
"vip": "vip分组",
"default": "Default",
"vip": "VIP",
}
var userUsableGroupsMutex sync.RWMutex

Expand Down
5 changes: 3 additions & 2 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<!doctype html>
<html lang="zh">
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#ffffff" />
<!-- OpenAI API aggregation management, supports multiple channels including Azure, can be used for secondary distribution management of keys, single executable file, pre-packaged Docker image, one-click deployment, ready to use (Quản lý tổng hợp API OpenAI, hỗ trợ nhiều kênh bao gồm Azure, có thể dùng để quản lý phân phối thứ cấp các key, file thực thi đơn, Docker image đã đóng gói sẵn, triển khai một cú nhấp chuột, sẵn sàng sử dụng) -->
<meta
name="description"
content="OpenAI 接口聚合管理,支持多种渠道包括 Azure,可用于二次分发管理 key,仅单可执行文件,已打包好 Docker 镜像,一键部署,开箱即用"
content="OpenAI API aggregation management, supports multiple channels including Azure, can be used for secondary distribution management of keys, single executable file, pre-packaged Docker image, one-click deployment, ready to use"
/>
<title>New API</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion web/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ function App() {
</Suspense>
}
/>
{/* 方便使用chat2link直接跳转聊天... */}
{/* Convenient for using chat2link to directly jump to chat (Thuận tiện để sử dụng chat2link nhảy trực tiếp đến trò chuyện) */}
<Route
path='/chat2link'
element={
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/auth/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const LoginForm = () => {
setUserData(data);
updateAPI();
navigate('/');
showSuccess('登录成功!');
showSuccess(t('登录成功!'));
setShowWeChatLoginModal(false);
} else {
showError(message);
Expand Down Expand Up @@ -146,7 +146,7 @@ const LoginForm = () => {
userDispatch({ type: 'login', payload: data });
setUserData(data);
updateAPI();
showSuccess('登录成功!');
showSuccess(t('登录成功!'));
if (username === 'root' && password === '123456') {
Modal.error({
title: '您正在使用默认密码!',
Expand Down Expand Up @@ -192,7 +192,7 @@ const LoginForm = () => {
if (success) {
userDispatch({ type: 'login', payload: data });
localStorage.setItem('user', JSON.stringify(data));
showSuccess('登录成功!');
showSuccess(t('登录成功!'));
setUserData(data);
updateAPI();
navigate('/');
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/auth/RegisterForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const RegisterForm = () => {
setUserData(data);
updateAPI();
navigate('/');
showSuccess('登录成功!');
showSuccess(t('登录成功!'));
setShowWeChatLoginModal(false);
} else {
showError(message);
Expand Down Expand Up @@ -254,7 +254,7 @@ const RegisterForm = () => {
if (success) {
userDispatch({ type: 'login', payload: data });
localStorage.setItem('user', JSON.stringify(data));
showSuccess('登录成功!');
showSuccess(t('登录成功!'));
setUserData(data);
updateAPI();
navigate('/');
Expand Down
9 changes: 8 additions & 1 deletion web/src/components/layout/HeaderBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useSetTheme, useTheme } from '../../context/Theme/index.js';
import { useTranslation } from 'react-i18next';
import { API, getLogo, getSystemName, showSuccess, stringToColor } from '../../helpers/index.js';
import fireworks from 'react-fireworks';
import { CN, GB } from 'country-flag-icons/react/3x2';
import { CN, GB, VN } from 'country-flag-icons/react/3x2';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Language code ‘vn’ vs. standard ‘vi/vi-VN’ — verify mapping

You register/select 'vn'. Confirm i18n initialization maps 'vi'/'vi-VN' detections to 'vn' to prevent fallback misses.

Also applies to: 611-617


🏁 Script executed:

#!/bin/bash
# Inspect i18n setup and locale files for 'vn' mapping and detection
rg -n "i18n\\.|init\\(|changeLanguage\\(|supportedLngs|fallbackLng|languageDetector" -C3 web/src/i18n
rg -n "\"vn\"|locales/vn\\.json" -n web/src/i18n -C2
rg -n "vi-VN|vi_" -n web/src/i18n -C2

Length of output: 858


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== file: web/src/i18n/i18n.js (first 240 lines) ==="
if [ -f web/src/i18n/i18n.js ]; then
  nl -ba web/src/i18n/i18n.js | sed -n '1,240p'
else
  echo "MISSING: web/src/i18n/i18n.js"
fi

echo
echo "=== rg: i18n config tokens in web/src/i18n ==="
rg -n "supportedLngs|fallbackLng|resources:|LanguageDetector|init\\(|use\\(|nonExplicit|load|lookup|order|caches|whitelist" web/src/i18n -S -n -C3 || true

echo
echo "=== locales listing ==="
ls -la web/src/i18n/locales || true

echo
echo "=== show vn.json and vi.json if present ==="
if [ -f web/src/i18n/locales/vn.json ]; then
  echo "--- vn.json ---"
  nl -ba web/src/i18n/locales/vn.json | sed -n '1,200p'
else
  echo "vn.json not found"
fi

if [ -f web/src/i18n/locales/vi.json ]; then
  echo "--- vi.json ---"
  nl -ba web/src/i18n/locales/vi.json | sed -n '1,200p'
else
  echo "vi.json not found"
fi

echo
echo "=== search repo for 'vi' and 'vn' occurrences ==="
rg -n --hidden --no-ignore -S "\\bvi(-|_)|vi\\b|vi-VN|\\bvn\\b" || true

echo
echo "=== search for language change/usages ==="
rg -n "changeLanguage\\(|i18n\\.changeLanguage|i18n\\.language|LanguageDetector" -S || true

Length of output: 93


Use standard language code 'vi' or add explicit mapping for Vietnamese

i18n currently registers "vn" (web/src/i18n/i18n.js imports ./locales/vn.json and resources include vn) while LanguageDetector returns "vi" / "vi-VN", causing fallbacks (fallbackLng: 'zh'). Fix by renaming the locale key/file to "vi" (rename vn.json → vi.json and update imports/resources) or add an explicit alias (assign resources.vi = vnTranslation and set supportedLngs or map detected "vi"/"vi-VN" → "vn"). HeaderBar's flag import (VN) is the ISO country code and can remain. Files to change: web/src/i18n/i18n.js, web/src/i18n/locales/vn.json (or rename), web/src/components/layout/HeaderBar.js:8 (also applies to 611–617).

🤖 Prompt for AI Agents
In web/src/components/layout/HeaderBar.js around line 8 (and likewise lines
~611–617) and in web/src/i18n/i18n.js and web/src/i18n/locales/vn.json, fix the
Vietnamese locale mismatch: i18n currently registers "vn" while the language
detector returns "vi"/"vi-VN", causing fallback to zh. Resolve by either (A)
renaming vn.json → vi.json and updating all imports/resources keys in
web/src/i18n/i18n.js to use "vi", and update any references in HeaderBar (keep
the VN flag import as-is), or (B) keep vn.json and in web/src/i18n/i18n.js add
an explicit alias mapping so resources.vi = resources.vn and include "vi" (and
optionally "vi-VN") in supportedLngs or detection mapping so detected
"vi"/"vi-VN" resolves to the existing "vn" translations; ensure imports and
resource keys are consistent after the change.

import NoticeModal from './NoticeModal.js';

import {
Expand Down Expand Up @@ -608,6 +608,13 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
<GB title="English" className="!w-5 !h-auto" />
<span>English</span>
</Dropdown.Item>
<Dropdown.Item
onClick={() => handleLanguageChange('vn')}
className={`!flex !items-center !gap-2 !px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'vn' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`}
>
<VN title="Tiếng Việt" className="!w-5 !h-auto" />
<span>Tiếng Việt</span>
</Dropdown.Item>
</Dropdown.Menu>
}
>
Expand Down
38 changes: 19 additions & 19 deletions web/src/components/settings/DashboardSetting.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const DashboardSetting = () => {
setLoading(true);
await getOptions();
} catch (error) {
showError('刷新失败');
showError(t('刷新失败'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix: t is undefined here

This file doesn’t import/use i18n; calling t(...) will crash at runtime.

Apply:

 import React, { useEffect, useState, useMemo } from 'react';
 import { Card, Spin, Button, Modal } from '@douyinfe/semi-ui';
 import { API, showError, showSuccess, toBoolean } from '../../helpers';
+import { useTranslation } from 'react-i18next';
@@
-const DashboardSetting = () => {
+const DashboardSetting = () => {
+  const { t } = useTranslation();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
showError(t('刷新失败'));
import React, { useEffect, useState, useMemo } from 'react';
import { Card, Spin, Button, Modal } from '@douyinfe/semi-ui';
import { API, showError, showSuccess, toBoolean } from '../../helpers';
import { useTranslation } from 'react-i18next';
const DashboardSetting = () => {
const { t } = useTranslation();
showError(t('刷新失败'));
🤖 Prompt for AI Agents
In web/src/components/settings/DashboardSetting.js around line 62, the call to
t('刷新失败') uses an undefined t and will crash; fix by wiring up i18n: import and
use the react-i18next hook (useTranslation) at the top of the component and
destructure t (const { t } = useTranslation()) before using it, or alternatively
import a named t helper from your i18n module if your project uses that pattern,
then replace the raw t call with the properly imported/initialized function.

console.error(error);
} finally {
setLoading(false);
Expand All @@ -86,12 +86,12 @@ const DashboardSetting = () => {
try {
setLoading(true);
await API.post('/api/option/migrate_console_setting');
showSuccess('旧配置迁移完成');
showSuccess(t('旧配置迁移完成'));
await onRefresh();
setShowMigrateModal(false);
} catch (err) {
console.error(err);
showError('迁移失败: ' + (err.message || '未知错误'));
showError(t('迁移失败') + ': ' + (err.message || t('未知错误')));
} finally {
setLoading(false);
}
Expand All @@ -101,22 +101,22 @@ const DashboardSetting = () => {
<>
<Spin spinning={loading} size='large'>
{/* 用于迁移检测的旧键模态框,下个版本会删除 */}
<Modal
title="配置迁移确认"
visible={showMigrateModal}
onOk={handleMigrate}
onCancel={() => setShowMigrateModal(false)}
confirmLoading={loading}
okText="确认迁移"
cancelText="取消"
>
<p>检测到旧版本的配置数据,是否要迁移到新的配置格式?</p>
<p style={{ color: '#f57c00', marginTop: '10px' }}>
<strong>注意:</strong>迁移过程中会自动处理数据格式转换,迁移完成后旧配置将被清除,请在迁移前在数据库中备份好旧配置。
</p>
</Modal>

{/* 数据看板设置 */}
<Modal
title={t('配置迁移确认')}
visible={showMigrateModal}
onOk={handleMigrate}
onCancel={() => setShowMigrateModal(false)}
confirmLoading={loading}
okText={t('确认迁移')}
cancelText={t('取消')}
>
<p>{t('检测到旧版本的配置数据,是否要迁移到新的配置格式?')}</p>
<p style={{ color: '#f57c00', marginTop: '10px' }}>
<strong>{t('注意:')}</strong>{t('迁移过程中会自动处理数据格式转换,迁移完成后旧配置将被清除,请在迁移前在数据库中备份好旧配置。')}
</p>
</Modal>

{/* 数据看板设置 */}
<Card style={{ marginTop: '10px' }}>
<SettingsDataDashboard options={inputs} refresh={onRefresh} />
</Card>
Expand Down
Loading