Skip to content

fix(i18n): complete Chinese language pack and eliminate hardcoded English in platform UI#1131

Merged
hotlong merged 5 commits intomainfrom
copilot/fix-chinese-language-pack-issues
Mar 26, 2026
Merged

fix(i18n): complete Chinese language pack and eliminate hardcoded English in platform UI#1131
hotlong merged 5 commits intomainfrom
copilot/fix-chinese-language-pack-issues

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 26, 2026

zh.ts had untranslated keys (e.g. toolbarEnabledCount still English), and platform UI components had hardcoded English strings for dialog titles, select placeholders, and button text — breaking the Chinese UI experience.

Locale packs (all 10 locales)

  • Fix toolbarEnabledCount — was '{{count}} of {{total}} enabled' in all 9 non-English locales
  • Add 7 new keys: common.selectOption, common.select, form.createTitle, form.editTitle, form.createDescription, form.editDescription, form.saveRecord

Replace hardcoded English in components

  • App.tsx — modal dialog title/description/submit/cancel now use t() with interpolation:
    title: t('form.createTitle', { object: currentObjectDef?.label })
    // zh: "新建联系人"  en: "Create Contact"
  • SelectField, LookupField, FieldFactory, form renderer"Select an option" / "Select..." / "Search..." replaced with t('common.selectOption') etc.

Shared safe translation infrastructure

  • createSafeTranslation(defaults, testKey) factory added to @object-ui/i18n — creates hooks that fall back to English defaults when no I18nProvider wraps the component (tests, Storybook, standalone usage). Follows existing createSafeTranslationHook pattern from plugin-detail.
  • useFieldTranslation in @object-ui/fields built on top of it.

Tests

  • 3 new tests covering new keys in en/zh (interpolation, toolbarEnabledCount fix)
  • Existing suites green: 75 i18n, 541 react, 427 fields (1 pre-existing scrollIntoView failure in happy-dom)
Original prompt

This section details on the original issue you should resolve

<issue_title>【i18n】完善中文语言包(zh.ts)遗漏与平台/元数据国际化分层问题</issue_title>
<issue_description>### 背景
ObjectUI 已具备完善的 i18n 架构,包含十种语言的 key-value 资源、useObjectLabel/fieldLabel 自动解析、动态 API 词条加载等。但根据实际使用发现,中文界面下平台层还有硬编码英文(如弹窗标题、按钮、占位符等),且zh.ts 语言包有少量缺失、部分 key 未翻译。


发现的问题

1. 平台 UI 层漏翻/硬编码(需纳入 zh.ts)

  • 弹窗标题、描述、submit/cancel按钮,仍在 App.tsx 等文件用英文字符串模板直接拼接(如"Create Contact"、"Save Record"、"Add a new ... to your database."、"Cancel"等)。
  • 部分组件(如SelectField、LookupField)默认 placeholder 写死英文"Select an option"、"Select..."等。
  • 表单/表格等部分按钮或说明文字 fallback 英文。

2. zh.ts 文件具体问题

  • 个别常用 key "toolbarEnabledCount" ({{count}} of {{total}} enabled) 未翻译,存在英文。
  • 未包含 select 组件常用占位/option提示,如"Select an option"、"Select..."等。
  • 元数据 placeholder/option label 如 email@example.com, (555) 123-4567 来源业务 schema,目前尚不支持 schema 多语言,建议后续升级元数据格式。

优化建议

A. 平台 UI 文案100%国际化

  • 全量梳理 platform UI 层硬编码英文,全部纳入 i18n key,通过 t()useObjectLabel 自动解析。
  • 提供 select/lookup/表单/弹窗等底层组件默认文案的多语言 key。
  • 新增 common.selectOption: '请选择'common.select: '选择...' 等。

B. 修正/补充 zh.ts 翻译

  • 补齐所有 fallback、常用 key、查漏补缺。
  • 技术方案建议所有 fallback default 字符串都走 i18n,不出现英文 fallback。

C. 长远元数据国际化改进

  • 元数据层(业务字段/placeholder)建议添加多语言属性(如 placeholder.zh、label.en 等),schema 设计时支持多语言。
  • 若用低代码 schema、自动生成翻译包。

任务拆解(建议分阶段)

  1. 梳理、替换全部平台英文硬编码,全部走 i18n(平台层100%覆盖)
  2. zh.ts 文件补齐所有 key & 修正所有 fallback
  3. Schema 设计升级任务(元数据多语言支持,方案设计可拆子 issue 跟踪)
  4. CI 国际化覆盖率检测脚本

如需子任务可继续分解��

参考场景见截图和代码实际引用逻辑。</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui-demo Ready Ready Preview, Comment Mar 26, 2026 7:08am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Mar 26, 2026 7:08am
objectui-storybook Ignored Ignored Mar 26, 2026 7:08am

Request Review

Copilot AI and others added 2 commits March 26, 2026 06:42
…Count for 8 locales

