Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…/dashboard labels Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot crm只是一个元数据的例子,console是元数据驱动的平台,console只会通过api加载crn中的元数据包括翻译,中不应该有crm的代码。请确认spec中关于国际化的规范。 |
… approach Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…ec-aligned i18n Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
… logging Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
已按照spec规范重新实现。CRM元数据现在使用 关键变更(commit c92afa8):
Console中零CRM特定代码。 |
There was a problem hiding this comment.
Pull request overview
This PR implements spec-aligned internationalization for CRM metadata by migrating from plain string labels to I18nLabel objects ({ key, defaultValue, params? }) and establishing an API-based translation loading mechanism.
Changes:
- CRM metadata (app, dashboard, navigation) now uses I18nLabel objects for all user-facing text
- Navigation types updated to support I18nLabel union type alongside plain strings
- Translation utilities enhanced to accept optional
t()function for i18n resolution - API endpoint
/api/v1/i18n/:langadded to serve application translations dynamically - Console components (AppSidebar, DashboardView) updated to resolve I18nLabel objects for app/dashboard-level labels
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/types/src/app.ts | Updated NavigationItem and NavigationArea label types to support I18nLabel objects |
| packages/layout/src/NavigationRenderer.tsx | Added resolveLabel() helper and applied it to all label rendering points |
| packages/layout/src/AppSchemaRenderer.tsx | Uses resolveLabel() for area switcher and mobile navigation |
| examples/crm/src/apps/crm.app.ts | Converted all navigation labels to I18nLabel objects with crm.* translation keys |
| examples/crm/src/dashboards/crm.dashboard.ts | Converted dashboard label and all widget titles to I18nLabel objects |
| examples/crm/src/tests/crm-metadata.test.ts | Added resolveTitle() helper for test assertions on I18nLabel fields |
| apps/console/src/utils.ts | Enhanced resolveI18nLabel() to accept optional t() function for i18n resolution |
| apps/console/src/main.tsx | Added loadLanguage callback that fetches translations from API endpoint |
| apps/console/src/mocks/browser.ts | Added /api/v1/i18n/:lang handler that loads CRM locales |
| apps/console/src/mocks/server.ts | Added /api/v1/i18n/:lang handler for Node test environment |
| apps/console/src/components/DashboardView.tsx | Added useObjectTranslation() hook and resolves dashboard-level labels |
| apps/console/src/components/AppSidebar.tsx | Passes t() to resolveI18nLabel for app label/description resolution |
| apps/console/src/tests/AppSidebar.test.tsx | Updated mock for resolveI18nLabel and added useObjectTranslation mock |
| apps/console/src/tests/app-creation-integration.test.tsx | Updated resolveI18nLabel mock to handle I18nLabel objects |
| ROADMAP.md | Documented P1 i18n implementation completion |
| name: 'crm_dashboard', | ||
| label: 'CRM Overview', | ||
| label: { key: 'crm.dashboard.title', defaultValue: 'CRM Overview' }, | ||
| description: 'Revenue metrics, pipeline analytics, and deal insights', |
There was a problem hiding this comment.
The dashboard description field is a plain string, while the label field uses an I18nLabel object. For consistency and complete i18n coverage, the description should also use an I18nLabel object like { key: 'crm.dashboard.description', defaultValue: 'Revenue metrics, pipeline analytics, and deal insights' }. This ensures all user-facing text in the dashboard metadata can be translated.
| description: 'Revenue metrics, pipeline analytics, and deal insights', | |
| description: { key: 'crm.dashboard.description', defaultValue: 'Revenue metrics, pipeline analytics, and deal insights' }, |
| style={primaryColor ? { backgroundColor: primaryColor } : undefined} | ||
| > | ||
| {logo ? ( | ||
| <img src={logo} alt={resolveI18nLabel(activeApp.label)} className="size-6 object-contain" /> | ||
| <img src={logo} alt={resolveI18nLabel(activeApp.label, t)} className="size-6 object-contain" /> | ||
| ) : ( | ||
| React.createElement(getIcon(activeApp.icon), { className: "size-4" }) | ||
| )} | ||
| </div> | ||
| <div className="grid flex-1 text-left text-sm leading-tight"> | ||
| <span className="truncate font-semibold">{resolveI18nLabel(activeApp.label)}</span> | ||
| <span className="truncate font-semibold">{resolveI18nLabel(activeApp.label, t)}</span> | ||
| <span className="truncate text-xs"> | ||
| {resolveI18nLabel(activeApp.description) || `${activeApps.length} Apps Available`} | ||
| {resolveI18nLabel(activeApp.description, t) || `${activeApps.length} Apps Available`} |
There was a problem hiding this comment.
The mobile bottom navigation renders item.label directly (line 533) without resolving I18nLabel objects. Navigation items from CRM use I18nLabel objects (e.g., { key: 'crm.navigation.dashboard', defaultValue: 'Dashboard' }), which will display as [object Object] in the mobile UI. This should use resolveI18nLabel(item.label, t) like the rest of AppSidebar does (see lines 272, 278, 280).
| const { t } = useObjectTranslation(); | ||
| const [isLoading, setIsLoading] = useState(true); | ||
| const [configPanelOpen, setConfigPanelOpen] = useState(false); | ||
| const [selectedWidgetId, setSelectedWidgetId] = useState<string | null>(null); |
There was a problem hiding this comment.
Widget titles in the CRM dashboard use I18nLabel objects, but the flattenWidgetConfig function treats widget.title as a plain string (line 82). When a widget title is an I18nLabel object like { key: 'crm.dashboard.widgets.totalRevenue', defaultValue: 'Total Revenue' }, it should be resolved using resolveI18nLabel(widget.title, t) before being flattened. This ensures the config panel displays the translated title and the saved config preserves the I18nLabel structure when needed.
| {/* ── Header ───────────────────────────────────────────────── */} | ||
| <div className="flex flex-col sm:flex-row justify-between sm:items-center gap-3 sm:gap-4 p-4 sm:p-6 border-b shrink-0"> | ||
| <div className="min-w-0 flex-1"> | ||
| <h1 className="text-lg sm:text-xl md:text-2xl font-bold tracking-tight truncate">{resolveI18nLabel(dashboard.label) || dashboard.name}</h1> | ||
| <h1 className="text-lg sm:text-xl md:text-2xl font-bold tracking-tight truncate">{resolveI18nLabel(dashboard.label, t) || dashboard.name}</h1> | ||
| {dashboard.description && ( | ||
| <p className="text-sm text-muted-foreground mt-1 line-clamp-2">{resolveI18nLabel(dashboard.description)}</p> | ||
| <p className="text-sm text-muted-foreground mt-1 line-clamp-2">{resolveI18nLabel(dashboard.description, t)}</p> | ||
| )} | ||
| </div> | ||
| <div className="shrink-0 flex items-center gap-1.5"> |
There was a problem hiding this comment.
Dashboard label and description are correctly resolved with resolveI18nLabel(dashboard.label, t), but widget titles also use I18nLabel objects in the CRM dashboard metadata and need similar resolution. The current implementation passes widget schemas directly to DashboardRenderer, which will render I18nLabel objects as [object Object] strings. Consider preprocessing the schema to resolve all I18nLabel fields in widgets before passing to DashboardRenderer, similar to how NavigationRenderer handles labels.
CRM metadata used plain string labels instead of spec-aligned
I18nLabelobjects. The console had no mechanism to translate them, and language switching had no effect on CRM labels.Per feedback, the console is a metadata-driven platform and must not import CRM code directly. Instead, CRM metadata now uses spec-aligned
I18nLabelobjects ({ key, defaultValue }) and translations are loaded via API.Changes
crm.app.ts,crm.dashboard.ts) — All navigation labels, app name/description, and dashboard widget titles now useI18nLabelobjects (e.g.{ key: 'crm.navigation.dashboard', defaultValue: 'Dashboard' }) instead of plain stringsNavigationItem/NavigationAreatypes (app.ts) —labelfield updated to supportstring | { key, defaultValue, params? }(I18nLabel union), aligning with@objectstack/specNavigationRenderer.tsx— Added exportedresolveLabel()helper that resolves I18nLabel objects to plain strings (defaultValue fallback)AppSchemaRenderer.tsx— UsesresolveLabel()for area and mobile navigation labelsutils.ts(console) —resolveI18nLabel(label, t?)now accepts an optionalt()function for i18n translation lookupAppSidebar.tsx/DashboardView.tsx— Passt()fromuseObjectTranslationtoresolveI18nLabelfor runtime translationmain.tsx— AddedloadLanguagecallback that fetches translations from/api/v1/i18n/:langvia the I18nProvider's lazy-loading APIbrowser.ts,server.ts) — Added/api/v1/i18n/:langendpoint that serves application locale bundles from installed example packagesOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.