Skip to content

Commit 409987e

Browse files
authored
Merge pull request #969 from objectstack-ai/copilot/add-internationalization-support
2 parents e8a1023 + ef4453f commit 409987e

File tree

20 files changed

+662
-35
lines changed

20 files changed

+662
-35
lines changed

ROADMAP.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,41 @@ All 313 `@object-ui/fields` tests pass.
14361436

14371437
---
14381438

1439+
## 📦 Detail Page & Related List i18n (P1.15)
1440+
1441+
> **Goal:** Salesforce-style detail page enhancements: i18n for all detail page UI elements, improved empty value display, related list actions, and auto-discovery of related lists.
1442+
1443+
**i18n Integration:**
1444+
- [x] Add `detail.*` translation keys to all 11 locale files (en, zh, ja, de, fr, es, ar, ru, pt, ko)
1445+
- [x] `useDetailTranslation` safe wrapper hook with English fallback (follows existing useGridTranslation/useListViewTranslation pattern)
1446+
- [x] DetailView fully i18n-integrated (Back, Edit, Share, Delete, Duplicate, Export, View history, Record not found, Related heading, favorites, navigation)
1447+
- [x] DetailSection copy tooltip i18n via `useSectionTranslation`
1448+
- [x] RelatedList i18n-integrated (record counts, loading, empty state)
1449+
- [x] Add `'detail'` to `BUILTIN_KEYS` in `useObjectLabel.ts` to prevent namespace collision
1450+
1451+
**Empty Value Display:**
1452+
- [x] Replace hardcoded `-` with styled em-dash (``) using `text-muted-foreground/50 text-xs italic` for elegant empty state
1453+
1454+
**Related List Enhancements:**
1455+
- [x] Add `onNew` prop and "New" button to RelatedList header
1456+
- [x] Add `onViewAll` prop and "View All" button to RelatedList header
1457+
- [x] Record count uses singular/plural i18n keys
1458+
1459+
**Tests:**
1460+
- [x] 10 new RelatedList tests (title, record counts, empty state, New/View All buttons)
1461+
- [x] 2 new DetailView i18n fallback tests (Record not found text, Related heading)
1462+
- [x] Updated DetailSection tests for new empty value styling
1463+
1464+
**Remaining (future PRs):**
1465+
- [ ] Auto-discover related lists from objectSchema reference fields
1466+
- [ ] Tab layout (Details/Related/Activity) for detail page
1467+
- [ ] Related list row-level Edit/Delete quick actions
1468+
- [ ] Related list pagination, sorting, filtering
1469+
- [ ] Collapsible section groups
1470+
- [ ] Header highlight area with key fields
1471+
1472+
---
1473+
14391474
## 📚 Reference
14401475

14411476
- [CONTRIBUTING.md](./CONTRIBUTING.md) — Contribution guidelines

packages/i18n/src/locales/ar.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,41 @@ const ar = {
131131
deleteCard: 'حذف بطاقة',
132132
deleteColumn: 'حذف عمود',
133133
},
134+
detail: {
135+
back: 'رجوع',
136+
edit: 'تحرير',
137+
editInline: 'تحرير مباشر',
138+
save: 'حفظ',
139+
saveChanges: 'حفظ التغييرات',
140+
editFieldsInline: 'تحرير الحقول مباشرة',
141+
share: 'مشاركة',
142+
duplicate: 'نسخ',
143+
export: 'تصدير',
144+
viewHistory: 'عرض السجل',
145+
delete: 'حذف',
146+
moreActions: 'المزيد من الإجراءات',
147+
addToFavorites: 'إضافة إلى المفضلة',
148+
removeFromFavorites: 'إزالة من المفضلة',
149+
previousRecord: 'السجل السابق',
150+
nextRecord: 'السجل التالي',
151+
recordOf: '{{current}} من {{total}}',
152+
recordNotFound: 'لم يتم العثور على السجل',
153+
recordNotFoundDescription: 'السجل الذي تبحث عنه غير موجود أو ربما تم حذفه.',
154+
goBack: 'رجوع',
155+
details: 'التفاصيل',
156+
related: 'ذات صلة',
157+
relatedRecords: '{{count}} سجلات',
158+
relatedRecordOne: '{{count}} سجل',
159+
noRelatedRecords: 'لا توجد سجلات ذات صلة',
160+
loading: 'جاري التحميل...',
161+
copyToClipboard: 'نسخ إلى الحافظة',
162+
copied: 'تم النسخ!',
163+
deleteConfirmation: 'هل أنت متأكد أنك تريد حذف هذا السجل؟',
164+
editRecord: 'تحرير السجل',
165+
viewAll: 'عرض الكل',
166+
new: 'جديد',
167+
emptyValue: '—',
168+
},
134169
chart: {
135170
noData: 'لا تتوفر بيانات للرسم البياني',
136171
loading: 'جاري تحميل الرسم البياني...',

packages/i18n/src/locales/de.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const de = {
130130
deleteCard: 'Karte löschen',
131131
deleteColumn: 'Spalte löschen',
132132
},
133+
detail: {
134+
back: 'Zurück',
135+
edit: 'Bearbeiten',
136+
editInline: 'Inline bearbeiten',
137+
save: 'Speichern',
138+
saveChanges: 'Änderungen speichern',
139+
editFieldsInline: 'Felder inline bearbeiten',
140+
share: 'Teilen',
141+
duplicate: 'Duplizieren',
142+
export: 'Exportieren',
143+
viewHistory: 'Verlauf anzeigen',
144+
delete: 'Löschen',
145+
moreActions: 'Weitere Aktionen',
146+
addToFavorites: 'Zu Favoriten hinzufügen',
147+
removeFromFavorites: 'Aus Favoriten entfernen',
148+
previousRecord: 'Vorheriger Datensatz',
149+
nextRecord: 'Nächster Datensatz',
150+
recordOf: '{{current}} von {{total}}',
151+
recordNotFound: 'Datensatz nicht gefunden',
152+
recordNotFoundDescription: 'Der gesuchte Datensatz existiert nicht oder wurde möglicherweise gelöscht.',
153+
goBack: 'Zurück',
154+
details: 'Details',
155+
related: 'Verknüpft',
156+
relatedRecords: '{{count}} Datensätze',
157+
relatedRecordOne: '{{count}} Datensatz',
158+
noRelatedRecords: 'Keine verknüpften Datensätze gefunden',
159+
loading: 'Laden...',
160+
copyToClipboard: 'In Zwischenablage kopieren',
161+
copied: 'Kopiert!',
162+
deleteConfirmation: 'Sind Sie sicher, dass Sie diesen Datensatz löschen möchten?',
163+
editRecord: 'Datensatz bearbeiten',
164+
viewAll: 'Alle anzeigen',
165+
new: 'Neu',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: 'Keine Diagrammdaten verfügbar',
135170
loading: 'Diagramm wird geladen...',

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/es.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const es = {
130130
deleteCard: 'Eliminar tarjeta',
131131
deleteColumn: 'Eliminar columna',
132132
},
133+
detail: {
134+
back: 'Volver',
135+
edit: 'Editar',
136+
editInline: 'Editar en línea',
137+
save: 'Guardar',
138+
saveChanges: 'Guardar cambios',
139+
editFieldsInline: 'Editar campos en línea',
140+
share: 'Compartir',
141+
duplicate: 'Duplicar',
142+
export: 'Exportar',
143+
viewHistory: 'Ver historial',
144+
delete: 'Eliminar',
145+
moreActions: 'Más acciones',
146+
addToFavorites: 'Añadir a favoritos',
147+
removeFromFavorites: 'Quitar de favoritos',
148+
previousRecord: 'Registro anterior',
149+
nextRecord: 'Siguiente registro',
150+
recordOf: '{{current}} de {{total}}',
151+
recordNotFound: 'Registro no encontrado',
152+
recordNotFoundDescription: 'El registro que busca no existe o puede haber sido eliminado.',
153+
goBack: 'Volver',
154+
details: 'Detalles',
155+
related: 'Relacionados',
156+
relatedRecords: '{{count}} registros',
157+
relatedRecordOne: '{{count}} registro',
158+
noRelatedRecords: 'No se encontraron registros relacionados',
159+
loading: 'Cargando...',
160+
copyToClipboard: 'Copiar al portapapeles',
161+
copied: '¡Copiado!',
162+
deleteConfirmation: '¿Está seguro de que desea eliminar este registro?',
163+
editRecord: 'Editar registro',
164+
viewAll: 'Ver todo',
165+
new: 'Nuevo',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: 'No hay datos de gráfico disponibles',
135170
loading: 'Cargando gráfico...',

packages/i18n/src/locales/fr.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const fr = {
130130
deleteCard: 'Supprimer la carte',
131131
deleteColumn: 'Supprimer la colonne',
132132
},
133+
detail: {
134+
back: 'Retour',
135+
edit: 'Modifier',
136+
editInline: 'Modifier en ligne',
137+
save: 'Enregistrer',
138+
saveChanges: 'Enregistrer les modifications',
139+
editFieldsInline: 'Modifier les champs en ligne',
140+
share: 'Partager',
141+
duplicate: 'Dupliquer',
142+
export: 'Exporter',
143+
viewHistory: "Voir l'historique",
144+
delete: 'Supprimer',
145+
moreActions: "Plus d'actions",
146+
addToFavorites: 'Ajouter aux favoris',
147+
removeFromFavorites: 'Retirer des favoris',
148+
previousRecord: 'Enregistrement précédent',
149+
nextRecord: 'Enregistrement suivant',
150+
recordOf: '{{current}} sur {{total}}',
151+
recordNotFound: 'Enregistrement introuvable',
152+
recordNotFoundDescription: "L'enregistrement que vous recherchez n'existe pas ou a peut-être été supprimé.",
153+
goBack: 'Retour',
154+
details: 'Détails',
155+
related: 'Associés',
156+
relatedRecords: '{{count}} enregistrements',
157+
relatedRecordOne: '{{count}} enregistrement',
158+
noRelatedRecords: 'Aucun enregistrement associé trouvé',
159+
loading: 'Chargement...',
160+
copyToClipboard: 'Copier dans le presse-papiers',
161+
copied: 'Copié !',
162+
deleteConfirmation: 'Êtes-vous sûr de vouloir supprimer cet enregistrement ?',
163+
editRecord: "Modifier l'enregistrement",
164+
viewAll: 'Tout afficher',
165+
new: 'Nouveau',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: 'Aucune donnée de graphique disponible',
135170
loading: 'Chargement du graphique...',

packages/i18n/src/locales/ja.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const ja = {
130130
deleteCard: 'カードを削除',
131131
deleteColumn: 'カラムを削除',
132132
},
133+
detail: {
134+
back: '戻る',
135+
edit: '編集',
136+
editInline: 'インライン編集',
137+
save: '保存',
138+
saveChanges: '変更を保存',
139+
editFieldsInline: 'フィールドをインライン編集',
140+
share: '共有',
141+
duplicate: '複製',
142+
export: 'エクスポート',
143+
viewHistory: '履歴を表示',
144+
delete: '削除',
145+
moreActions: 'その他の操作',
146+
addToFavorites: 'お気に入りに追加',
147+
removeFromFavorites: 'お気に入りから削除',
148+
previousRecord: '前のレコード',
149+
nextRecord: '次のレコード',
150+
recordOf: '{{current}} / {{total}}',
151+
recordNotFound: 'レコードが見つかりません',
152+
recordNotFoundDescription: 'お探しのレコードは存在しないか、削除された可能性があります。',
153+
goBack: '戻る',
154+
details: '詳細',
155+
related: '関連',
156+
relatedRecords: '{{count}} 件のレコード',
157+
relatedRecordOne: '{{count}} 件のレコード',
158+
noRelatedRecords: '関連レコードが見つかりません',
159+
loading: '読み込み中...',
160+
copyToClipboard: 'クリップボードにコピー',
161+
copied: 'コピーしました!',
162+
deleteConfirmation: 'このレコードを削除してもよろしいですか?',
163+
editRecord: 'レコードを編集',
164+
viewAll: 'すべて表示',
165+
new: '新規',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: 'チャートデータがありません',
135170
loading: 'チャート読み込み中...',

packages/i18n/src/locales/ko.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const ko = {
130130
deleteCard: '카드 삭제',
131131
deleteColumn: '열 삭제',
132132
},
133+
detail: {
134+
back: '뒤로',
135+
edit: '편집',
136+
editInline: '인라인 편집',
137+
save: '저장',
138+
saveChanges: '변경사항 저장',
139+
editFieldsInline: '필드 인라인 편집',
140+
share: '공유',
141+
duplicate: '복제',
142+
export: '내보내기',
143+
viewHistory: '기록 보기',
144+
delete: '삭제',
145+
moreActions: '더 많은 작업',
146+
addToFavorites: '즐겨찾기에 추가',
147+
removeFromFavorites: '즐겨찾기에서 제거',
148+
previousRecord: '이전 레코드',
149+
nextRecord: '다음 레코드',
150+
recordOf: '{{current}} / {{total}}',
151+
recordNotFound: '레코드를 찾을 수 없음',
152+
recordNotFoundDescription: '찾으시는 레코드가 존재하지 않거나 삭제되었을 수 있습니다.',
153+
goBack: '뒤로',
154+
details: '상세정보',
155+
related: '관련',
156+
relatedRecords: '{{count}}개 레코드',
157+
relatedRecordOne: '{{count}}개 레코드',
158+
noRelatedRecords: '관련 레코드를 찾을 수 없습니다',
159+
loading: '로딩 중...',
160+
copyToClipboard: '클립보드에 복사',
161+
copied: '복사됨!',
162+
deleteConfirmation: '이 레코드를 삭제하시겠습니까?',
163+
editRecord: '레코드 편집',
164+
viewAll: '모두 보기',
165+
new: '새로 만들기',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: '차트 데이터가 없습니다',
135170
loading: '차트 로딩 중...',

packages/i18n/src/locales/pt.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ const pt = {
130130
deleteCard: 'Excluir cartão',
131131
deleteColumn: 'Excluir coluna',
132132
},
133+
detail: {
134+
back: 'Voltar',
135+
edit: 'Editar',
136+
editInline: 'Editar inline',
137+
save: 'Salvar',
138+
saveChanges: 'Salvar alterações',
139+
editFieldsInline: 'Editar campos inline',
140+
share: 'Compartilhar',
141+
duplicate: 'Duplicar',
142+
export: 'Exportar',
143+
viewHistory: 'Ver histórico',
144+
delete: 'Excluir',
145+
moreActions: 'Mais ações',
146+
addToFavorites: 'Adicionar aos favoritos',
147+
removeFromFavorites: 'Remover dos favoritos',
148+
previousRecord: 'Registro anterior',
149+
nextRecord: 'Próximo registro',
150+
recordOf: '{{current}} de {{total}}',
151+
recordNotFound: 'Registro não encontrado',
152+
recordNotFoundDescription: 'O registro que você está procurando não existe ou pode ter sido excluído.',
153+
goBack: 'Voltar',
154+
details: 'Detalhes',
155+
related: 'Relacionados',
156+
relatedRecords: '{{count}} registros',
157+
relatedRecordOne: '{{count}} registro',
158+
noRelatedRecords: 'Nenhum registro relacionado encontrado',
159+
loading: 'Carregando...',
160+
copyToClipboard: 'Copiar para área de transferência',
161+
copied: 'Copiado!',
162+
deleteConfirmation: 'Tem certeza de que deseja excluir este registro?',
163+
editRecord: 'Editar registro',
164+
viewAll: 'Ver tudo',
165+
new: 'Novo',
166+
emptyValue: '—',
167+
},
133168
chart: {
134169
noData: 'Nenhum dado de gráfico disponível',
135170
loading: 'Carregando gráfico...',

0 commit comments

Comments
 (0)