Add common.selectOption and common.select keys to all 8 locale files.
Add form.createTitle, editTitle, createDescription, editDescription, and
saveRecord keys to all 8 locale files.
Replace untranslated toolbarEnabledCount with proper localized translations.

Locales updated: ja, ko, de, fr, es, pt, ru, ar

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… to all 10 locales

- Fix untranslated toolbarEnabledCount in all 9 non-English locales
- Add common.selectOption and common.select keys to all 10 locales
- Add form.createTitle, form.editTitle, form.createDescription, form.editDescription, form.saveRecord to all 10 locales

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/1e02cef1-b595-4316-86d4-24d7df245f95
- App.tsx: Use t() for modal dialog title, description, submit/cancel text
- SelectField: Use safe i18n hook for "Select an option" placeholder
- LookupField: Use safe i18n hook for "Select...", "Search..." placeholders
- FieldFactory: Use safe i18n hook for select option placeholder
- form.tsx renderer: Use safe i18n hook for select placeholder
- Add useFieldTranslation safe hook with English fallbacks
- Add @object-ui/i18n dependency to fields and components packages
- Add test coverage for new i18n keys (en/zh)

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/1e02cef1-b595-4316-86d4-24d7df245f95
…ui/i18n

- Move safe translation pattern from inline implementations to shared
  createSafeTranslation factory in @object-ui/i18n
- Refactor useFieldTranslation to use createSafeTranslation
- Refactor FieldFactory and form.tsx to use createSafeTranslation
- Restore fallback placeholder in renderFieldComponent for safety
- Update CHANGELOG.md with all i18n improvements

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/1e02cef1-b595-4316-86d4-24d7df245f95
Copilot AI changed the title [WIP] Fix missing translations and hardcoded strings in Chinese language pack fix(i18n): complete Chinese language pack and eliminate hardcoded English in platform UI Mar 26, 2026
Copilot AI requested a review from hotlong March 26, 2026 07:14
@hotlong hotlong marked this pull request as ready for review March 26, 2026 14:01
Copilot AI review requested due to automatic review settings March 26, 2026 14:01
@hotlong hotlong merged commit 38fde39 into main Mar 26, 2026
4 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves ObjectUI’s internationalization by completing locale packs and replacing several hardcoded English UI strings with i18n keys, while adding a reusable “safe translation” hook factory to support tests/Storybook usage without an I18nProvider.

Changes:

  • Added new shared i18n keys (select + form dialog strings) across locale packs and fixed toolbarEnabledCount translations.
  • Replaced hardcoded English placeholders/text in console modal form schema + several field/rendering components with t() calls.
  • Introduced createSafeTranslation() in @object-ui/i18n and a useFieldTranslation() helper in @object-ui/fields.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds workspace links for @object-ui/i18n where newly depended upon.
packages/react/src/components/form/FieldFactory.tsx Uses safe i18n fallback for the HTML <select> empty option label.
packages/i18n/src/useSafeTranslation.ts Adds createSafeTranslation() hook factory (provider-missing fallback behavior).
packages/i18n/src/index.ts Exports createSafeTranslation() from the i18n package public API.
packages/i18n/src/tests/i18n.test.ts Adds tests for new keys and confirms zh toolbarEnabledCount is translated.
packages/i18n/src/locales/en.ts Adds common.selectOption, common.select, and new form.* strings.
packages/i18n/src/locales/zh.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/ar.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/de.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/es.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/fr.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/ja.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/ko.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/pt.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/i18n/src/locales/ru.ts Adds select/form keys + fixes toolbarEnabledCount translation.
packages/fields/src/widgets/useFieldTranslation.ts Adds a shared safe field-widget translation hook with defaults.
packages/fields/src/widgets/SelectField.tsx Replaces hardcoded placeholder with i18n key.
packages/fields/src/widgets/LookupField.tsx Replaces some hardcoded placeholders/labels with i18n keys.
packages/fields/package.json Declares dependency on @object-ui/i18n for field widgets.
packages/components/src/renderers/form/form.tsx Injects i18n-based select placeholder when building field props.
packages/components/package.json Declares dependency on @object-ui/i18n for the form renderer.
apps/console/src/App.tsx Uses i18n keys for modal form title/description and submit/cancel button text.
CHANGELOG.md Documents the i18n fixes/additions and UI string replacements.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines 523 to 525
<SelectTrigger className="min-h-[44px] sm:min-h-0">
<SelectValue placeholder={placeholder ?? 'Select an option'} />
<SelectValue placeholder={placeholder || 'Select an option'} />
</SelectTrigger>
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still hardcodes an English fallback ('Select an option') and also uses || instead of ??, which will override an intentionally empty-string placeholder. Since the rest of the PR is moving placeholders to i18n, consider using t('common.selectOption') (via the safe hook already added) and prefer placeholder ?? ... to preserve empty placeholders.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +17
const FIELD_DEFAULTS: Record<string, string> = {
'common.selectOption': 'Select an option',
'common.select': 'Select...',
'common.search': 'Search',
'table.selected': '{{count}} selected',
};

