Skip to content

feat: complete Vietnamese localization for new-api - #1802

Closed
nosime wants to merge 2 commits into
QuantumNous:mainfrom
nosime:main
Closed

feat: complete Vietnamese localization for new-api#1802
nosime wants to merge 2 commits into
QuantumNous:mainfrom
nosime:main

Conversation

@nosime

@nosime nosime commented Sep 14, 2025

Copy link
Copy Markdown

📝 Vietnamese Localization for new-api

🎯 Overview

Complete Vietnamese (vi-VN) localization for the new-api project, covering both backend and frontend components.

🔧 Backend Changes

  • Update main.go with Vietnamese language support
  • Add Vietnamese translations to setting files:
    • setting/chat.go - Chat configuration
    • setting/payment.go - Payment settings
    • setting/user_usable_group.go - User group management
  • Update docker-compose.yml and .env.example for i18n support

🎨 Frontend Changes

  • Complete Vietnamese translation in web/src/i18n/locales/vn.json (2,600+ entries)
  • Update English (en.json) and Chinese (zh.json) locale files
  • Add Vietnamese support to authentication components:
    • LoginForm, RegisterForm
  • Implement i18n for all table components:
    • ChannelsTable, RedemptionsTable, TokensTable, UsersTable
  • Add Vietnamese translations to all setting pages:
    • Dashboard, Personal, Other settings
  • Update pagination components with Vietnamese text support
  • Add i18n to all main pages:
    • Home, About, Detail, Channel, Redemption
  • Update CSS and utilities for Vietnamese language support
  • Configure i18n in App.js and index.js

📋 Features Covered

  • ✅ Authentication system
  • ✅ Dashboard and settings
  • ✅ Data tables with pagination
  • ✅ Payment and billing
  • ✅ Model management (Gemini, Global models)
  • ✅ Channel and token management
  • ✅ All UI components and messages

🧪 Testing

  • Build successful
  • No breaking changes
  • Vietnamese text displays correctly
  • Fallback to English for missing translations

📊 Statistics

  • Files changed: 38
  • Lines added: 2,611
  • Lines removed: 251
  • Translation entries: 2,600+

🇻🇳 This PR enables Vietnamese users to use new-api with a fully localized interface.

Summary by CodeRabbit

  • New Features

    • Vietnamese language support across the UI (header switch, home, i18n resources).
    • Enhanced Model Pricing Editor with per-token/request modes, auto-calculation, and smoother editing.
    • About page now fetches and caches content, rendering Markdown or external URLs.
  • Improvements

    • Extensive localization: login/register messages, settings, tables, modals, confirmations, empty states, and pagination.
    • Standardized English labels for payment methods and chat providers.
    • Default site language set to English.
  • Chores

    • Updated example environment configuration.
    • Docker Compose now builds from the local Dockerfile.

Backend changes:
- Update main.go with Vietnamese language support
- Add Vietnamese translations to setting files (chat.go, payment.go, user_usable_group.go)
- Update docker-compose.yml and .env.example for i18n support

Frontend changes:
- Complete Vietnamese translation in web/src/i18n/locales/vn.json
- Update English (en.json) and Chinese (zh.json) locale files
- Add Vietnamese support to authentication components (LoginForm, RegisterForm)
- Implement i18n for all table components (ChannelsTable, RedemptionsTable, TokensTable, UsersTable)
- Add Vietnamese translations to all setting pages
- Update pagination components with Vietnamese text support
- Add i18n to all main pages (Home, About, Detail, Channel, etc.)
- Update CSS and utils for Vietnamese language support
- Configure i18n in App.js and index.js

This is a comprehensive Vietnamese localization covering:
- Authentication system
- Dashboard and settings
- Data tables with pagination
- Payment and billing
- Model management
- Channel and token management
- All UI components and messages
@coderabbitai

coderabbitai Bot commented Sep 14, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR activates numerous environment variables in .env.example, switches docker-compose to build the API locally, updates multilingual comments and labels, broadens i18n (adds Vietnamese), internationalizes many UI strings and modals, enhances About page content loading with caching, and significantly refactors the Model Settings visual editor to support dual pricing modes with conversions.

Changes

