feat: complete Vietnamese localization for new-api - #1802
Conversation
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
WalkthroughThe 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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.
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. Comment |
There was a problem hiding this comment.
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 changeClosing 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 directlyDirectly 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 (labelvstext) breaks Select optionsElsewhere
optionListuses{ 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 i18nIn 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 formSeveral 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 headerSetting '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 bracesBiome warns. Use {/* ... */} inside JSX.
- /* Webhook Settings */ + {/* Webhook Settings */}.env.example (2)
9-11: Safeguard defaults: disable debug/profiling in exampleShip 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 examplesdotenv-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 toastTranslate the success message for manageToken.
- showSuccess('操作成功完成!'); + showSuccess(t('操作成功完成!'));web/src/components/table/RedemptionsTable.js (1)
595-596: Pagination API mismatch:pageSizeOptionsvs Semi UI’spageSizeOptsElsewhere (UsersTable) you use
pageSizeOpts. Using the AntD-stylepageSizeOptionsmay 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 localizedWrap with
t()to align with the rest.- showSuccess('操作成功完成!'); + showSuccess(t('操作成功完成!'));
173-174: i18n gap: inline label not localizedAvoid 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:readonly→readOnlyCurrent prop is ignored; input becomes editable unintentionally.
- <Input - readonly + <Input + readOnly
63-64: Optional: unify duplicate “copied to clipboard” keysOther 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 orderThe 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
📒 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 placeholderssetting/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 i18nizationModal title/buttons/body text correctly switched to t(...).
web/src/components/settings/OtherSetting.js (1)
107-113: LGTM: success/error messages localizedSuccess 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 + cacheClear fetch flow, localStorage warm-start, and translated fallback message are good.
web/src/components/table/TokensTable.js (1)
423-424: LGTM: localized confirm buttonsokText/cancelText localized correctly.
Also applies to: 793-795
web/src/pages/Detail/index.js (1)
651-651: LGTM: fallback row localizedt('无数据') is correct.
web/src/App.js (1)
280-280: LGTM — comment-only changeNo functional impact. Keep as-is.
web/src/pages/Setting/Model/SettingGeminiModel.js (1)
174-175: LGTM — consolidated translation stringCleaner
t()usage; no functional change.web/src/i18n/locales/zh.json (1)
12-13: LGTM — new warning key addedJSON 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/cancelTextadded correctly.
492-493: LGTM — localized Modal buttonsConsistent 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 toastsGood consistency across login paths.
Also applies to: 149-150, 195-196
97-99: Major i18n gaps in auth flows — localize remaining Chinese stringsWrap 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 branchLanguage check aligns with
vnresource key used elsewhere.web/src/index.js (1)
13-13: Comment-only change — LGTMBilingual note is fine and has no runtime effect.
web/src/pages/Setting/Ratio/UpstreamRatioSync.js (1)
52-53: Localized ok/cancel labels — LGTMAdding okText/cancelText to Modal improves i18n.
web/src/pages/Setting/Ratio/GroupRatioSettings.js (1)
105-106: Escaped JSON in help text — LGTMThe 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 — LGTMConsistent 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 — LGTMokText/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.jsonConfirmed "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. — Verifiedtype2secretFormat + 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.
| - 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!!!) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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 .envOptional: 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'; |
There was a problem hiding this comment.
💡 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 -C2Length 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 || trueLength 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('刷新失败')); |
There was a problem hiding this comment.
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.
| 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}')); |
There was a problem hiding this comment.
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.
| 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.
| 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); |
There was a problem hiding this comment.
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.
| 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.
|
|
||
| import enTranslation from './locales/en.json'; | ||
| import zhTranslation from './locales/zh.json'; | ||
| import vnTranslation from './locales/vn.json'; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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
| okText={t('确定')} | ||
|
|
||
| </Button> |
There was a problem hiding this comment.
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.
| 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.
| cancelText={t('取消')} | ||
| </Button> |
There was a problem hiding this comment.
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.
| 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.
| { value: 10, label: t('10 条/页') }, | ||
| { value: 20, label: t('20 条/页') }, | ||
| { value: 50, label: t('50 条/页') }, | ||
| { value: 100, label: t('100 条/页') } | ||
| ] | ||
| }} |
There was a problem hiding this comment.
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.
| { 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.
| okText={t('确定')} | ||
| cancelText={t('取消')} | ||
| onCancel={handleModalClose} |
There was a problem hiding this comment.
💡 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.
…bindings-i18n fix(profile): 修正邮箱重复显示问题并添加国际化语言支持
📝 Vietnamese Localization for new-api
🎯 Overview
Complete Vietnamese (vi-VN) localization for the new-api project, covering both backend and frontend components.
🔧 Backend Changes
main.gowith Vietnamese language supportsetting/chat.go- Chat configurationsetting/payment.go- Payment settingssetting/user_usable_group.go- User group managementdocker-compose.ymland.env.examplefor i18n support🎨 Frontend Changes
web/src/i18n/locales/vn.json(2,600+ entries)en.json) and Chinese (zh.json) locale files📋 Features Covered
🧪 Testing
📊 Statistics
🇻🇳 This PR enables Vietnamese users to use new-api with a fully localized interface.
Summary by CodeRabbit
New Features
Improvements
Chores