export const useFieldTranslation = createSafeTranslation(
FIELD_DEFAULTS,
'common.selectOption',
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useFieldTranslation defines a fallback for table.selected, but the runtime i18n locale packs currently don’t provide table.selected for most languages. Since this hook is meant to avoid raw i18n keys showing up, ensure table.selected is added to all locales (or switch to an existing key that is already present across locales) so the in-app translation path works too.

Copilot uses AI. Check for mistakes.
Comment thread CHANGELOG.md
Comment on lines 10 to 36
### Fixed

- **Chinese language pack (zh.ts) untranslated key** (`@object-ui/i18n`): Fixed `console.objectView.toolbarEnabledCount` which was still in English (`'{{count}} of {{total}} enabled'`) — now properly translated to `'已启用 {{count}}/{{total}} 项'`. Also fixed the same untranslated key in all other 8 non-English locales (ja, ko, de, fr, es, pt, ru, ar).

- **Hardcoded English strings in platform UI** (`apps/console`, `@object-ui/fields`, `@object-ui/react`, `@object-ui/components`): Replaced hardcoded English strings with i18n `t()` calls:
- Console `App.tsx`: Modal dialog title (`"Create/Edit Contact"`), description (`"Add a new ... to your database."`), submitText (`"Save Record"`), and cancelText (`"Cancel"`) now use i18n keys.
- `SelectField`: Default placeholder `"Select an option"` now uses `t('common.selectOption')`.
- `LookupField`: Default placeholder `"Select..."` and search placeholder `"Search..."` now use i18n keys.
- `FieldFactory`: Default select option placeholder now uses `t('common.selectOption')`.
- Form renderer: Default select placeholder now uses `t('common.selectOption')`.

### Added

- **New i18n keys for select/form components** (`@object-ui/i18n`): Added new translation keys across all 10 locale packs:
- `common.selectOption` — Default select field placeholder (e.g., '请选择' in Chinese)
- `common.select` — Short select placeholder (e.g., '选择...' in Chinese)
- `form.createTitle` — Form dialog create title with interpolation (e.g., '新建{{object}}' in Chinese)
- `form.editTitle` — Form dialog edit title with interpolation
- `form.createDescription` — Form dialog create description with interpolation
- `form.editDescription` — Form dialog edit description with interpolation
- `form.saveRecord` — Save record button text

- **Safe translation hook for field widgets** (`@object-ui/fields`): Added `useFieldTranslation` hook that provides fallback to English defaults when no `I18nProvider` is available, following the same pattern as `useDetailTranslation` in `@object-ui/plugin-detail`.

### Fixed

- **ObjectView ChatterPanel test assertion mismatch** (`apps/console`): Fixed the failing CI test in `ObjectView.test.tsx` where the RecordChatterPanel drawer test used `getByLabelText('Show discussion')` but the actual aria-label rendered by the component is `'Show Discussion (0)'` (from the i18n default translation `detail.showDiscussion` with count parameter). Updated the test assertion to match the correct aria-label.
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under ## [Unreleased], this adds a new ### Added section between two existing ### Fixed sections, resulting in duplicate headings (Fixed appears multiple times). To keep the changelog easy to scan (Keep a Changelog format), consider merging these new bullets into the existing ### Fixed / ### Added sections instead of creating additional repeated headings.

Copilot uses AI. Check for mistakes.
? lookupField?.placeholder || 'Select...'
: multiple ? `${selectedOptions.length} selected` : 'Change selection'
? lookupField?.placeholder || t('common.select')
: multiple ? t('table.selected', { count: selectedOptions.length }) : t('common.select')
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t('table.selected', { count }) is now used for the multi-select label, but table.selected currently only exists in en/zh locale packs (most other locales don't define table.selected). With an I18nProvider present this will render the raw key (table.selected) for those locales. Add table.selected to all locale files (and ideally a small i18n test to assert key parity/availability).

Suggested change
: multiple ? t('table.selected', { count: selectedOptions.length }) : t('common.select')
: multiple ? `${selectedOptions.length} selected` : t('common.select')

Copilot uses AI. Check for mistakes.
? lookupField?.placeholder || 'Select...'
: multiple ? `${selectedOptions.length} selected` : 'Change selection'
? lookupField?.placeholder || t('common.select')
: multiple ? t('table.selected', { count: selectedOptions.length }) : t('common.select')
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the single-select case with an existing selection, the trigger text changed from "Change selection" to t('common.select') (same label as the empty/placeholder state). This is a user-visible behavior regression (it no longer indicates that a value is already selected). Consider introducing a dedicated i18n key (e.g. common.changeSelection) or showing the selected label here instead of reusing the placeholder text.

Suggested change
: multiple ? t('table.selected', { count: selectedOptions.length }) : t('common.select')
: multiple
? t('table.selected', { count: selectedOptions.length })
: (selectedOptions[0]?.[displayField] || selectedOptions[0]?.label || t('common.select'))

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【i18n】完善中文语言包(zh.ts)遗漏与平台/元数据国际化分层问题

3 participants