Cohort / File(s) Summary
Env configuration activation
.env.example
Converts commented examples into active env keys (server, DB, cache, timeouts, security, feature flags).
Compose build switch
docker-compose.yml
Changes new-api service to build from local Dockerfile and tag as new-api:latest; updates inline comments.
Backend comments and labels
main.go, setting/chat.go, setting/payment.go, setting/user_usable_group.go
Bilingual comment updates; minor log tweak; English display name changes for chats, payment methods, and user group labels.
Web scaffolding & i18n setup
web/index.html, web/src/index.js, web/src/index.css, web/src/i18n/i18n.js, web/src/i18n/locales/en.json, web/src/i18n/locales/zh.json, web/src/App.js, web/src/components/layout/HeaderBar.js
Adds Vietnamese locale and header option; expands en.json; adds one zh key; updates HTML lang and comments; no functional CSS changes.
Auth forms i18n
web/src/components/auth/LoginForm.js, web/src/components/auth/RegisterForm.js
Replaces hard-coded success messages with i18n t(...).
Settings i18n (dashboard/other/personal)
web/src/components/settings/DashboardSetting.js, web/src/components/settings/OtherSetting.js, web/src/components/settings/PersonalSetting.js
Converts UI text, banners, modals, and docs to i18n/English; no logic changes.
Tables: modal labels & empty states
web/src/components/table/ChannelsTable.js, .../RedemptionsTable.js, .../TokensTable.js, .../UsersTable.js
Adds okText/cancelText via t(...); adds emptyText for tables where applicable.
Helpers i18n
web/src/helpers/utils.js
Localizes error messages in showError across status branches and defaults.
About page content loader
web/src/pages/About/index.js
Adds displayAbout: fetch /api/about, localStorage cache, markdown rendering, error handling; invoked on mount.
Home language branch
web/src/pages/Home/index.js
Adds Vietnamese heading variant.
Channel editing UX text
web/src/pages/Channel/EditChannel.js, web/src/pages/Channel/EditTagModal.js
Localizes confirm modal for /v1 warning and tag modal buttons; comment updates.
Detail empty row i18n
web/src/pages/Detail/index.js
Localizes “no data” label.
Redemption create modal labels
web/src/pages/Redemption/EditRedemption.js
Adds localized ok/cancel text to success confirm modal.
Model settings minor i18n
web/src/pages/Setting/Model/SettingGeminiModel.js, web/src/pages/Setting/Model/SettingGlobalModel.js
Text refactors and i18n wrapping; no logic changes.
Ratio settings & editors
web/src/pages/Setting/Ratio/GroupRatioSettings.js, .../ModelRationNotSetEditor.js, .../ModelSettingsVisualEditor.js, .../UpstreamRatioSync.js
i18n for labels, pagination, modals; escapes JSON in help text; substantial editor upgrade in ModelSettingsVisualEditor for dual pricing modes with conversions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant AboutPage as About Page
  participant LocalStorage
  participant API as /api/about
  participant Markdown as Markdown Parser

  User->>AboutPage: Navigate
  activate AboutPage
  AboutPage->>LocalStorage: getItem('about')
  LocalStorage-->>AboutPage: cachedContent or null
  AboutPage->>AboutPage: set state from cache (if any)
  AboutPage->>API: GET /api/about
  alt success
    API-->>AboutPage: data
    alt data starts with "https://"
      AboutPage->>AboutPage: set iframe URL
    else render markdown
      AboutPage->>Markdown: parse(data)
      Markdown-->>AboutPage: html
      AboutPage->>LocalStorage: setItem('about', data)
      AboutPage->>AboutPage: set HTML content
    end
  else failure
    API-->>AboutPage: error
    AboutPage->>AboutPage: set translated error text
  end
  AboutPage->>AboutPage: set aboutLoaded = true
  deactivate AboutPage
Loading
sequenceDiagram
  autonumber
  actor Admin
  participant Editor as ModelSettingsVisualEditor
  participant Form as Modal Form
  participant API as Save Endpoints

  Admin->>Editor: Edit model
  Editor->>Form: Open with prefilled fields
  note over Editor,Form: Mode switch supported<br/>(per-request vs per-token)<br/>Sub-mode: ratio vs token-price
  Admin->>Form: Change pricing mode/sub-mode
  Form->>Editor: onModeChange
  Editor->>Editor: Convert existing data<br/>update ratio/tokenPrice/completionRatio
  Admin->>Form: Edit fields
  Form->>Editor: onOk
  alt per-token + token-price
    Editor->>Editor: Compute ratio/completionRatio from prices
  else ratio mode
    Editor->>Editor: Use ratios directly
  end
  Editor->>API: Submit ModelPrice/ModelRatio/CompletionRatio
  alt success
    API-->>Editor: ok
    Editor->>Admin: showSuccess(t('保存成功'))
  else partial/failed
    API-->>Editor: errors
    Editor->>Admin: showError(t('保存失败,请重试') / partial info)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • xyfacai

Poem

A bunny taps keys with gentle delight,
Flags unfurl—VN joins the night.
Env vars awake, compose learns to build,
Tables say “OK” with courage instilled.
Models flip modes, ratios align—
Cache the About, let markdown shine.
Hippity-hop, ship time is fine! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: complete Vietnamese localization for new-api" accurately and concisely describes the primary change: adding Vietnamese (vi‑VN) localization across backend and frontend. The PR summary and file diffs show extensive i18n updates (large vn locale, component translations, backend label changes), so the title is relevant and specific for reviewers scanning history.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
web/src/pages/Redemption/EditRedemption.js (1)

121-139: Guard modal on success; use clearer button label.

The confirm modal can trigger on failures if data is present. Also, “继续” isn’t explicit for a download action.

Apply:

