Conversation
- Install i18next, react-i18next, i18next-browser-languagedetector - Create i18n config with en and zh-CN locales - Replace hardcoded strings across 36 component/page files - Add Ant Design ConfigProvider with dynamic locale switching - Add language switcher in top navigation (desktop + mobile) Note: user.tsx i18n pending (complex file, separate commit)
✅ Deploy Preview for pushy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
More reviews will be available in 47 minutes and 2 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds ChangesFull-app i18n via i18next + react-i18next
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
- Replace all 63 Chinese strings with t() calls - Convert module-level constants to functions accepting t - Add useTranslation to 7 components - TypeScript and biome checks pass
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 (1)
src/pages/manage/components/version-table.tsx (1)
153-173: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winFinish localizing the delete dialog body.
This function now translates the modal title, but the body still uses
versionNames.join(','), which leaves Chinese punctuation in English mode. Rendering the names as a list avoids that locale-specific leak.Suggested fix
Modal.confirm({ title: t('version_table.delete_title'), - content: versionNames.join(','), + content: ( + <div className="max-h-48 overflow-y-auto"> + {versionNames.map((name) => ( + <div key={name}>{name}</div> + ))} + </div> + ), maskClosable: true,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/manage/components/version-table.tsx` around lines 153 - 173, The delete dialog body in removeSelectedVersions is still building a locale-specific string with versionNames.join(','), which leaks Chinese punctuation into non-Chinese locales. Update the Modal.confirm content to render the selected version names as a locale-aware list instead of joining with a hardcoded delimiter, keeping the existing translation flow used by t('version_table.delete_title') and the versionNames collection logic.
🧹 Nitpick comments (4)
src/pages/register.tsx (1)
129-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
rootRouterPath.loginfor this link.This file already uses
rootRouterPathfor navigation, so hardcoding"/login"creates a second route source that can drift later.♻️ Proposed fix
- <Link to="/login">{t('register.has_account')}</Link> + <Link to={rootRouterPath.login}>{t('register.has_account')}</Link>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/register.tsx` at line 129, The register page link is hardcoded to "/login" instead of using the shared router constant, which can drift from the app’s routing source of truth. Update the Link in register page to use rootRouterPath.login, following the existing rootRouterPath usage in this component, so navigation stays consistent and centralized.src/pages/inactivated.tsx (1)
49-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse
rootRouterPath.loginfor the back button target.The guard above already navigates with
rootRouterPath.login; hardcoding"/user"here makes the destination easier to drift away from the translated “back login” label.♻️ Proposed fix
- <Button key="back" href="/user"> + <Button key="back" href={rootRouterPath.login}> {t('inactivated.back_login')} </Button>,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/inactivated.tsx` around lines 49 - 50, The back button in the inactivated page is hardcoding the login destination instead of reusing the shared router constant. Update the Button in the inactivated page to use rootRouterPath.login, matching the existing navigation guard and keeping the target consistent with the “back login” action.src/pages/reset-password/components/success.tsx (1)
10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the shared login route instead of hardcoding
/#/login.This introduces a second login URL shape in the same flow. Keeping the CTA on the router-managed login path avoids route drift and the forced full-page reload here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/reset-password/components/success.tsx` around lines 10 - 12, The reset-password success CTA is hardcoding the login URL, which bypasses the shared router path and can cause route drift. Update the Button in the Success component to use the existing login route constant/helper used elsewhere in the app instead of the literal /#/login, so the reset flow stays aligned with the router-managed login path.src/pages/manage/components/publish-feature-table.tsx (1)
20-37: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winKeep tag state separate from translated copy.
The tag color now depends on
text.includes('✓')/text.includes('⚠'). That makes every locale string responsible for preserving those glyphs. Store a semantic status in the row data and translate only the displayed label.Also applies to: 57-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/manage/components/publish-feature-table.tsx` around lines 20 - 37, The publish feature table is coupling tag color to translated text via the `text.includes('✓')` / `text.includes('⚠')` logic, so the state should be made semantic instead of inferred from localized copy. Update the row data used in `PublishFeatureTable` (and the related entries in the table definition block) to carry an explicit status field for each tag, and keep `t(...)` only for the visible label. Then adjust the tag rendering logic to use that status field for color/variant selection instead of inspecting the translated string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/app-drawer.tsx`:
- Around line 136-142: The app count and check-count translations in app-drawer
should keep their count values numeric for i18next pluralization, rather than
passing locale-formatted strings. Update the calls in the app list and the
checkbox label to pass the raw numeric values from apps.length and the active
check count, and handle any locale-specific formatting only in the rendered text
layer. Use the translation keys and the surrounding AppDrawer rendering logic to
locate the affected count labels.
In `@src/components/daily-check-quota.tsx`:
- Around line 37-40: The tier label in daily-check-quota still falls back to
localized data from quotas.title, which can surface Chinese plan names in
English mode. Update the rendering in daily-check-quota (including the other
affected tier display path) to resolve known tiers through t(...) first, then
fall back to a custom server-provided title, and only use user.tier as the last
resort. Use the existing tier lookup points around quota, quotas[user.tier], and
the tier text rendering to keep the fix consistent.
In `@src/components/top-navigation.tsx`:
- Around line 72-89: The `top-navigation` menu items for documentation and about
are using translated labels but fixed `href`s, so the destination does not match
the active locale. Update the `TopNavigation` link setup for the `document` and
`about` entries to choose the locale-specific URL based on the current language
(using the same i18n/lang source already available in this component), or
otherwise ensure each label points to the matching language page.
In `@src/i18n/locales/zh-CN.json`:
- Around line 49-135: The zh-CN locale bundle is missing several keys that the
new UI expects, so align it with the English key set by adding the absent
entries under user, deps_table, and the full publish_feature_table namespace in
zh-CN.json. Use the existing translation patterns in that file to add localized
strings for symbols like user.fetching_addon_quote, user.per_year,
user.upgrade_button, deps_table.cli_required,
deps_table.native_package_with_name, deps_table.ota_version_with_name,
bind_package.publish, and publish_feature_table so the localized screens don’t
fall back to raw keys.
In `@src/index.tsx`:
- Around line 68-70: The Antd locale lookup in App is using i18n.language
directly, which can remain an unresolved detector value and miss the normalized
translation language. Update the locale selection to use the resolved language
from the i18n instance when indexing antdLocaleMap, so the App component picks
the correct Antd locale instead of falling back to zhCN for English. Keep the
fix localized to the App function and the antdLocaleMap lookup.
In `@src/pages/audit-logs.tsx`:
- Line 217: The audit logs page is using a fixed Day.js locale at module load,
so relative timestamps from date.fromNow() can stay in the wrong language even
after useTranslation switches the UI language. Update the audit-logs page logic
around useTranslation and the time rendering to sync Day.js with the active i18n
language, preferably by setting dayjs.locale from the current language inside
the component lifecycle instead of once at import time.
- Around line 123-129: The audit log action filter is using translated text as
the query value, so `selectedAction` can break across locales and shared URLs.
Update `getActionOptions` (and the related select/query handling in the audit
logs page) to store a stable action key derived from `getActionMap` output, such
as `METHOD + normalizedPath`, and use the translated string only for the
displayed `label`. Make sure the same stable key is used wherever the action
value is written to or read from the URL/query param so filtering and selection
remain consistent after language changes.
In `@src/pages/manage/index.tsx`:
- Line 73: The bulk-select aria-label in the Manage page is collapsing to just
filterLabel because the conditional adds nothing, so screen readers miss the
action description. Update the label in the manage page component to use a
descriptive bulk-select text for the checkbox action, and remove the dead
t('common.save') branch from that aria-label expression.
In `@src/pages/realtime-metrics.tsx`:
- Around line 95-100: The tooltip text in formatTooltipItem is assembling
localized output by concatenating translation fragments, which can produce
awkward spacing and word order. Update the realtime-metrics translations and the
code paths using formatTooltipItem/current_dimension to use full interpolated
messages instead of stitching together checks_suffix or similar fragments, so
the locale controls the complete phrase naturally.
In `@src/pages/reset-password/index.tsx`:
- Around line 21-25: The reset-password success route is still using a step
value that is outside the Steps range, so the progress indicator becomes out of
sync on the success screen. Update the reset-password page logic in the
component that renders Steps so the success route is mapped to display index 2
before passing the value into current, while keeping the existing step routes
and content rendering unchanged.
---
Outside diff comments:
In `@src/pages/manage/components/version-table.tsx`:
- Around line 153-173: The delete dialog body in removeSelectedVersions is still
building a locale-specific string with versionNames.join(','), which leaks
Chinese punctuation into non-Chinese locales. Update the Modal.confirm content
to render the selected version names as a locale-aware list instead of joining
with a hardcoded delimiter, keeping the existing translation flow used by
t('version_table.delete_title') and the versionNames collection logic.
---
Nitpick comments:
In `@src/pages/inactivated.tsx`:
- Around line 49-50: The back button in the inactivated page is hardcoding the
login destination instead of reusing the shared router constant. Update the
Button in the inactivated page to use rootRouterPath.login, matching the
existing navigation guard and keeping the target consistent with the “back
login” action.
In `@src/pages/manage/components/publish-feature-table.tsx`:
- Around line 20-37: The publish feature table is coupling tag color to
translated text via the `text.includes('✓')` / `text.includes('⚠')` logic, so
the state should be made semantic instead of inferred from localized copy.
Update the row data used in `PublishFeatureTable` (and the related entries in
the table definition block) to carry an explicit status field for each tag, and
keep `t(...)` only for the visible label. Then adjust the tag rendering logic to
use that status field for color/variant selection instead of inspecting the
translated string.
In `@src/pages/register.tsx`:
- Line 129: The register page link is hardcoded to "/login" instead of using the
shared router constant, which can drift from the app’s routing source of truth.
Update the Link in register page to use rootRouterPath.login, following the
existing rootRouterPath usage in this component, so navigation stays consistent
and centralized.
In `@src/pages/reset-password/components/success.tsx`:
- Around line 10-12: The reset-password success CTA is hardcoding the login URL,
which bypasses the shared router path and can cause route drift. Update the
Button in the Success component to use the existing login route constant/helper
used elsewhere in the app instead of the literal /#/login, so the reset flow
stays aligned with the router-managed login path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4bcc56f0-c6ea-4efe-9a44-0ed365d39ffa
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (39)
package.jsonsrc/components/app-detail-header.tsxsrc/components/app-drawer.tsxsrc/components/app-settings-modal.tsxsrc/components/create-app-modal.tsxsrc/components/daily-check-quota.tsxsrc/components/error-boundary.tsxsrc/components/footer.tsxsrc/components/top-navigation.tsxsrc/i18n/index.tssrc/i18n/locales/en.jsonsrc/i18n/locales/zh-CN.jsonsrc/index.tsxsrc/pages/activate.tsxsrc/pages/admin-apps.tsxsrc/pages/admin-config.tsxsrc/pages/admin-metrics.tsxsrc/pages/admin-service-status.tsxsrc/pages/admin-users.tsxsrc/pages/api-tokens.tsxsrc/pages/apps.tsxsrc/pages/audit-logs.tsxsrc/pages/inactivated.tsxsrc/pages/login.tsxsrc/pages/manage/components/bind-package.tsxsrc/pages/manage/components/commit.tsxsrc/pages/manage/components/deps-table.tsxsrc/pages/manage/components/package-list.tsxsrc/pages/manage/components/publish-feature-table.tsxsrc/pages/manage/components/setting-modal.tsxsrc/pages/manage/components/version-table.tsxsrc/pages/manage/index.tsxsrc/pages/realtime-metrics.tsxsrc/pages/register.tsxsrc/pages/reset-password/components/send-email.tsxsrc/pages/reset-password/components/set-password.tsxsrc/pages/reset-password/components/success.tsxsrc/pages/reset-password/index.tsxsrc/pages/welcome.tsx
| <div className="font-medium text-slate-900"> | ||
| {t('app_drawer.app_list')} | ||
| </div> | ||
| <div className="text-slate-500 text-xs"> | ||
| 共 {apps.length.toLocaleString()} 个应用 | ||
| {t('app_drawer.apps_count', { | ||
| count: apps.length.toLocaleString(), | ||
| })} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
For i18next/react-i18next pluralization, does the special count option need to stay numeric instead of a formatted string?
💡 Result:
Yes, the special count option must remain a numeric value (a JavaScript number) to function correctly for pluralization [1][2]. i18next uses the provided count value to determine which plural form (such as _one, _other, or language-specific forms) to select based on the rules for the active language [3][4]. Because the library relies on these numeric rules to perform calculations, passing a formatted string will prevent it from correctly identifying the appropriate translation key [2]. Key considerations include: 1. Numeric Requirement: The count must be a number [1][2]. If you need to display a formatted number (e.g., "1,000" or "1.000"), you should pass the raw numeric value to the count option for pluralization logic, and use i18next's interpolation formatting or a separate formatting library to handle the display string independently [3][5][4]. 2. Inference Limitations: In react-i18next, while the Trans component can automatically infer the count if it is present as {{count}} in the children, this inference also requires the value to be a JavaScript number [2]. String values will not be correctly inferred [2]. 3. Fallback Behavior: If a valid numeric count is not provided, i18next cannot determine the plural form and will fail to resolve the key correctly, often returning the raw key name instead of the translated string [3][4].
Citations:
- 1: https://react.i18next.com/latest/icu-trans-component.md
- 2: https://react.i18next.com/latest/trans-component
- 3: https://www.i18next.com/translation-function/plurals
- 4: https://localepack.app/guides/i18next-json-format-namespaces-plurals
- 5: https://www.i18next.com/misc/json-format
Keep count numeric and format the display separately
count must stay a number for i18next plural selection; apps.length.toLocaleString() breaks that contract. The same applies to the check-count label below, which should use the active UI locale only for rendering.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/app-drawer.tsx` around lines 136 - 142, The app count and
check-count translations in app-drawer should keep their count values numeric
for i18next pluralization, rather than passing locale-formatted strings. Update
the calls in the app list and the checkbox label to pass the raw numeric values
from apps.length and the active check count, and handle any locale-specific
formatting only in the rendered text layer. Use the translation keys and the
surrounding AppDrawer rendering logic to locate the affected count labels.
| {t('daily_check_quota.tier')} | ||
| {quota?.title ?? | ||
| quotas[user.tier as keyof typeof quotas]?.title ?? | ||
| user.tier} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Translate the tier name instead of reading title from quotas.
These paths still fall back to quota?.title / quotas[user.tier].title, and src/constants/quotas.ts:1-65 stores those titles in Chinese. English mode will still show mixed-language plan names here. Resolve known tiers through t(...) first, then only fall back to a custom server title or user.tier.
Also applies to: 101-105
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/daily-check-quota.tsx` around lines 37 - 40, The tier label in
daily-check-quota still falls back to localized data from quotas.title, which
can surface Chinese plan names in English mode. Update the rendering in
daily-check-quota (including the other affected tier display path) to resolve
known tiers through t(...) first, then fall back to a custom server-provided
title, and only use user.tier as the last resort. Use the existing tier lookup
points around quota, quotas[user.tier], and the tier text rendering to keep the
fix consistent.
| { | ||
| key: 'document', | ||
| icon: <ReadOutlined />, | ||
| label: ( | ||
| <ExtLink href="https://pushy.reactnative.cn/docs/getting-started.html"> | ||
| {t('nav.documentation')} | ||
| </ExtLink> | ||
| ), | ||
| }, | ||
| { | ||
| key: 'about', | ||
| icon: <InfoCircleOutlined />, | ||
| label: ( | ||
| <ExtLink href="https://reactnative.cn/about.html"> | ||
| {t('nav.about_us')} | ||
| </ExtLink> | ||
| ), | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use locale-aware docs/about destinations.
These labels are translated, but the hrefs stay fixed. That means the bilingual nav only changes the text; “Documentation” / “About us” still send every user to the same target. Please branch the destination by the active language, or keep the label aligned with the actual page language.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/top-navigation.tsx` around lines 72 - 89, The `top-navigation`
menu items for documentation and about are using translated labels but fixed
`href`s, so the destination does not match the active locale. Update the
`TopNavigation` link setup for the `document` and `about` entries to choose the
locale-specific URL based on the current language (using the same i18n/lang
source already available in this component), or otherwise ensure each label
points to the matching language page.
| "user": { | ||
| "invoice_hint": "请发送邮件至 <1>hi@charmlot.com</1>,并写明:", | ||
| "invoice_company": "公司名称、税号、注册邮箱、接收发票邮箱(不写则发送到注册邮箱),附带支付截图。", | ||
| "invoice_default": "我们默认会回复普通电子发票到接收邮箱(请同时留意垃圾邮件),类目为软件服务。", | ||
| "purchasing_note": "只可续费相同服务版本或升级更高版本,如果您需要购买较低的服务版本,请等待当前版本过期,或联系 QQ 客服 34731408 手动处理。", | ||
| "view_invoice": "点此查看如何申请发票", | ||
| "current_expire": "当前到期日", | ||
| "price_month": "个月", | ||
| "annual_billing": "个月(年付)", | ||
| "quoting": "报价中", | ||
| "order_settle": "按订单结算", | ||
| "renewal_unavailable": "当前版本暂未返回可续费价格", | ||
| "fetching_renewal_quote": "正在获取续费报价", | ||
| "renew": "续费", | ||
| "renew_after_expire": "续费后到期日", | ||
| "about_discount": "约{{discount}}折优惠", | ||
| "addon_price_monthly": "当前价格含加购费用每月 {{price}}", | ||
| "jumping": "跳转中", | ||
| "upgrade_purchase": "升级购买", | ||
| "upgrade_hint_free": "选择目标版本后按年付开通服务。", | ||
| "upgrade_hint_paid": "补差价由后端按剩余有效期报价,未超过优惠阈值按月费差额折算,超过后按年费优惠折算。", | ||
| "purchase_after_pay": "购买后从支付日起开通服务", | ||
| "annual_pay": "年付", | ||
| "proration": "补差价", | ||
| "days_unit": "天", | ||
| "check_quota_daily": "检查次数每日", | ||
| "app_count": "应用个数", | ||
| "native_pkg_count": "原生包数", | ||
| "hotfix_count": "热更包数", | ||
| "count_unit": "个", | ||
| "wan_unit": "万", | ||
| "account_info": "账户信息", | ||
| "username": "用户名", | ||
| "email": "邮箱", | ||
| "service_version": "服务版本", | ||
| "service_expire": "服务有效期至", | ||
| "no_expire": "无", | ||
| "purchase_note": "购买说明", | ||
| "quota_details": "额度详情", | ||
| "view_pricing": "查看价格表", | ||
| "bank_transfer": "使用网银转账", | ||
| "logout": "退出登录", | ||
| "logged_out": "您已退出登录", | ||
| "app_count_label": "应用数量", | ||
| "app_count_note": "当前账户下应用总数", | ||
| "hotfix_count_label": "热更包数量", | ||
| "native_pkg_count_label": "原生包数量", | ||
| "counting_hotfix": "正在统计各应用热更包数量", | ||
| "counting_native": "正在统计各应用原生包数量", | ||
| "max_single_app": "最高单应用使用量", | ||
| "counting": "统计中", | ||
| "over_quota": "超额", | ||
| "spec_limits": "规格限制", | ||
| "single_native_size": "单个原生包大小", | ||
| "single_hotfix_size": "单个热更包大小", | ||
| "check_quota_limit": "检查额度上限", | ||
| "per_day": "次 / 日", | ||
| "no_7day_details": "暂无 7 天明细", | ||
| "payment_invalid": "支付链接无效", | ||
| "addon_eligible_hint": "仅高级版及以上可加购检查额度,当前版本可先升级后再加购。", | ||
| "addon_empty": "暂无可购买项目", | ||
| "check_quota_addon": "检查额度加购", | ||
| "addon_price_desc": "每增加 {{quota}} 次 / 日,每月额外收费 {{price}}。", | ||
| "addon_proration_hint": "按剩余天数补差价。收费基准为:{{quota}}次/日,每月加收 {{price}},可叠加购买。", | ||
| "addon_purchase_hint": "收费基准为:{{quota}}次/日,每月加收 {{price}},可叠加购买。", | ||
| "addon_title_with_expire": "加购检查额度(当前有效期不变:至 {{date}})", | ||
| "addon_title": "加购检查额度", | ||
| "daily_check_title": "每日检查额度", | ||
| "daily_check_desc": "客户端检查热更新时消耗,按账户全部应用汇总。", | ||
| "remaining_today": "今日剩余额度", | ||
| "quota_limit_info": "上限 {{dailyQuota}} 次 / 日(套餐内 {{included}} 次 + 加购 {{extra}} 次)", | ||
| "exceeded_by": "已超出 {{count}} 次", | ||
| "low_below": "低于 {{percent}}%,请留意检查频率", | ||
| "recent_7day_avg": "最近 7 天平均剩余额度 {{avg}}({{range}})", | ||
| "count_suffix": "次", | ||
| "already_exceeded": "已超额", | ||
| "already_exhausted": "已用尽", | ||
| "low": "偏低", | ||
| "purchasable_tiers": { | ||
| "standard": "标准版", | ||
| "premium": "高级版", | ||
| "pro": "专业版", | ||
| "vip1": "大客户VIP1版", | ||
| "vip2": "大客户VIP2版", | ||
| "vip3": "大客户VIP3版" | ||
| } | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Sync the zh-CN bundle with the English key set.
src/i18n/locales/zh-CN.json is missing several keys that src/i18n/locales/en.json defines and the newly localized screens depend on, including user.fetching_addon_quote, user.per_year, user.upgrade_button, deps_table.cli_required, deps_table.native_package_with_name, deps_table.ota_version_with_name, bind_package.publish, and the entire publish_feature_table.* namespace. Chinese users will get fallback/raw keys in those views.
Also applies to: 633-688, 714-719
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/i18n/locales/zh-CN.json` around lines 49 - 135, The zh-CN locale bundle
is missing several keys that the new UI expects, so align it with the English
key set by adding the absent entries under user, deps_table, and the full
publish_feature_table namespace in zh-CN.json. Use the existing translation
patterns in that file to add localized strings for symbols like
user.fetching_addon_quote, user.per_year, user.upgrade_button,
deps_table.cli_required, deps_table.native_package_with_name,
deps_table.ota_version_with_name, bind_package.publish, and
publish_feature_table so the localized screens don’t fall back to raw keys.
| function App() { | ||
| const { i18n } = useTranslation(); | ||
| const antdLocale = antdLocaleMap[i18n.language] ?? zhCN; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use the resolved i18n language for the Antd locale lookup. Line 70 keys antdLocaleMap with i18n.language, which can stay as a detector value like en-US even when translations resolve to en. That misses the en entry and falls back to zhCN, so English pages can render with Chinese Antd locale strings.
Suggested fix
function App() {
const { i18n } = useTranslation();
- const antdLocale = antdLocaleMap[i18n.language] ?? zhCN;
+ const language = i18n.resolvedLanguage ?? i18n.language;
+ const antdLocale = antdLocaleMap[language] ?? zhCN;📝 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.
| function App() { | |
| const { i18n } = useTranslation(); | |
| const antdLocale = antdLocaleMap[i18n.language] ?? zhCN; | |
| function App() { | |
| const { i18n } = useTranslation(); | |
| const language = i18n.resolvedLanguage ?? i18n.language; | |
| const antdLocale = antdLocaleMap[language] ?? zhCN; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/index.tsx` around lines 68 - 70, The Antd locale lookup in App is using
i18n.language directly, which can remain an unresolved detector value and miss
the normalized translation language. Update the locale selection to use the
resolved language from the i18n instance when indexing antdLocaleMap, so the App
component picks the correct Antd locale instead of falling back to zhCN for
English. Keep the fix localized to the App function and the antdLocaleMap
lookup.
| function getActionOptions(t: TranslateFn) { | ||
| return Object.values(getActionMap(t)) | ||
| .sort() | ||
| .map((value) => ({ | ||
| label: value, | ||
| value, | ||
| })); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Store a stable action key in the query param.
The select now uses the translated action text as its value, so changing languages (or opening a shared URL in another locale) leaves selectedAction unmatched and the filter stops working. Keep the URL value stable — e.g. METHOD + normalizedPath — and translate only the display label.
Also applies to: 236-238, 271-271
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/audit-logs.tsx` around lines 123 - 129, The audit log action filter
is using translated text as the query value, so `selectedAction` can break
across locales and shared URLs. Update `getActionOptions` (and the related
select/query handling in the audit logs page) to store a stable action key
derived from `getActionMap` output, such as `METHOD + normalizedPath`, and use
the translated string only for the displayed `label`. Make sure the same stable
key is used wherever the action value is written to or read from the URL/query
param so filtering and selection remain consistent after language changes.
| }; | ||
|
|
||
| export const AuditLogs = () => { | ||
| const { t } = useTranslation(); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Sync Day.js locale with the active i18n language.
This page now translates its UI, but dayjs.locale('zh-cn') is still fixed at module load. date.fromNow() in the time column will stay Chinese in English mode, and the global Day.js locale can remain wrong after this page is opened.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/audit-logs.tsx` at line 217, The audit logs page is using a fixed
Day.js locale at module load, so relative timestamps from date.fromNow() can
stay in the wrong language even after useTranslation switches the UI language.
Update the audit-logs page logic around useTranslation and the time rendering to
sync Day.js with the active i18n language, preferably by setting dayjs.locale
from the current language inside the component lifecycle instead of once at
import time.
| <span className="inline-flex items-center gap-2"> | ||
| <Checkbox | ||
| aria-label={`${filterLabel}全选`} | ||
| aria-label={`${filterLabel}${t('common.save') ? '' : ''}`} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use a descriptive bulk-select label here.
Line 73 always collapses to filterLabel, so the extra t('common.save') lookup is dead code and screen readers only get the filter name instead of the checkbox action.
Suggested fix
- aria-label={`${filterLabel}${t('common.save') ? '' : ''}`}
+ aria-label={t('manage.select_visible_packages', {
+ filter: filterLabel,
+ })}📝 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.
| aria-label={`${filterLabel}${t('common.save') ? '' : ''}`} | |
| aria-label={t('manage.select_visible_packages', { | |
| filter: filterLabel, | |
| })} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/manage/index.tsx` at line 73, The bulk-select aria-label in the
Manage page is collapsing to just filterLabel because the conditional adds
nothing, so screen readers miss the action description. Update the label in the
manage page component to use a descriptive bulk-select text for the checkbox
action, and remove the dead t('common.save') branch from that aria-label
expression.
| const formatTooltipItem = (point: ChartDataPoint, t: (key: string) => string) => { | ||
| const countLabel = `${point.value.toLocaleString()}${t('realtime_metrics.checks_suffix')}`; | ||
| if (point.isTotal || point.sharePercent === undefined) { | ||
| return countLabel; | ||
| } | ||
| return `${countLabel} (${point.sharePercent.toFixed(1)}%)`; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Avoid building localized phrases by concatenation.
checks_suffix and current_dimension currently rely on the translation text to carry the right spacing and word order, so English will render awkwardly (for example 1,234checks or Current dimensionBundle). These should be full interpolated messages instead of stitched fragments.
Also applies to: 493-499
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/realtime-metrics.tsx` around lines 95 - 100, The tooltip text in
formatTooltipItem is assembling localized output by concatenating translation
fragments, which can produce awkward spacing and word order. Update the
realtime-metrics translations and the code paths using
formatTooltipItem/current_dimension to use full interpolated messages instead of
stitching together checks_suffix or similar fragments, so the locale controls
the complete phrase naturally.
| current={Number(step)} | ||
| items={[ | ||
| { title: '输入绑定邮箱' }, | ||
| { title: '设置新密码' }, | ||
| { title: '设置成功' }, | ||
| { title: t('reset_password.step_email') }, | ||
| { title: t('reset_password.step_password') }, | ||
| { title: t('reset_password.step_success') }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The success route is one step past the Steps list.
SetPassword still routes to step '3', but this Steps component only has indices 0..2. On the success screen, current={Number(step)} becomes 3, so the indicator falls out of sync with the rendered content. Map the success route to display index 2 before passing it to current.
Suggested fix
- current={Number(step)}
+ current={step === '3' ? 2 : Number(step)}📝 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.
| current={Number(step)} | |
| items={[ | |
| { title: '输入绑定邮箱' }, | |
| { title: '设置新密码' }, | |
| { title: '设置成功' }, | |
| { title: t('reset_password.step_email') }, | |
| { title: t('reset_password.step_password') }, | |
| { title: t('reset_password.step_success') }, | |
| current={step === '3' ? 2 : Number(step)} | |
| items={[ | |
| { title: t('reset_password.step_email') }, | |
| { title: t('reset_password.step_password') }, | |
| { title: t('reset_password.step_success') }, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/reset-password/index.tsx` around lines 21 - 25, The reset-password
success route is still using a step value that is outside the Steps range, so
the progress indicator becomes out of sync on the success screen. Update the
reset-password page logic in the component that renders Steps so the success
route is mapped to display index 2 before passing the value into current, while
keeping the existing step routes and content rendering unchanged.
变更内容
待完成
Summary by CodeRabbit
New Features
Bug Fixes