fix(i18n): fix empty translations in MSW, server mode, and root dev mode#1110
fix(i18n): fix empty translations in MSW, server mode, and root dev mode#1110
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
…lations
The createKernel i18n service's getTranslations was returning
{ locale, translations } instead of the flat Record<string, any>
expected by the spec II18nService interface. The HttpDispatcher
wraps the return in { data: { locale, translations } }, so the
extra layer caused /api/v1/i18n/translations/:lang to return
empty translations. Now returns resolveI18nTranslations() directly.
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/a9fb6c1a-aac9-485a-afb5-952077e8f771
|
@copilot 问题不是这个啊,问题是 "translations" 为空。 |
…sion tests
The createKernel i18n service's getTranslations returned
{ locale, translations } instead of the flat Record<string, any>
expected by the spec II18nService interface. The HttpDispatcher
wraps the return in { data: { locale, translations } }, so the
extra layer caused /api/v1/i18n/translations/:lang to return
empty or double-nested translations.
Now returns resolveI18nTranslations() directly. Added 6 regression
tests covering: kernel service, HttpDispatcher, and MSW handler.
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/9e014d2c-cb4d-4779-8774-216d48126f73
I've added 6 end-to-end regression tests (commit The test
All 905 existing tests pass. |
|
目前 pnpm dev , msw 模式则是正常的。 |
…i18n service The real ObjectStack server (pnpm start) uses AppPlugin.loadTranslations() which reads from a top-level `translations` array, not `i18n.bundles`. Changes: - objectstack.shared.ts: add `translations` array in spec format (each entry maps locale → namespace-scoped data) so AppPlugin can load them into the kernel's in-memory i18n fallback - createKernel.ts: expand i18n service with loadTranslations(), getLocales(), getDefaultLocale(), setDefaultLocale() methods required by AppPlugin and HttpDispatcher - i18n-translations.test.ts: add 3 server-mode compatibility tests Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/5581f722-7675-404d-9a1b-a025dca6484a
Found the server-mode root cause and fixed in Fix:
All 745 console + 61 CRM tests pass. The i18n endpoint should now return populated translations in both MSW mode and server mode ( |
|
@copilot 还是不对呀,我觉得不是格式的问题,我感觉翻译文件完全没加载,评估到底是内核哪里的问题? |
Root cause: In server mode (`pnpm start`), the kernel bootstrap sequence
runs plugin starts BEFORE validateSystemRequirements(). The memory i18n
fallback service is auto-registered in validateSystemRequirements() —
AFTER AppPlugin.start() → loadTranslations() has already tried (and
failed) to find the i18n service, silently skipping translation loading.
Fix: Add a MemoryI18nPlugin to objectstack.config.ts that registers the
in-memory i18n service during the init phase (Phase 1). Since ALL inits
complete before ANY starts, the service is available when AppPlugin.start()
runs in Phase 2. The plugin's name ('com.objectstack.service.i18n') matches
the CLI's dedup check, preventing conflicts.
Also added a server-mode simulation test that validates the full flow:
createMemoryI18n → loadTranslations (AppPlugin-style) → getTranslations.
All 746 console tests pass.
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/8eb7854e-6a7b-4b91-bcca-6d28bff373a3
你说得对,翻译确实完全没有被加载。根因是 kernel bootstrap 的时序问题: Bootstrap 顺序:
修复 ( |
|
还是不对,你可以改完了自己运行服务自己测试一下 |
|
…lations
Root cause: The CLI's isHostConfig() detects plugins with init methods and
skips auto-registration of AppPlugin. Without AppPlugin, translations were
never loaded via loadTranslations(). Fix: explicitly include AppPlugin(sharedConfig)
in the plugins array after MemoryI18nPlugin.
Verified by running the actual server (pnpm start) and testing:
- GET /api/v1/i18n/translations/zh → {"crm":{"objects":{"account":{"label":"客户"}},...}}
- GET /api/v1/i18n/translations/en → {"crm":{"objects":{"account":{"label":"Account"}},...}}
- GET /api/v1/i18n/locales → all 10 CRM locales
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/555f26bf-5911-46a3-b3e4-c717756ddedc
实际运行服务并测试后发现了真正的根因:CLI 的 修复 ( 已实际启动服务器验证:
|
|
@copilot 你是怎么测试的呢?为什么我测试下来还是空? pnpm i , pnpm build, pnpm dev |
Root cause: `pnpm dev` from the monorepo root runs `objectstack serve --dev`
which loads the ROOT objectstack.config.ts — not apps/console/objectstack.config.ts.
The root config collects plugin configs via getConfig() and composes them with
composeStacks(), but composeStacks() doesn't handle the custom `i18n` field.
So translation data from CRM (and other stacks) was lost during composition,
and AppPlugin.loadTranslations() found nothing to load.
Fix:
1. Aggregate i18n bundles from all plugin configs (same pattern as objectstack.shared.ts)
2. Build spec-format translations array passed to AppPlugin(mergedApp)
3. Add MemoryI18nPlugin to register i18n service during init phase
Verified by actually running the server and testing:
- GET /api/v1/i18n/translations/zh → {"crm":{"objects":{"account":{"label":"客户"}},...}}
- GET /api/v1/i18n/translations/en → {"crm":{"objects":{"account":{"label":"Account"}},...}}
- GET /api/v1/i18n/locales → all 10 CRM locales
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/8b23b829-56d5-4f03-b8f4-fe066b662587
找到问题了。 根目录配置通过 修复 (
这次是实际启动服务器测试验证的: |
/api/v1/i18n/translations/:langreturnstranslations: {}in MSW mode, console server mode (pnpm start), and root dev mode (pnpm dev). Five independent root causes were identified and fixed.Root Cause 1 — MSW Mode (double-nesting)
The kernel i18n service in
createKernel.tswrapped its return value in{ locale, translations }, but the specHttpDispatcher.handleI18napplies that same wrapping — resulting in double-nesting that downstreamloadLanguage.tscan't extract.Root Cause 2 — Server Mode (missing translations array)
The runtime's
AppPlugin.loadTranslations()reads from a top-leveltranslationsarray in the config, butobjectstack.shared.tsonly provided translations in the customi18n.bundlesformat used bycreateKernel.ts. Added a spec-formattranslationsarray soAppPlugincan find and load the bundles.Root Cause 3 — Server Mode (i18n service timing)
The kernel bootstrap sequence runs plugin starts before
validateSystemRequirements(). The memory i18n fallback service is auto-registered invalidateSystemRequirements()— afterAppPlugin.start()→loadTranslations()has already run and found no i18n service. Translation files were never loaded because the i18n service didn't exist whenAppPlugintried to use it.Root Cause 4 — Server Mode (AppPlugin never registered)
The CLI's
isHostConfig()function detects thatconfig.pluginscontains objects withinitmethods (ObjectQLPlugin, DriverPlugin, HonoServerPlugin, ConsolePlugin) and treats the config as a "host config" — skipping auto-registration ofAppPlugin. WithoutAppPlugin,loadTranslations()never runs, so translation data is never loaded into the i18n service. This was the primary reason translations were completely empty in server mode.Root Cause 5 — Root Dev Mode (
pnpm devfrom monorepo root)Running
pnpm devfrom the monorepo root executesobjectstack dev→objectstack serve --dev, which loads the rootobjectstack.config.ts— notapps/console/objectstack.config.ts. The root config collects plugin configs viagetConfig()and composes them withcomposeStacks(), butcomposeStacks()does not handle the customi18nfield. CRM translation data was silently dropped during composition, soAppPlugin.loadTranslations()found nothing to load. Additionally, the root config had noMemoryI18nPluginto register the i18n service during init phase.Changes
objectstack.config.ts(root):objectstack.shared.tstranslationsarray and included it inmergedApppassed toAppPluginMemoryI18nPluginto register the in-memory i18n service during init phase, beforeAppPlugin.start()runsapps/console/objectstack.config.ts:MemoryI18nPluginthat registers the in-memory i18n service during the init phase (Phase 1), beforeAppPlugin.start()runs in Phase 2. UsescreateMemoryI18n()from@objectstack/core. Plugin namecom.objectstack.service.i18nmatches the CLI's dedup check, preventing conflicts with@objectstack/service-i18nif installed later.AppPlugin(sharedConfig)to the plugins array. SinceisHostConfig()returnstruefor this config, the CLI never auto-registersAppPlugin— so it must be included manually to ensure translations, objects, and seed data are loaded.apps/console/src/mocks/createKernel.ts:getTranslationsnow returns the flatRecord<string, any>the specII18nServicecontract expects (fixes MSW mode)II18nServiceinterface:loadTranslations(),getLocales(),getDefaultLocale(),setDefaultLocale()— methods required by bothAppPluginandHttpDispatcherapps/console/objectstack.shared.ts:translationsarray in the spec format (Array<{ [locale]: { namespace: data } }>) so thatAppPlugin.loadTranslations()can load translations into the kernel's i18n service in server modedefaultLocale: 'en'to thei18nconfigapps/console/src/__tests__/i18n-translations.test.ts: Added 10 regression tests covering:getTranslations('zh')returns flat namespace-scoped dict without wrapper{ success, data: { locale, translations } }envelopeGET /api/v1/i18n/translations/zhreturns populated CRM translationsII18nServiceinterface,getLocales()returns all CRM locales,appConfig.translationsis correctly formatted forAppPlugincreateMemoryI18n→loadTranslations(AppPlugin-style) →getTranslationsflow end-to-endVerification
Fix verified by actually running the server in both modes and testing the endpoint:
Root dev mode (
objectstack servewith root config):GET /api/v1/i18n/translations/zh→{"success":true,"data":{"locale":"zh","translations":{"crm":{"objects":{"account":{"label":"客户"},...}}}}}✅GET /api/v1/i18n/translations/en→{"success":true,"data":{"locale":"en","translations":{"crm":{"objects":{"account":{"label":"Account"},...}}}}}✅GET /api/v1/i18n/locales→ all 10 CRM locales returned ✅Console server mode (
pnpm startfrom apps/console):GET /api/v1/i18n/translations/zh→ populated CRM translations ✅GET /api/v1/i18n/translations/en→ populated CRM translations ✅All 746 console tests pass ✅
Original prompt
/api/v1/i18n/translations/:lang返回 translations 为空,根因与 createKernel i18n Service 注册实现有关 #1109📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.