-    if (!isEdit && data) {
+    if (success && !isEdit && Array.isArray(data) && data.length) {
@@
-        okText: t('继续'),
+        okText: t('下载'),
         cancelText: t('取消'),
web/src/components/settings/PersonalSetting.js (3)

969-977: Bug: Input should be readOnly (camelCase)

Semi Input expects readOnly; readonly won’t apply, allowing edits.

-                                    <Input
-                                      readonly
+                                    <Input
+                                      readOnly
                                       value={systemToken}

270-277: Bug: wrong modal state updated after password change

Closing WeChat modal instead of Change Password modal on success.

-      showSuccess(t('密码修改成功!'));
-      setShowWeChatBindModal(false);
+      showSuccess(t('密码修改成功!'));
+      setShowChangePasswordModal(false);
@@
-    setShowChangePasswordModal(false);
+    // already closed on success; keep this if you want to always close
+    setShowChangePasswordModal(false);

313-316: Avoid mutating context state directly

Directly setting userState.user.email won’t trigger React updates.

-      setShowEmailBindModal(false);
-      userState.user.email = inputs.email;
+      setShowEmailBindModal(false);
+      // Option 1: refresh from server
+      await getUserData();
+      // Option 2: or update context directly
+      // userDispatch({ type: 'login', payload: { ...userState.user, email: inputs.email } });
web/src/pages/Channel/EditTagModal.js (2)

254-258: Option list shape mismatch (label vs text) breaks Select options

Elsewhere optionList uses { label, value }. Here you push { key, text, value }, so labels won’t render.

-        localModelOptions.push({
-          key: model,
-          text: model,
-          value: model,
-        });
+        localModelOptions.push({
+          label: model,
+          value: model,
+        });

146-151: Wrap hard-coded user strings with t() for i18n

In web/src/pages/Channel/EditTagModal.js these user-facing Chinese messages are not localized — wrap them with t() and add corresponding keys to en/vn locales:

-        showInfo('模型映射必须是合法的 JSON 格式!');
+        showInfo(t('模型映射必须是合法的 JSON 格式!'));
...
-      showWarning('没有任何修改!');
+      showWarning(t('没有任何修改!'));
...
-        showSuccess('标签更新成功!');
+        showSuccess(t('标签更新成功!'));
web/src/components/auth/RegisterForm.js (1)

96-101: Localize remaining user‑facing messages in this form

Several info/success/error strings remain hard-coded in Chinese; wrap them with t(...) to complete i18n.

Example edits:

-      showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
+      showInfo(t('请稍后几秒重试,Turnstile 正在检查用户环境!'));

-      showInfo('密码长度不得小于 8 位!');
+      showInfo(t('密码长度不得小于 8 位!'));

-      showInfo('两次输入的密码不一致');
+      showInfo(t('两次输入的密码不一致'));

-          showSuccess('注册成功!');
+          showSuccess(t('注册成功!'));

-      showError('登录失败,请重试');
+      showError(t('登录失败,请重试'));

-      showSuccess('验证码发送成功,请检查你的邮箱!');
+      showSuccess(t('验证码发送成功,请检查你的邮箱!'));

-      showError('发送验证码失败,请重试');
+      showError(t('发送验证码失败,请重试'));

Also applies to: 129-143, 154-162, 168-189

web/src/components/table/ChannelsTable.js (1)

61-67: Don’t use Map like an object; memoize labels.

Current code recreates a Map each render and indexes it as an object. Use a memoized object.

Apply this diff:

-  let type2label = undefined;
+  const type2label = React.useMemo(() => {
+    const obj = {};
+    for (let i = 0; i < CHANNEL_OPTIONS.length; i++) {
+      obj[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i];
+    }
+    return obj;
+  }, []);

And keep usages as type2label[type].

🧹 Nitpick comments (27)
web/index.html (1)

2-12: OK, but consider dynamic for i18n.

Static lang="en" is fine, yet for a multilingual SPA, setting documentElement.lang to the user’s selected locale on startup improves a11y/SEO.

setting/payment.go (1)

13-24: Use stable IDs for API; localize display names on the client.

Switching “name” to English is fine, but better to treat “type” as canonical and translate “name” via frontend i18n to avoid backend text churn.

web/src/pages/Redemption/EditRedemption.js (1)

160-160: Prop typo: “visiable”.

SideSheet expects “visible”. If this is not intentional, rename across call sites to avoid confusion.

-        visible={props.visiable}
+        visible={props.visible}
setting/user_usable_group.go (1)

10-12: Label consistency with fallback paths.

You switched to English labels (“Default”, “VIP”), but unknown-group fallback still returns Chinese and the “default” auto-fill uses lower-case.

Apply:

-            groupsCopy["default"] = "default"
+            groupsCopy["default"] = "Default"
@@
-        groupsCopy[userGroup] = "用户分组"
+        groupsCopy[userGroup] = "User Group"
main.go (3)

167-169: Duplicate “.env not found” log.

You log the same message twice. Keep one.

-        common.SysLog("No .env file found, using default environment variables. If needed, please create a .env file and set the relevant variables.")

141-149: Make cookie “Secure” configurable.

Hardcoding Secure: false weakens session cookies in HTTPS deployments. Gate via env.

-    store.Options(sessions.Options{
+    secureCookie := os.Getenv("COOKIE_SECURE") == "true"
+    store.Options(sessions.Options{
         Path:     "/",
         MaxAge:   2592000, // 30 days
         HttpOnly: true,
-        Secure:   false,
+        Secure:   secureCookie,
         SameSite: http.SameSiteStrictMode,
     })

61-66: Info log written with SysError.

“sync frequency” isn’t an error. Prefer SysLog.

-        common.SysError(fmt.Sprintf("sync frequency: %d seconds", common.SyncFrequency))
+        common.SysLog(fmt.Sprintf("sync frequency: %d seconds", common.SyncFrequency))
web/src/components/settings/OtherSetting.js (1)

175-185: Browser-forbidden header

Setting 'User-Agent' in fetch headers is blocked by browsers; it’s ignored or may cause errors. Remove it.

       const res = await fetch(
         'https://api.github.com/repos/Calcium-Ion/new-api/releases/latest',
         {
           headers: {
             Accept: 'application/json',
             'Content-Type': 'application/json',
-            // Adding User-Agent which is often required by GitHub API
-            'User-Agent': 'new-api-update-checker',
           },
         },
       ).then((response) => response.json());
web/src/components/settings/PersonalSetting.js (1)

1109-1109: JSX comment must be wrapped in braces

Biome warns. Use {/* ... */} inside JSX.

-                          /* Webhook Settings */
+                          {/* Webhook Settings */}
.env.example (2)

9-11: Safeguard defaults: disable debug/profiling in example

Ship examples safe-by-default to avoid accidental prod exposure.

-ENABLE_PPROF=true
-DEBUG=true
+# Recommended defaults; enable only when needed
+ENABLE_PPROF=false
+DEBUG=false

15-25: dotenv hygiene — fix ordering/blank lines and re-comment DSN examples

dotenv-linter found 14 issues in .env.example (UnorderedKey + ExtraBlankLine). Reorder keys and remove the extra blank lines per the linter output, and re-comment/sanitize SQL_DSN, LOG_SQL_DSN, and SQLITE_PATH examples to prevent accidental usage.

web/src/components/table/TokensTable.js (1)

588-588: i18n consistency: localize success toast

Translate the success message for manageToken.

-      showSuccess('操作成功完成!');
+      showSuccess(t('操作成功完成!'));
web/src/components/table/RedemptionsTable.js (1)

595-596: Pagination API mismatch: pageSizeOptions vs Semi UI’s pageSizeOpts

Elsewhere (UsersTable) you use pageSizeOpts. Using the AntD-style pageSizeOptions may be ignored.

-            pageSizeOptions: [10, 20, 50, 100],
+            pageSizeOpts: [10, 20, 50, 100],
web/src/components/table/UsersTable.js (2)

403-404: i18n gap: success toast not localized

Wrap with t() to align with the rest.

-      showSuccess('操作成功完成!');
+      showSuccess(t('操作成功完成!'));

173-174: i18n gap: inline label not localized

Avoid hard-coded Chinese in template literals.

-                {record.inviter_id === 0 ? t('无邀请人') : `邀请人: ${record.inviter_id}`}
+                {record.inviter_id === 0 ? t('无邀请人') : `${t('邀请人')}: ${record.inviter_id}`}

Ensure keys exist in en/vn.

web/src/pages/Home/index.js (2)

140-140: React prop case: readonlyreadOnly

Current prop is ignored; input becomes editable unintentionally.

-                    <Input
-                      readonly
+                    <Input
+                      readOnly

63-64: Optional: unify duplicate “copied to clipboard” keys

Other files use “已复制到剪贴板!”. Consider reusing one key to avoid translation drift.

web/src/components/auth/RegisterForm.js (1)

113-114: i18n for success toasts — LGTM; align toast vs. navigation order

The translation-based success messages look good. For consistency and to avoid a toast being lost on route change, show the toast before navigating (WeChat path currently navigates first; Telegram path shows first).

Apply:

-        updateAPI();
-        navigate('/');
-        showSuccess(t('登录成功!'));
+        updateAPI();
+        showSuccess(t('登录成功!'));
+        navigate('/');

Also applies to: 257-258

web/src/pages/Setting/Ratio/UpstreamRatioSync.js (1)

748-754: Avoid passing unused props to ConflictConfirmModal (or support them)

You pass okText/cancelText to ConflictConfirmModal, but its props don’t accept/use them. Either remove at call site or support override in the component.

Apply:

-function ConflictConfirmModal({ t, visible, items, onOk, onCancel }) {
+function ConflictConfirmModal({ t, visible, items, onOk, onCancel, okText, cancelText }) {-    <Modal
+    <Modal
       title={t('确认冲突项修改')}
       visible={visible}
-      okText={t('确定')}
-      cancelText={t('取消')}
+      okText={okText || t('确定')}
+      cancelText={cancelText || t('取消')}
       onCancel={onCancel}
       onOk={onOk}
web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js (1)

506-507: Text consistency: placeholder should say “补全倍率”

Field label is “补全倍率”; placeholder says “输入补全价格”.

Apply:

-                placeholder={t('输入补全价格')}
+                placeholder={t('输入补全倍率')}
web/src/i18n/locales/en.json (2)

1795-1807: Fix Stripe-related English phrasing (readability).

Tighten several labels to natural English.

Apply this diff:

-  "Stripe 密钥、Webhook 等设置请": "For Stripe key, Webhook and other settings, please",
+  "Stripe 密钥、Webhook 等设置请": "For Stripe keys, webhooks, and other settings, please",
-  "测试环境": "Test environment",
+  "测试环境": "Test Environment",
-  "进行设置,最好先在": "set up in the",
+  "进行设置,最好先在": "set them up, ideally in the",
-  "进行测试。": "for testing.",
+  "进行测试。": "to test first.",
-  "Webhook 填": "Webhook fill in",
+  "Webhook 填": "Webhook URL",
-  "需要包含事件": "needs to include event",
+  "需要包含事件": "Must include events",

1862-1869: Remove leading spaces and unify punctuation.

Values start with an extra space; clean them up.

Apply this diff:

- "Logo 更新失败":" Logo update failed",
- "首页内容更新失败":" Homepage content update failed",
- "系统名称更新失败":" System name update failed",
- "公告更新失败":" Notice update failed",
- "关于内容更新失败":" About content update failed",
- "关于内容已更新":" About content updated",
- "已是最新版本:${tag_name}":" Already the latest version: ${tag_name}",
- "检查更新失败,请稍后再试":" Check for updates failed, please try again later",
+ "Logo 更新失败":"Logo update failed",
+ "首页内容更新失败":"Homepage content update failed",
+ "系统名称更新失败":"System name update failed",
+ "公告更新失败":"Notice update failed",
+ "关于内容更新失败":"About content update failed",
+ "关于内容已更新":"About content updated",
+ "已是最新版本:${tag_name}":"Already the latest version: ${tag_name}",
+ "检查更新失败,请稍后再试":"Check for updates failed, please try again later",
web/src/components/table/ChannelsTable.js (3)

2211-2212: Use “empty” (not “emptyText”) for Semi UI Table.

Semi Table expects the “empty” prop; “emptyText” has no effect.

Apply this diff:

-                emptyText={t('暂无数据')}
+                empty={<div style={{ textAlign: 'center', padding: 20 }}>{t('暂无数据')}</div>}

1088-1088: Localize hardcoded success text.

Wrap “操作成功完成!” with t() for full i18n coverage.

Apply this diff:

-      showSuccess('操作成功完成!');
+      showSuccess(t('操作成功完成'));

1420-1435: Localize validation messages.

Wrap “优先级必须是整数!” and “权重必须是非负整数!” with t().

Apply this diff:

-          showInfo('优先级必须是整数!');
+          showInfo(t('优先级必须是整数!'));
...
-          showInfo('权重必须是非负整数!');
+          showInfo(t('权重必须是非负整数!'));
web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js (1)

53-56: Harden ServerAddress check.

Guard undefined/null, not only empty string.

Apply this diff:

-    if (props.options.ServerAddress === '') {
+    if (!props?.options?.ServerAddress) {
       showError(t('请先填写服务器地址'));
       return;
     }
web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js (1)

22-22: Remove unused import/variable.

getQuotaPerUnit() and quotaPerUnit are unused.

Apply this diff:

- import { API, showError, showSuccess, getQuotaPerUnit } from '../../../helpers';
+ import { API, showError, showSuccess } from '../../../helpers';
...
-  const quotaPerUnit = getQuotaPerUnit();

Also applies to: 38-39

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f760a8 and bdb4200.

📒 Files selected for processing (37)
  • .env.example (1 hunks)
  • docker-compose.yml (2 hunks)
  • main.go (3 hunks)
  • setting/chat.go (1 hunks)
  • setting/payment.go (1 hunks)
  • setting/user_usable_group.go (1 hunks)
  • web/index.html (1 hunks)
  • web/src/App.js (1 hunks)
  • web/src/components/auth/LoginForm.js (3 hunks)
  • web/src/components/auth/RegisterForm.js (2 hunks)
  • web/src/components/layout/HeaderBar.js (2 hunks)
  • web/src/components/settings/DashboardSetting.js (3 hunks)
  • web/src/components/settings/OtherSetting.js (6 hunks)
  • web/src/components/settings/PersonalSetting.js (2 hunks)
  • web/src/components/table/ChannelsTable.js (10 hunks)
  • web/src/components/table/RedemptionsTable.js (2 hunks)
  • web/src/components/table/TokensTable.js (2 hunks)
  • web/src/components/table/UsersTable.js (3 hunks)
  • web/src/helpers/utils.js (1 hunks)
  • web/src/i18n/i18n.js (2 hunks)
  • web/src/i18n/locales/en.json (2 hunks)
  • web/src/i18n/locales/zh.json (1 hunks)
  • web/src/index.css (25 hunks)
  • web/src/index.js (1 hunks)
  • web/src/pages/About/index.js (1 hunks)
  • web/src/pages/Channel/EditChannel.js (15 hunks)
  • web/src/pages/Channel/EditTagModal.js (1 hunks)
  • web/src/pages/Detail/index.js (1 hunks)
  • web/src/pages/Home/index.js (1 hunks)
  • web/src/pages/Redemption/EditRedemption.js (1 hunks)
  • web/src/pages/Setting/Model/SettingGeminiModel.js (1 hunks)
  • web/src/pages/Setting/Model/SettingGlobalModel.js (3 hunks)
  • web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js (1 hunks)
  • web/src/pages/Setting/Ratio/GroupRatioSettings.js (3 hunks)
  • web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js (4 hunks)
  • web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js (6 hunks)
  • web/src/pages/Setting/Ratio/UpstreamRatioSync.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
web/src/components/auth/LoginForm.js (1)
web/src/helpers/utils.js (1)
  • showSuccess (129-131)
web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js (1)
web/src/helpers/utils.js (2)
  • a (231-231)
  • removeTrailingSlash (149-156)
web/src/components/settings/DashboardSetting.js (1)
web/src/helpers/utils.js (2)
  • showError (94-123)
  • showSuccess (129-131)
web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js (1)
web/src/helpers/utils.js (2)
  • showError (94-123)
  • showSuccess (129-131)
web/src/components/auth/RegisterForm.js (1)
web/src/helpers/utils.js (1)
  • showSuccess (129-131)
web/src/components/settings/OtherSetting.js (1)
web/src/helpers/utils.js (2)
  • showSuccess (129-131)
  • showError (94-123)
web/src/pages/About/index.js (2)
web/src/App.js (1)
  • About (32-32)
web/src/components/layout/Footer.js (1)
  • currentYear (22-22)
main.go (3)
model/option.go (1)
  • SyncOptions (158-164)
common/constants.go (1)
  • SyncFrequency (115-115)
common/logger.go (1)
  • SysLog (50-53)
🪛 Biome (2.1.2)
web/src/components/settings/PersonalSetting.js

[error] 1127-1129: Wrap comments inside children within braces.

Unsafe fix: Wrap the comments with braces

(lint/suspicious/noCommentText)

🪛 dotenv-linter (3.3.0)
.env.example

[warning] 4-4: [UnorderedKey] The FRONTEND_BASE_URL key should go before the PORT key

(UnorderedKey)


[warning] 6-6: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 11-11: [UnorderedKey] The DEBUG key should go before the ENABLE_PPROF key

(UnorderedKey)


[warning] 17-17: [UnorderedKey] The LOG_SQL_DSN key should go before the SQL_DSN key

(UnorderedKey)


[warning] 19-19: [UnorderedKey] The SQLITE_PATH key should go before the SQL_DSN key

(UnorderedKey)


[warning] 25-25: [UnorderedKey] The SQL_MAX_LIFETIME key should go before the SQL_MAX_OPEN_CONNS key

(UnorderedKey)


[warning] 27-27: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 34-34: [UnorderedKey] The MEMORY_CACHE_ENABLED key should go before the REDIS_CONN_STRING key

(UnorderedKey)


[warning] 36-36: [UnorderedKey] The CHANNEL_UPDATE_FREQUENCY key should go before the MEMORY_CACHE_ENABLED key

(UnorderedKey)


[warning] 38-38: [UnorderedKey] The BATCH_UPDATE_ENABLED key should go before the CHANNEL_UPDATE_FREQUENCY key

(UnorderedKey)


[warning] 40-40: [UnorderedKey] The BATCH_UPDATE_INTERVAL key should go before the CHANNEL_UPDATE_FREQUENCY key

(UnorderedKey)


[warning] 64-64: [UnorderedKey] The COHERE_SAFETY_SETTING key should go before the GENERATE_DEFAULT_TOKEN key

(UnorderedKey)


[warning] 70-70: [UnorderedKey] The DIFY_DEBUG key should go before the GENERATE_DEFAULT_TOKEN key

(UnorderedKey)


[warning] 72-72: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)

🔇 Additional comments (33)
web/src/index.css (1)

1-617: Comment-only changes LGTM.

No behavioral CSS changes detected.

docker-compose.yml (1)

3-7: Local build + image tag is fine.

Using both build and image tags is acceptable to name the built artifact.

setting/chat.go (1)

16-23: Confirm URL-encoding of chat link placeholders

setting/chat.go defines the templates (Lobe Chat, AI as Workspace, AMA, Cherry Studio, OpenCat). I could not find where {address}, {key}, or {cherryConfig} are substituted — confirm the replacement code URL-encodes values (query params: url.QueryEscape / encodeURIComponent; JSON blobs: JSON-encode then URL-escape or base64-url).

web/src/components/settings/DashboardSetting.js (1)

104-117: LGTM: modal i18nization

Modal title/buttons/body text correctly switched to t(...).

web/src/components/settings/OtherSetting.js (1)

107-113: LGTM: success/error messages localized

Success and error toasts for Logo/Home/About/Footer and update-check failure are correctly moved to t(...).

Also applies to: 123-127, 139-143, 152-156, 203-204

web/src/pages/About/index.js (1)

15-31: LGTM: about content fetch + cache

Clear fetch flow, localStorage warm-start, and translated fallback message are good.

web/src/components/table/TokensTable.js (1)

423-424: LGTM: localized confirm buttons

okText/cancelText localized correctly.

Also applies to: 793-795

web/src/pages/Detail/index.js (1)

651-651: LGTM: fallback row localized

t('无数据') is correct.

web/src/App.js (1)

280-280: LGTM — comment-only change

No functional impact. Keep as-is.

web/src/pages/Setting/Model/SettingGeminiModel.js (1)

174-175: LGTM — consolidated translation string

Cleaner t() usage; no functional change.

web/src/i18n/locales/zh.json (1)

12-13: LGTM — new warning key added

JSON remains valid.

Please ensure the same key exists in en.json and vn.json to avoid fallback-to-Chinese in non-zh locales.

web/src/components/table/RedemptionsTable.js (3)

155-156: LGTM — localized Modal buttons

okText/cancelText added correctly.


492-493: LGTM — localized Modal buttons

Consistent with other tables.


576-576: No issue — prop name is consistent (visiable)

EditRedemption reads props.visiable (web/src/pages/Redemption/EditRedemption.js:160) and RedemptionsTable passes visiable={showEdit} (web/src/components/table/RedemptionsTable.js:576).

Likely an incorrect or invalid review comment.

web/src/components/table/UsersTable.js (3)

221-223: LGTM — localized Modal buttons (promote)

Matches Semi UI API.


238-239: LGTM — localized Modal buttons (demote)

Consistent usage.


254-255: LGTM — localized Modal buttons (delete)

Consistent usage.

web/src/components/auth/LoginForm.js (2)

112-113: LGTM — localized login success toasts

Good consistency across login paths.

Also applies to: 149-150, 195-196


97-99: Major i18n gaps in auth flows — localize remaining Chinese strings

Wrap all user-facing Chinese strings with t(...) and add the corresponding keys to en/vn locales. Affected file: web/src/components/auth/LoginForm.js (around lines 97–99, 118–121, 152–166).

-      showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
+      showInfo(t('请稍后几秒重试,Turnstile 正在检查用户环境!'));
...
-      showError('登录失败,请重试');
+      showError(t('登录失败,请重试'));
...
-        showError('请输入用户名和密码!');
+        showError(t('请输入用户名和密码!'));
...
-      showError('登录失败,请重试');
+      showError(t('登录失败,请重试'));
...
-            Modal.error({
-              title: '您正在使用默认密码!',
-              content: '请立刻修改默认密码!',
+            Modal.error({
+              title: t('您正在使用默认密码!'),
+              content: t('请立刻修改默认密码!'),
web/src/pages/Home/index.js (1)

122-126: LGTM — VN headline branch

Language check aligns with vn resource key used elsewhere.

web/src/index.js (1)

13-13: Comment-only change — LGTM

Bilingual note is fine and has no runtime effect.

web/src/pages/Setting/Ratio/UpstreamRatioSync.js (1)

52-53: Localized ok/cancel labels — LGTM

Adding okText/cancelText to Modal improves i18n.

web/src/pages/Setting/Ratio/GroupRatioSettings.js (1)

105-106: Escaped JSON in help text — LGTM

The escaped JSON strings avoid broken translations and render predictably.

Also applies to: 129-130, 153-154

web/src/pages/Setting/Model/SettingGlobalModel.js (1)

89-90: i18n of extra texts/warning — LGTM

Consistent with the project’s localization approach.

Also applies to: 100-101, 110-111

web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js (1)

449-451: Localized modal actions — LGTM

okText/cancelText added for both modals; consistent with other pages.

Also applies to: 523-525

web/src/i18n/locales/en.json (1)

1008-1008: LGTM on “Save”.

Translation looks good.

web/src/components/table/ChannelsTable.js (1)

462-464: Good localization of modal buttons.

Adding okText/cancelText improves consistency across locales.

Also applies to: 516-518, 556-557, 580-581, 1590-1592, 1623-1625, 1643-1644, 1663-1664, 1683-1684

web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.js (2)

115-132: LGTM on i18n extraction.

Text around Stripe setup moved to t(); dynamic parts preserved.


137-142: Approve — i18n keys present in en.json and vn.json

Confirmed "Webhook 填", "需要包含事件", and "和" exist in web/src/i18n/locales/en.json and web/src/i18n/locales/vn.json.

web/src/pages/Channel/EditChannel.js (2)

138-147: LGTM: safer UX for “/v1” Base URL.

Good warning with localized buttons.


57-77: Avoid t() on dynamic full sentences; parameterize the prompt. — Verified

type2secretFormat + composing the placeholder with t('按照如下格式输入:') + fmt (fallback t('请输入渠道对应的鉴权密钥')) is correct; those keys exist in web/src/i18n/locales/en.json (lines 540, 547) and vn.json (lines 536, 543).

web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js (2)

487-491: LGTM: localized empty state.

Table “empty” content now translated.


140-156: Save flow i18n looks good.

Success/error messages localized; batch PUT guarded.

Comment thread docker-compose.yml
Comment on lines 16 to +21
- 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!!!)

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.

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.

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.

const { tag_name, body } = res;
if (tag_name === statusState?.status?.version) {
showSuccess(`已是最新版本:${tag_name}`);
showSuccess(t('已是最新版本:${tag_name}'));

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

Bug: incorrect i18n interpolation

Template literal inside t(...) won’t interpolate. Use i18n params.

-      showSuccess(t('已是最新版本:${tag_name}'));
+      showSuccess(t('已是最新版本:{{tag_name}}', { tag_name }));
📝 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
showSuccess(t('已是最新版本:${tag_name}'));
showSuccess(t('已是最新版本:{{tag_name}}', { tag_name }));
🤖 Prompt for AI Agents
In web/src/components/settings/OtherSetting.js around line 193, the call
showSuccess(t('已是最新版本:${tag_name}')) incorrectly uses a JS template literal
inside the i18n function; update it to use the i18n interpolation API, e.g.
showSuccess(t('已是最新版本:{{tag_name}}', { tag_name })) (or the equivalent
placeholder syntax your i18n lib expects) so the tag_name value is passed as a
parameter to t() and rendered correctly.

Comment thread web/src/helpers/utils.js
Comment on lines +106 to +121
Toast.error(t('错误:请求次数过多,请稍后再试!'));
break;
case 500:
Toast.error('错误:服务器内部错误,请联系管理员!');
Toast.error(t('错误:服务器内部错误,请联系管理员!' ));
break;
case 405:
Toast.info('本站仅作演示之用,无服务端!');
Toast.info(t('本站仅作演示之用,无服务端!'));
break;
default:
Toast.error('错误:' + error.message);
Toast.error(t('错误:') + error.message);
}
return;
}
Toast.error('错误:' + error.message);
Toast.error(t('错误:') + error.message);
} else {
Toast.error('错误:' + error);
Toast.error(t('错误:') + error);

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

Bug: t is undefined in this module

This file doesn’t define/import t; calls to t(...) will throw at runtime. Use i18next.t(...) (module-safe) and guard Axios errors without response.

Apply:

@@
-import { Toast } from '@douyinfe/semi-ui';
+import { Toast } from '@douyinfe/semi-ui';
+import i18next from 'i18next';
@@
-        case 429:
-          Toast.error(t('错误:请求次数过多,请稍后再试!'));
+        case 429:
+          Toast.error(i18next.t('错误:请求次数过多,请稍后再试!'));
           break;
         case 500:
-          Toast.error(t('错误:服务器内部错误,请联系管理员!'  ));
+          Toast.error(i18next.t('错误:服务器内部错误,请联系管理员!'));
           break;
         case 405:
-          Toast.info(t('本站仅作演示之用,无服务端!'));
+          Toast.info(i18next.t('本站仅作演示之用,无服务端!'));
           break;
         default:
-          Toast.error(t('错误:') + error.message);
+          Toast.error(i18next.t('错误:') + error.message);
       }
       return;
     }
-    Toast.error(t('错误:') + error.message);
+    Toast.error(i18next.t('错误:') + error.message);
   } else {
-    Toast.error(t('错误:') + error);
+    Toast.error(i18next.t('错误:') + error);
   }

And harden Axios branch:

-    if (error.name === 'AxiosError') {
-      switch (error.response.status) {
+    if (error.name === 'AxiosError') {
+      if (!error.response) {
+        Toast.error(i18next.t('错误:网络异常或无响应'));
+        return;
+      }
+      switch (error.response.status) {
📝 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
Toast.error(t('错误:请求次数过多,请稍后再试!'));
break;
case 500:
Toast.error('错误:服务器内部错误,请联系管理员!');
Toast.error(t('错误:服务器内部错误,请联系管理员!' ));
break;
case 405:
Toast.info('本站仅作演示之用,无服务端!');
Toast.info(t('本站仅作演示之用,无服务端!'));
break;
default:
Toast.error('错误:' + error.message);
Toast.error(t('错误:') + error.message);
}
return;
}
Toast.error('错误:' + error.message);
Toast.error(t('错误:') + error.message);
} else {
Toast.error('错误:' + error);
Toast.error(t('错误:') + error);
if (error.name === 'AxiosError') {
if (!error.response) {
Toast.error(i18next.t('错误:网络异常或无响应'));
return;
}
switch (error.response.status) {
case 429:
Toast.error(i18next.t('错误:请求次数过多,请稍后再试!'));
break;
case 500:
Toast.error(i18next.t('错误:服务器内部错误,请联系管理员!'));
break;
case 405:
Toast.info(i18next.t('本站仅作演示之用,无服务端!'));
break;
default:
Toast.error(i18next.t('错误:') + error.message);
}
return;
}
Toast.error(i18next.t('错误:') + error.message);
} else {
Toast.error(i18next.t('错误:') + error);
🤖 Prompt for AI Agents
web/src/helpers/utils.js around lines 106 to 121, the code calls t(...) which is
not defined and will throw; replace all t(...) calls with i18next.t(...) (and
add/import i18next at the top of the module) and harden the Axios error branch
by first checking whether error is an AxiosError with a response (e.g., error &&
error.isAxiosError && error.response) before switching on response.status; for
non-Axios or Axios-without-response cases fall back to i18next.t('错误:') + (error
&& (error.message || String(error)) || 'Unknown error') so you never call
message on undefined and all paths use i18next.t for translations.

Comment thread web/src/i18n/i18n.js

import enTranslation from './locales/en.json';
import zhTranslation from './locales/zh.json';
import vnTranslation from './locales/vn.json';

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

Language code: use 'vi' (add alias for detector compatibility)

Browsers report Vietnamese as vi / vi-VN. Using only vn breaks auto-detect and some manual paths.

 import zhTranslation from './locales/zh.json';
 import vnTranslation from './locales/vn.json';
@@
     resources: {
       en: {
         translation: enTranslation,
       },
       zh: {
         translation: zhTranslation,
       },
+      vi: {
+        translation: vnTranslation,
+      },
       vn: {
         translation: vnTranslation,
       },
     },
+    // optionally constrain and include dialects
+    // supportedLngs: ['en', 'zh', 'vi'],
+    // load: 'languageOnly',

Also update the language switcher to use 'vi' to be consistent.

Also applies to: 20-22

Comment on lines +303 to 305
okText={t('确定')}

</Button>

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

Broken button labels: okText is not a valid prop on Button

@douyinfe/semi-ui Button renders its label via children; okText is ignored. The button will show only the icon with no text.

Apply:

-            >
-              okText={t('确定')}
-        
+            >
+              {t('确定')}
📝 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
okText={t('确定')}
</Button>
{t('确定')}
</Button>
🤖 Prompt for AI Agents
In web/src/pages/Channel/EditTagModal.js around lines 303 to 305, the Button is
using an unsupported okText prop so the label is not rendered; remove the okText
prop and pass t('确定') as the Button's child (i.e. put the translated string
between the Button tags), ensuring any icon prop remains and the button shows
both icon and text.

Comment on lines +312 to 313
cancelText={t('取消')}
</Button>

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

Broken button labels: cancelText is not a valid prop on Button

Same issue for the cancel button; use children for text.

-            >
-              cancelText={t('取消')}
+            >
+              {t('取消')}
📝 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
cancelText={t('取消')}
</Button>
{t('取消')}
</Button>
🤖 Prompt for AI Agents
In web/src/pages/Channel/EditTagModal.js around lines 312-313, the Button is
using an invalid prop cancelText which doesn't render the label; replace the
cancelText prop with the button's children string (e.g. <Button>取消</Button>) and
remove the cancelText attribute (do the same for any other Button instances
using cancelText), ensuring the label text is passed as the Button's children.

Comment on lines +431 to 436
{ value: 10, label: t('10 条/页') },
{ value: 20, label: t('20 条/页') },
{ value: 50, label: t('50 条/页') },
{ value: 100, label: t('100 条/页') }
]
}}

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

Pagination API mismatch: pageSizeOptions expects numbers/strings, not objects

Semi UI Pagination typically accepts numbers/strings. Passing objects may render “[object Object]” or break the selector.

Apply:

-            pageSizeOptions: [
-              { value: 10, label: t('10 条/页') },
-              { value: 20, label: t('20 条/页') },
-              { value: 50, label: t('50 条/页') },
-              { value: 100, label: t('100 条/页') }
-            ]
+            pageSizeOptions: [10, 20, 50, 100]

If you need localized “items/page” text, set the component/app locale rather than customizing option labels.

📝 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
{ value: 10, label: t('10 条/页') },
{ value: 20, label: t('20 条/页') },
{ value: 50, label: t('50 条/页') },
{ value: 100, label: t('100 条/页') }
]
}}
pageSizeOptions: [10, 20, 50, 100]
}}
🤖 Prompt for AI Agents
In web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js around lines 431 to
436, the Pagination pageSizeOptions is being passed an array of objects ({
value, label }) which Semi UI expects to be an array of numbers or strings;
replace the objects with plain numbers or strings (e.g. [10, 20, 50, 100] or
['10','20','50','100']) and remove custom label usage there, and if localized
"items/page" text is needed, configure the component/app locale or use the
Pagination's locale props instead of embedding labels in pageSizeOptions.

