语种表按需加载:首屏 gzip 减少 102 KB(-10.1%) - #2
Open
Tianbuyu-wwx wants to merge 2 commits into
Open
Tianbuyu-wwx wants to merge 2 commits into
Tianbuyu-wwx wants to merge 2 commits into
Conversation
The seven tables were merged into one chunk that every visitor downloaded before the first frame — 535 KB raw / 155 KB gzip, the largest item on the start-up payload, and every locale's prose rather than theirs. English has to stay eager (`translateFor` falls back to it for any key a locale is missing), so it does; the other six are now their own chunks and arrive with the language that reads them. - `i18n/locales/index.ts`: one static specifier per table. `ensureLocaleStrings(locale)` registers the table as an i18n OVERLAY through the same `registerExtraStrings` the Help Center already uses for its prose, and shares one in-flight import between concurrent callers. A failed fetch is dropped from the memo so a later attempt retries; the caller still sees the rejection. - `i18n/context.tsx` reads the eager table (English alone) and then the overlay, unchanged otherwise. - `main.tsx` renders once the saved language's table is in hand, for the same reason the cursor properties are written there: a frame in the wrong language is a flash. English resolves without a request, and a table that fails to arrive renders anyway rather than leaving the page blank. - `Windows.tsx` fetches before committing a language switch, so the picker and the interface never disagree; a failed fetch keeps the previous language rather than storing one nothing can render. - `i18n/translations.ts` keeps the merged record for tests and scripts and is now off the runtime graph. `__tests__/i18n/eager-tables.test.ts` fails if any module the app loads imports it again, if a second locale joins the eager set, or if a table on disk has no loader. - `SettingsModal` derives its pills from a `Record<Locale, string>` of endonyms, so a table that exists without a pill is a compile error rather than a language a reader can be in but not pick. Measured on the production build (entry script + every `rel=modulepreload` + stylesheet, gzip -9): 3,368,078 raw / 1,013,088 gzip → 3,002,313 raw / 910,672 gzip — 102,416 gzip, 10.1%, off the first paint — with no locale chunk preloaded. Verified in the built app: it boots in the saved language with no English beneath it, and switching to Japanese then French through the real picker replaces the tables rather than layering on English (each switch shows exactly that locale's strings, console clean, no chunk errors). Tests: 8763 passed, 0 failures. `vitest.setup.ts` installs the merged record before any test runs, because a test that renders in French should not have to fetch French — it would otherwise read English silently. Not covered here: `zh` is still on the eager bundle, because `legal/providers-list.ts` resolves its disclosure strings through the whole table (`strings[key]`) and the bundler cannot narrow a dynamic key. Splitting that means giving those two lists a narrow per-locale record of their own; left out to keep this change to the loading mechanism. Signed-off-by: 天不语 <2364309541@qq.com>
Owner
|
再次感谢!减少启动下载量是非常值得考虑的方向,不过目前我认为实现上还有几处需要调整:快速切换语言时,较早发起的请求可能覆盖最后一次选择;启动时语言资源一直未返回会阻塞界面;加载失败时也需要明确的提示和重试方式。 另外,基础界面译文需要保留高于帮助扩展文案的优先级,建议分别管理这两类资源。测试也需要覆盖语言尚未加载的情况,包括请求乱序、失败和重试。 性能方面,希望补充包含当前语言资源在内的冷启动、缓存启动和首次切换语言耗时。现有体积数据说明静态入口变小了,但还需要确认实际使用是否更快。 最后是code hygiene问题,我觉得可以进一步避免使用防御性语言以及在代码中过度解释或解释过时的行为,以确保代码可读性。确认补齐这些后,我们会再进行评估。辛苦辛苦~ |
Author
|
OK👌🏻 |
Answer the review of the lazy-locale change. - The two late-arriving resource classes are separate now. A locale's INTERFACE table goes through `registerBaseStrings`; a window's prose (the Help Center) stays on `registerExtraStrings`. Base outranks extra in the lookup, so a help key cannot re-word the interface it is displayed in. - Boot no longer waits indefinitely. It waits for the saved language's table up to a deadline, then paints regardless, and `I18nProvider` subscribes to arrivals so the table reaches what already rendered. Before this, a fetch that never settled left the page blank. - The switch is a hook (`ui/shell/windows/use-locale-switch.ts`) with a ticket, so the LATEST choice wins: an earlier fetch landing late cannot take the interface back to a language already left. A failure keeps the current language, reports it through the toast, and leaves the chunk unremembered, so the next press is the retry. `modal.settings_language_failed` is new in all seven tables. - Tests: an arrival reaches a rendered tree, interface keys outrank prose, a failed chunk is fetched again by the next call, a chunk is fetched once for concurrent callers, and the switch's ordering and failure rules. Signed-off-by: 天不语 <2364309541@qq.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
语种表按需加载:首屏 gzip 减少 102 KB(-10.1%)
感谢 review。七点逐条回应如下。
1. 快速切换语言时的竞态 → 已修(后发胜出)
切换逻辑抽成
ui/shell/windows/use-locale-switch.ts:每次切换领一个票号,只有最新那一次可以落库,也只有它有权报告失败。先发起、后返回的请求不会把界面拉回已经离开的语言。测试:
use-locale-switch.test.ts用可控 promise 让早发的请求晚于后发者返回,断言界面仍停在后选的语言。2. 启动时语言资源未返回会阻塞界面 → 已修(有界等待)
原来
await到表到手才渲染首帧。现在只等一个上限(150ms),到点即渲染;表到达后由I18nProvider订阅到并重渲染。取舍是明确的:错语言的帧是闪烁,永不出现的页面更糟。为此i18n/context.tsx增加了到达订阅(subscribeStrings),使"表到达"能触达已经渲染的组件。测试:
locale-table-loading.test.tsx断言表到达后,先前用回退渲染的树会更新。3. 加载失败要有明确提示与重试 → 已加
失败时保持当前语言,并通过 toast 说明(新增
modal.settings_language_failed,7 个语种表均已补齐)。失败的 chunk 不进 memo,所以下次点选即重试。测试:失败后不落库、且提示被调用一次;被更晚选择取代的失败不会误报。
4. 基础界面译文与帮助扩展文案分开管理 → 已分两层
i18n/context.tsx现在是两个存储:baseTables(语种自己的界面表)与extraTables(窗口自带的散文,如帮助中心)。查找顺序为base → extra → en—— 界面文案优先于帮助文案,且两者只能新增键,不能改写更上层。测试:两层注册同一个键时,界面文案胜出。
5. 测试覆盖 → 已补
__tests__/i18n/locale-table-loading.test.tsx__tests__/ui/shell/use-locale-switch.test.ts6. 性能:冷启动 / 缓存启动 / 首次切换(均含当前语言资源)
生产构建 + 真实浏览器(CDP 驱动,脚本
.audit/locale-timing.py)实测:两点说明:① 冷启动一列的传输量含应用自身的启动集(模型等预取,约 19.9 MB / 128 请求),不是语种表的体积,语种表的到达时刻如上;②
performance.now()自导航起点计时、轮询间隔 50ms,界面出现时间可读作"±50ms 内"。7. Code hygiene → 已改
去掉防御性叙述与解释过时行为的注释;
locales/index.ts、context.tsx、main.tsx、vitest.setup.ts的注释缩短为只讲当前行为。验证
npm run lint(即tsc --noEmit):0 错误object-remove-cost、compression-compat、operation-purity、root-docs)是本 PR 基于main的既有失败,由 Windows 可移植性那个 PR 修复;ui/agent/panel-dock单独运行 34/34 通过,仅在整批并行时出现一次布局宽度断言失败(同次运行还有一次 worker fork 异常),属加载敏感,不是本改动引入。仍未覆盖
zh仍在首屏:legal/providers-list.ts用strings[key]动态取键,打包器无法收窄,整张 zh 表因此被拉进入口图(约 20 KB gzip)。拆它需要给那两张清单一份自己的窄表,属于另一件事,未纳入本次。