Skip to content

Commit c56ca4e

Browse files
Copilothotlong
andcommitted
feat: add i18n support to detail view, section, and related list components
- Add detail.* translation keys to en.ts and zh.ts locale files - Add useDetailTranslation() hook following existing grid/list patterns - Replace all hardcoded strings in DetailView with t() calls - Replace all hardcoded strings in RelatedList with t() calls - Add useSectionTranslation() for DetailSection copy tooltips - Improve empty value display: replace plain '-' with styled em-dash - Add 'detail' to BUILTIN_KEYS in useObjectLabel.ts - Add onNew/onViewAll props and buttons to RelatedList - Update tests to expect new empty value styling Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent fdbab7d commit c56ca4e

7 files changed

Lines changed: 275 additions & 38 deletions

File tree

packages/i18n/src/locales/en.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,41 @@ const en = {
146146
deleteCard: 'Delete card',
147147
deleteColumn: 'Delete column',
148148
},
149+
detail: {
150+
back: 'Back',
151+
edit: 'Edit',
152+
editInline: 'Edit inline',
153+
save: 'Save',
154+
saveChanges: 'Save changes',
155+
editFieldsInline: 'Edit fields inline',
156+
share: 'Share',
157+
duplicate: 'Duplicate',
158+
export: 'Export',
159+
viewHistory: 'View history',
160+
delete: 'Delete',
161+
moreActions: 'More actions',
162+
addToFavorites: 'Add to favorites',
163+
removeFromFavorites: 'Remove from favorites',
164+
previousRecord: 'Previous record',
165+
nextRecord: 'Next record',
166+
recordOf: '{{current}} of {{total}}',
167+
recordNotFound: 'Record not found',
168+
recordNotFoundDescription: 'The record you are looking for does not exist or may have been deleted.',
169+
goBack: 'Go back',
170+
details: 'Details',
171+
related: 'Related',
172+
relatedRecords: '{{count}} records',
173+
relatedRecordOne: '{{count}} record',
174+
noRelatedRecords: 'No related records found',
175+
loading: 'Loading...',
176+
copyToClipboard: 'Copy to clipboard',
177+
copied: 'Copied!',
178+
deleteConfirmation: 'Are you sure you want to delete this record?',
179+
editRecord: 'Edit record',
180+
viewAll: 'View All',
181+
new: 'New',
182+
emptyValue: '—',
183+
},
149184
chart: {
150185
noData: 'No chart data available',
151186
loading: 'Loading chart...',

packages/i18n/src/locales/zh.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,41 @@ const zh = {
146146
deleteCard: '删除卡片',
147147
deleteColumn: '删除列',
148148
},
149+
detail: {
150+
back: '返回',
151+
edit: '编辑',
152+
editInline: '内联编辑',
153+
save: '保存',
154+
saveChanges: '保存更改',
155+
editFieldsInline: '内联编辑字段',
156+
share: '分享',
157+
duplicate: '复制',
158+
export: '导出',
159+
viewHistory: '查看历史',
160+
delete: '删除',
161+
moreActions: '更多操作',
162+
addToFavorites: '添加到收藏',
163+
removeFromFavorites: '从收藏中移除',
164+
previousRecord: '上一条记录',
165+
nextRecord: '下一条记录',
166+
recordOf: '第 {{current}} 条,共 {{total}} 条',
167+
recordNotFound: '未找到记录',
168+
recordNotFoundDescription: '您查找的记录不存在或已被删除。',
169+
goBack: '返回',
170+
details: '详情',
171+
related: '相关',
172+
relatedRecords: '{{count}} 条记录',
173+
relatedRecordOne: '{{count}} 条记录',
174+
noRelatedRecords: '暂无相关记录',
175+
loading: '加载中...',
176+
copyToClipboard: '复制到剪贴板',
177+
copied: '已复制!',
178+
deleteConfirmation: '确定要删除此记录吗?',
179+
editRecord: '编辑记录',
180+
viewAll: '查看全部',
181+
new: '新建',
182+
emptyValue: '—',
183+
},
149184
chart: {
150185
noData: '暂无图表数据',
151186
loading: '图表加载中...',

packages/i18n/src/useObjectLabel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { useObjectTranslation } from './provider';
2828
const BUILTIN_KEYS = new Set([
2929
'common', 'validation', 'form', 'table', 'grid', 'calendar',
3030
'list', 'kanban', 'chart', 'dashboard', 'configPanel',
31-
'appDesigner', 'console', 'errors',
31+
'appDesigner', 'console', 'errors', 'detail',
3232
]);
3333

3434
/**

packages/plugin-detail/src/DetailSection.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,29 @@ import {
2424
TooltipTrigger,
2525
} from '@object-ui/components';
2626
import { ChevronDown, ChevronRight, Copy, Check } from 'lucide-react';
27-
import { SchemaRenderer } from '@object-ui/react';
27+
import { SchemaRenderer, useObjectTranslation } from '@object-ui/react';
2828
import { getCellRenderer } from '@object-ui/fields';
2929
import type { DetailViewSection as DetailViewSectionType, DetailViewField, FieldMetadata } from '@object-ui/types';
3030
import { applyDetailAutoLayout } from './autoLayout';
3131

32+
const SECTION_TRANSLATIONS: Record<string, string> = {
33+
'detail.copyToClipboard': 'Copy to clipboard',
34+
'detail.copied': 'Copied!',
35+
};
36+
37+
function useSectionTranslation() {
38+
try {
39+
const result = useObjectTranslation();
40+
const testValue = result.t('detail.copyToClipboard');
41+
if (testValue === 'detail.copyToClipboard') {
42+
return { t: (key: string) => SECTION_TRANSLATIONS[key] || key };
43+
}
44+
return { t: result.t };
45+
} catch {
46+
return { t: (key: string) => SECTION_TRANSLATIONS[key] || key };
47+
}
48+
}
49+
3250
export interface DetailSectionProps {
3351
section: DetailViewSectionType;
3452
data?: any;
@@ -51,6 +69,7 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
5169
}) => {
5270
const [isCollapsed, setIsCollapsed] = React.useState(section.defaultCollapsed ?? false);
5371
const [copiedField, setCopiedField] = React.useState<string | null>(null);
72+
const { t } = useSectionTranslation();
5473

5574
const handleCopyField = React.useCallback((fieldName: string, value: any) => {
5675
const textValue = value !== null && value !== undefined ? String(value) : '';
@@ -77,7 +96,7 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
7796
field.span === 6 ? 'col-span-6' : '';
7897

7998
const displayValue = (() => {
80-
if (value === null || value === undefined) return '-';
99+
if (value === null || value === undefined) return <span className="text-muted-foreground/50 text-xs italic"></span>;
81100
// Enrich field with objectSchema metadata — merge missing properties
82101
// even when field.type is explicitly set (e.g., type: 'lookup' without reference_to)
83102
const objectDefField = objectSchema?.fields?.[field.name];
@@ -159,7 +178,7 @@ export const DetailSection: React.FC<DetailSectionProps> = ({
159178
</Button>
160179
</TooltipTrigger>
161180
<TooltipContent>
162-
{isCopied ? 'Copied!' : 'Copy to clipboard'}
181+
{isCopied ? t('detail.copied') : t('detail.copyToClipboard')}
163182
</TooltipContent>
164183
</Tooltip>
165184
</TooltipProvider>

0 commit comments

Comments
 (0)