Comment on lines +737 to 739
okText={t('确定')}
cancelText={t('取消')}
onCancel={handleModalClose}

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

Verify ChannelSelectorModal supports okText/cancelText

Ensure ChannelSelectorModal forwards okText/cancelText to its internal Modal; otherwise these props are no-ops.


🏁 Script executed:

#!/bin/bash
# Find ChannelSelectorModal and check if okText/cancelText are supported/forwarded
fd -t f 'ChannelSelectorModal.*\.jsx?$' web/src | xargs -I{} sh -c 'echo "== {} =="; rg -n "function\\s+ChannelSelectorModal|export\\s+default\\s+function\\s+ChannelSelectorModal|const\\s+ChannelSelectorModal" -n {} -C2; rg -n "okText|cancelText|<Modal" -n {} -C2'

Length of output: 310


Forward okText/cancelText through ChannelSelectorModal to its internal Modal

web/src/components/settings/ChannelSelectorModal.js currently renders (≈lines 185–187) without okText/cancelText, so props passed from UpstreamRatioSync are no-ops — forward them to (e.g. okText={okText} cancelText={cancelText}) or remove the upstream props.

🤖 Prompt for AI Agents
In web/src/pages/Setting/Ratio/UpstreamRatioSync.js around lines 737–739 the
parent passes okText/cancelText to ChannelSelectorModal, but in
web/src/components/settings/ChannelSelectorModal.js (around lines 185–187) the
internal <Modal visible={visible} onCancel={onCancel} /> call does not receive
okText/cancelText so the props are ignored; update ChannelSelectorModal to
accept okText and cancelText (from props/destructure) and forward them into the
internal Modal (e.g., add okText={okText} cancelText={cancelText}), or if you
prefer, remove those upstream props — forwarding is recommended to preserve
behavior.

@nosime nosime closed this Sep 14, 2025
jiutubaba pushed a commit to jiutubaba/fx-api that referenced this pull request May 17, 2026
…bindings-i18n

fix(profile): 修正邮箱重复显示问题并添加国际化语言支持
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant