@object-ui/i18n 3.0.3 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/locales/ar.d.ts +314 -0
- package/dist/locales/ar.js +314 -0
- package/dist/locales/de.d.ts +314 -0
- package/dist/locales/de.js +314 -0
- package/dist/locales/en.d.ts +372 -0
- package/dist/locales/en.js +372 -0
- package/dist/locales/es.d.ts +314 -0
- package/dist/locales/es.js +314 -0
- package/dist/locales/fr.d.ts +314 -0
- package/dist/locales/fr.js +314 -0
- package/dist/locales/index.d.ts +3256 -0
- package/dist/locales/ja.d.ts +314 -0
- package/dist/locales/ja.js +314 -0
- package/dist/locales/ko.d.ts +314 -0
- package/dist/locales/ko.js +314 -0
- package/dist/locales/pt.d.ts +314 -0
- package/dist/locales/pt.js +314 -0
- package/dist/locales/ru.d.ts +314 -0
- package/dist/locales/ru.js +314 -0
- package/dist/locales/zh.d.ts +372 -0
- package/dist/locales/zh.js +372 -0
- package/dist/provider.js +34 -4
- package/dist/useObjectLabel.d.ts +61 -0
- package/dist/useObjectLabel.js +114 -0
- package/package.json +3 -3
- package/src/__tests__/useObjectLabel.test.tsx +131 -0
- package/src/index.ts +3 -0
- package/src/locales/ar.ts +314 -0
- package/src/locales/de.ts +314 -0
- package/src/locales/en.ts +372 -0
- package/src/locales/es.ts +314 -0
- package/src/locales/fr.ts +314 -0
- package/src/locales/ja.ts +314 -0
- package/src/locales/ko.ts +314 -0
- package/src/locales/pt.ts +314 -0
- package/src/locales/ru.ts +314 -0
- package/src/locales/zh.ts +372 -0
- package/src/provider.tsx +33 -4
- package/src/useObjectLabel.ts +122 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/locales/zh.ts
CHANGED
|
@@ -69,6 +69,33 @@ const zh = {
|
|
|
69
69
|
selectAllRows: '选择所有行',
|
|
70
70
|
expandRow: '展开行',
|
|
71
71
|
collapseRow: '折叠行',
|
|
72
|
+
hideColumn: '隐藏列',
|
|
73
|
+
freezeColumn: '冻结列',
|
|
74
|
+
unfreezeColumn: '取消冻结列',
|
|
75
|
+
pageInfo: '第 {{current}} 页,共 {{total}} 页',
|
|
76
|
+
totalRecords: '共 {{count}} 条',
|
|
77
|
+
noResults: '未找到结果',
|
|
78
|
+
noResultsHint: '请尝试调整筛选条件或搜索关键词。',
|
|
79
|
+
cancelAll: '全部取消',
|
|
80
|
+
saveAll: '全部保存 ({{count}})',
|
|
81
|
+
addRecord: '添加记录',
|
|
82
|
+
open: '打开',
|
|
83
|
+
search: '搜索...',
|
|
84
|
+
modified: '{{count}} 行已修改',
|
|
85
|
+
selected: '已选择 {{count}} 项',
|
|
86
|
+
},
|
|
87
|
+
grid: {
|
|
88
|
+
actions: '操作',
|
|
89
|
+
edit: '编辑',
|
|
90
|
+
delete: '删除',
|
|
91
|
+
export: '导出',
|
|
92
|
+
exportAs: '导出为 {{format}}',
|
|
93
|
+
loading: '加载数据中...',
|
|
94
|
+
errorLoading: '数据加载失败',
|
|
95
|
+
pullToRefresh: '下拉刷新',
|
|
96
|
+
refreshing: '刷新中…',
|
|
97
|
+
openRecord: '打开记录',
|
|
98
|
+
rowHeight: '行高: {{mode}}',
|
|
72
99
|
},
|
|
73
100
|
calendar: {
|
|
74
101
|
today: '今天',
|
|
@@ -79,6 +106,38 @@ const zh = {
|
|
|
79
106
|
allDay: '全天',
|
|
80
107
|
noEvents: '暂无事件',
|
|
81
108
|
newEvent: '新建事件',
|
|
109
|
+
moreEvents: '+{{count}} 更多',
|
|
110
|
+
},
|
|
111
|
+
list: {
|
|
112
|
+
recordCount: '{{count}} 条记录',
|
|
113
|
+
recordCountOne: '{{count}} 条记录',
|
|
114
|
+
addRecord: '添加记录',
|
|
115
|
+
tabs: '标签页',
|
|
116
|
+
allRecords: '全部记录',
|
|
117
|
+
search: '搜索',
|
|
118
|
+
filter: '筛选',
|
|
119
|
+
filterRecords: '筛选记录',
|
|
120
|
+
sort: '排序',
|
|
121
|
+
sortRecords: '排序记录',
|
|
122
|
+
group: '分组',
|
|
123
|
+
groupBy: '分组依据',
|
|
124
|
+
export: '导出',
|
|
125
|
+
exportAs: '导出为 {{format}}',
|
|
126
|
+
color: '颜色',
|
|
127
|
+
rowColor: '行颜色',
|
|
128
|
+
colorByField: '按字段着色',
|
|
129
|
+
clear: '清除',
|
|
130
|
+
none: '无',
|
|
131
|
+
hideFields: '隐藏字段',
|
|
132
|
+
noItems: '未找到项目',
|
|
133
|
+
noItemsMessage: '没有可显示的记录。请尝试调整筛选条件或添加新数据。',
|
|
134
|
+
showAll: '显示全部',
|
|
135
|
+
pullToRefresh: '下拉刷新',
|
|
136
|
+
refreshing: '刷新中…',
|
|
137
|
+
share: '分享',
|
|
138
|
+
print: '打印',
|
|
139
|
+
hideFieldsTitle: '隐藏字段',
|
|
140
|
+
dataLimitReached: '显示前 {{limit}} 条记录。可能有更多数据。',
|
|
82
141
|
},
|
|
83
142
|
kanban: {
|
|
84
143
|
addCard: '添加卡片',
|
|
@@ -87,6 +146,41 @@ const zh = {
|
|
|
87
146
|
deleteCard: '删除卡片',
|
|
88
147
|
deleteColumn: '删除列',
|
|
89
148
|
},
|
|
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
|
+
},
|
|
90
184
|
chart: {
|
|
91
185
|
noData: '暂无图表数据',
|
|
92
186
|
loading: '图表加载中...',
|
|
@@ -98,9 +192,162 @@ const zh = {
|
|
|
98
192
|
saveLayout: '保存布局',
|
|
99
193
|
resetLayout: '重置布局',
|
|
100
194
|
},
|
|
195
|
+
configPanel: {
|
|
196
|
+
save: '保存',
|
|
197
|
+
discard: '丢弃',
|
|
198
|
+
close: '关闭',
|
|
199
|
+
layout: '布局',
|
|
200
|
+
columns: '列数',
|
|
201
|
+
gap: '间距',
|
|
202
|
+
rowHeight: '行高',
|
|
203
|
+
data: '数据',
|
|
204
|
+
refreshInterval: '刷新间隔',
|
|
205
|
+
appearance: '外观',
|
|
206
|
+
title: '标题',
|
|
207
|
+
showDescription: '显示描述',
|
|
208
|
+
theme: '主题',
|
|
209
|
+
configuration: '配置',
|
|
210
|
+
general: '基本',
|
|
211
|
+
advanced: '高级',
|
|
212
|
+
},
|
|
213
|
+
appDesigner: {
|
|
214
|
+
createApp: '创建应用',
|
|
215
|
+
editApp: '编辑应用',
|
|
216
|
+
basicInfo: '基本信息',
|
|
217
|
+
objects: '业务对象',
|
|
218
|
+
navigation: '导航',
|
|
219
|
+
branding: '品牌',
|
|
220
|
+
appName: '应用名称',
|
|
221
|
+
appTitle: '标题',
|
|
222
|
+
appDescription: '描述',
|
|
223
|
+
appIcon: '图标',
|
|
224
|
+
template: '模板',
|
|
225
|
+
layout: '布局',
|
|
226
|
+
layoutSidebar: '侧边栏',
|
|
227
|
+
layoutHeader: '顶部导航',
|
|
228
|
+
layoutEmpty: '空白',
|
|
229
|
+
selectObjects: '选择对象',
|
|
230
|
+
searchObjects: '搜索对象…',
|
|
231
|
+
selectAll: '全选',
|
|
232
|
+
deselectAll: '取消全选',
|
|
233
|
+
navBuilder: '导航构建器',
|
|
234
|
+
addGroup: '添加分组',
|
|
235
|
+
addUrl: '添加链接',
|
|
236
|
+
addSeparator: '添加分隔线',
|
|
237
|
+
noNavItems: '暂无导航项。',
|
|
238
|
+
logoUrl: 'Logo 链接',
|
|
239
|
+
primaryColor: '主色调',
|
|
240
|
+
faviconUrl: '网站图标链接',
|
|
241
|
+
preview: '预览',
|
|
242
|
+
complete: '完成',
|
|
243
|
+
snakeCaseHint: '必须使用 snake_case 格式(如 my_app)',
|
|
244
|
+
modeEdit: '编辑',
|
|
245
|
+
modePreview: '预览',
|
|
246
|
+
modeCode: '代码',
|
|
247
|
+
addWidget: '添加组件',
|
|
248
|
+
widgetProperties: '组件属性',
|
|
249
|
+
dataSource: '数据源',
|
|
250
|
+
valueField: '数值字段',
|
|
251
|
+
aggregate: '聚合',
|
|
252
|
+
colorVariant: '颜色',
|
|
253
|
+
addComponent: '添加组件',
|
|
254
|
+
componentProperties: '组件属性',
|
|
255
|
+
viewType: '视图类型',
|
|
256
|
+
fields: '字段',
|
|
257
|
+
toolbar: '工具栏',
|
|
258
|
+
showSearch: '显示搜索',
|
|
259
|
+
showFilters: '显示筛选',
|
|
260
|
+
showSort: '显示排序',
|
|
261
|
+
appearance: '外观',
|
|
262
|
+
rowHeight: '行高',
|
|
263
|
+
stripedRows: '斑马纹',
|
|
264
|
+
bordered: '边框',
|
|
265
|
+
livePreview: '实时预览',
|
|
266
|
+
stepBasicDesc: '名称、标题和布局',
|
|
267
|
+
stepObjectsDesc: '选择业务对象',
|
|
268
|
+
stepNavigationDesc: '构建导航树',
|
|
269
|
+
stepBrandingDesc: 'Logo、颜色和图标',
|
|
270
|
+
noObjectsFound: '未找到对象。',
|
|
271
|
+
noNavItemsHint: '暂无导航项。请在上一步选择对象或手动添加项目。',
|
|
272
|
+
separator: '分隔线',
|
|
273
|
+
separatorLabel: '— 分隔线 —',
|
|
274
|
+
newGroup: '新建分组',
|
|
275
|
+
newLink: '新建链接',
|
|
276
|
+
saveDraft: '保存草稿',
|
|
277
|
+
cancelConfirmTitle: '放弃更改?',
|
|
278
|
+
cancelConfirmMessage: '您有未保存的更改。确定要取消吗?',
|
|
279
|
+
confirmDiscard: '放弃',
|
|
280
|
+
keepEditing: '继续编辑',
|
|
281
|
+
navNoItems: '暂无导航项。点击上方按钮添加项目。',
|
|
282
|
+
navNoPreviewItems: '无项目',
|
|
283
|
+
navLivePreview: '实时预览',
|
|
284
|
+
navCollapseGroup: '折叠分组',
|
|
285
|
+
navExpandGroup: '展开分组',
|
|
286
|
+
navAddChild: '添加子项',
|
|
287
|
+
navMoveUp: '上移',
|
|
288
|
+
navMoveDown: '下移',
|
|
289
|
+
navRemove: '移除',
|
|
290
|
+
navObjectPage: '对象页面',
|
|
291
|
+
navDashboard: '仪表盘',
|
|
292
|
+
navPage: '页面',
|
|
293
|
+
navReport: '报表',
|
|
294
|
+
navGroup: '分组',
|
|
295
|
+
navUrl: '链接',
|
|
296
|
+
navSeparator: '分隔线',
|
|
297
|
+
navTypeObject: '对象',
|
|
298
|
+
navTypeDashboard: '仪表盘',
|
|
299
|
+
navTypePage: '页面',
|
|
300
|
+
navTypeReport: '报表',
|
|
301
|
+
navTypeUrl: '链接',
|
|
302
|
+
navTypeGroup: '分组',
|
|
303
|
+
navTypeSeparator: '分隔线',
|
|
304
|
+
navTypeAction: '操作',
|
|
305
|
+
navEditIcon: '编辑图标',
|
|
306
|
+
navToggleVisible: '切换可见性',
|
|
307
|
+
navHidden: '已隐藏',
|
|
308
|
+
navExportSchema: '导出 JSON',
|
|
309
|
+
navImportSchema: '导入 JSON',
|
|
310
|
+
navExportSuccess: '导航结构已导出',
|
|
311
|
+
navImportSuccess: '导航结构已导入',
|
|
312
|
+
navImportError: '无效的导航 JSON',
|
|
313
|
+
navIconPlaceholder: '图标名称(如 Users)',
|
|
314
|
+
dashboardEditor: '仪表盘编辑器',
|
|
315
|
+
noWidgets: '暂无小组件。点击上方按钮添加。',
|
|
316
|
+
widgetLayoutSize: '布局尺寸',
|
|
317
|
+
widgetWidth: '宽度',
|
|
318
|
+
widgetHeight: '高度',
|
|
319
|
+
dashboardPreview: '仪表盘预览',
|
|
320
|
+
noWidgetsPreview: '暂无可预览的小组件',
|
|
321
|
+
pageCanvasEditor: '页面画布编辑器',
|
|
322
|
+
emptyPage: '空白页面。点击上方按钮添加组件。',
|
|
323
|
+
pagePreview: '页面预览',
|
|
324
|
+
noComponentsPreview: '暂无可预览的组件',
|
|
325
|
+
modePage: '页面',
|
|
326
|
+
modeDashboard: '仪表盘',
|
|
327
|
+
undo: '撤销',
|
|
328
|
+
redo: '重做',
|
|
329
|
+
brandingEditor: '品牌编辑器',
|
|
330
|
+
brandingExport: '导出 JSON',
|
|
331
|
+
brandingImport: '导入 JSON',
|
|
332
|
+
brandingPreview: '预览',
|
|
333
|
+
brandingSampleButton: '示例按钮',
|
|
334
|
+
brandingSampleText: '这是您的品牌主题效果预览。',
|
|
335
|
+
colorPalette: '颜色面板',
|
|
336
|
+
fontFamily: '字体',
|
|
337
|
+
fontDefault: '默认(系统)',
|
|
338
|
+
modeLight: '浅色',
|
|
339
|
+
modeDark: '深色',
|
|
340
|
+
mobilePreview: '移动端预览',
|
|
341
|
+
},
|
|
101
342
|
console: {
|
|
102
343
|
title: 'ObjectStack 控制台',
|
|
103
344
|
initializing: '正在初始化应用程序...',
|
|
345
|
+
breadcrumb: {
|
|
346
|
+
dashboards: '仪表盘',
|
|
347
|
+
pages: '页面',
|
|
348
|
+
reports: '报告',
|
|
349
|
+
system: '系统',
|
|
350
|
+
},
|
|
104
351
|
loadingSteps: {
|
|
105
352
|
connecting: '正在连接数据源',
|
|
106
353
|
loadingConfig: '正在加载配置',
|
|
@@ -140,6 +387,7 @@ const zh = {
|
|
|
140
387
|
systemTheme: '系统主题',
|
|
141
388
|
actions: '操作',
|
|
142
389
|
openFullSearch: '打开完整搜索页面',
|
|
390
|
+
createApp: '创建新应用',
|
|
143
391
|
},
|
|
144
392
|
errors: {
|
|
145
393
|
somethingWentWrong: '出错了',
|
|
@@ -166,6 +414,130 @@ const zh = {
|
|
|
166
414
|
addView: '添加视图',
|
|
167
415
|
designTools: '设计工具',
|
|
168
416
|
new: '新建',
|
|
417
|
+
configureView: '配置视图',
|
|
418
|
+
closePanel: '关闭面板',
|
|
419
|
+
page: '页面',
|
|
420
|
+
pageConfigHint: '工具栏、导航与视图外壳设置',
|
|
421
|
+
listConfigHint: '此列表类型的数据渲染、列与外观',
|
|
422
|
+
general: '常规',
|
|
423
|
+
generalHint: '视图标题、描述和类型',
|
|
424
|
+
toolbar: '工具栏',
|
|
425
|
+
toolbarHint: '搜索、筛选、排序、分组和密度切换',
|
|
426
|
+
toolbarEnabledCount: '{{count}} of {{total}} enabled',
|
|
427
|
+
navigationSection: '导航',
|
|
428
|
+
navigationHint: '行点击行为和详情视图设置',
|
|
429
|
+
records: '记录',
|
|
430
|
+
recordsHint: '选择模式、添加记录和内联编辑',
|
|
431
|
+
exportPrint: '导出与打印',
|
|
432
|
+
exportPrintHint: '导出格式、打印和记录数显示',
|
|
433
|
+
showAllFields: '全部显示',
|
|
434
|
+
hideAllFields: '全部隐藏',
|
|
435
|
+
searchFields: '搜索字段...',
|
|
436
|
+
title: '标题',
|
|
437
|
+
description: '描述',
|
|
438
|
+
data: '数据',
|
|
439
|
+
source: '数据源',
|
|
440
|
+
columns: '列',
|
|
441
|
+
filterBy: '筛选条件',
|
|
442
|
+
sortBy: '排序条件',
|
|
443
|
+
appearance: '外观',
|
|
444
|
+
showDescription: '显示描述',
|
|
445
|
+
viewType: '视图类型',
|
|
446
|
+
userFilters: '用户筛选器',
|
|
447
|
+
enableSearch: '启用搜索',
|
|
448
|
+
enableFilter: '启用筛选',
|
|
449
|
+
enableSort: '启用排序',
|
|
450
|
+
enableHideFields: '启用隐藏字段',
|
|
451
|
+
enableGroup: '启用分组',
|
|
452
|
+
enableColor: '启用颜色',
|
|
453
|
+
enableDensity: '启用密度',
|
|
454
|
+
userActions: '用户操作',
|
|
455
|
+
addRecordViaForm: '通过表单添加记录',
|
|
456
|
+
advanced: '高级',
|
|
457
|
+
allowExport: '允许导出',
|
|
458
|
+
recordCount: '{{count}} 条记录',
|
|
459
|
+
noDescription: '无描述',
|
|
460
|
+
none: '无',
|
|
461
|
+
columnsConfigured: '{{count}} 列',
|
|
462
|
+
save: '保存',
|
|
463
|
+
discard: '丢弃',
|
|
464
|
+
createView: '创建视图',
|
|
465
|
+
newView: '新视图',
|
|
466
|
+
typeOptions: '类型选项',
|
|
467
|
+
groupByField: '分组字段',
|
|
468
|
+
startDateField: '开始日期字段',
|
|
469
|
+
titleField: '标题字段',
|
|
470
|
+
latitudeField: '纬度字段',
|
|
471
|
+
longitudeField: '经度字段',
|
|
472
|
+
imageField: '图片字段',
|
|
473
|
+
dateField: '日期字段',
|
|
474
|
+
selectField: '选择字段...',
|
|
475
|
+
gridOptionsHint: '网格视图使用上方配置的列。',
|
|
476
|
+
groupBy: '分组依据',
|
|
477
|
+
prefixField: '前缀字段',
|
|
478
|
+
fields: '字段',
|
|
479
|
+
fieldsVisible: '{{count}} 个可见',
|
|
480
|
+
sortsCount: '{{count}} 个排序',
|
|
481
|
+
filtersCount: '{{count}} 个筛选',
|
|
482
|
+
endDateField: '结束日期字段',
|
|
483
|
+
color: '颜色',
|
|
484
|
+
fieldTextColor: '字段文字颜色',
|
|
485
|
+
rowHeight: '行高',
|
|
486
|
+
wrapHeaders: '自动换行标题',
|
|
487
|
+
showFieldDescriptions: '显示字段描述',
|
|
488
|
+
collapseAllByDefault: '默认全部折叠',
|
|
489
|
+
striped: '斑马纹行',
|
|
490
|
+
bordered: '边框单元格',
|
|
491
|
+
inlineEdit: '内联编辑记录',
|
|
492
|
+
addDeleteRecordsInline: '内联添加/删除记录',
|
|
493
|
+
clickIntoRecordDetails: '点击进入记录详情',
|
|
494
|
+
navigationMode: '导航模式',
|
|
495
|
+
navigationWidth: '导航宽度',
|
|
496
|
+
navigationWidthHint: '适用于抽屉、弹窗和分屏导航模式',
|
|
497
|
+
openNewTab: '在新标签页打开',
|
|
498
|
+
openNewTabHint: '适用于页面和新窗口导航模式',
|
|
499
|
+
selectionMode: '选择模式',
|
|
500
|
+
selectionNone: '无',
|
|
501
|
+
selectionSingle: '单选',
|
|
502
|
+
selectionMultiple: '多选',
|
|
503
|
+
pageSize: '每页记录数',
|
|
504
|
+
pageSizeOptions: '每页选项',
|
|
505
|
+
exportFormats: '导出格式',
|
|
506
|
+
exportMaxRecords: '最大记录数',
|
|
507
|
+
exportIncludeHeaders: '包含表头',
|
|
508
|
+
exportFileNamePrefix: '文件名前缀',
|
|
509
|
+
searchableFields: '可搜索字段',
|
|
510
|
+
filterableFields: '可筛选字段',
|
|
511
|
+
resizableColumns: '可调整列宽',
|
|
512
|
+
densityCompact: '紧凑',
|
|
513
|
+
densityComfortable: '舒适',
|
|
514
|
+
densitySpacious: '宽松',
|
|
515
|
+
densityMode: '密度模式',
|
|
516
|
+
hiddenFields: '隐藏字段',
|
|
517
|
+
rowActions: '行操作',
|
|
518
|
+
bulkActions: '批量操作',
|
|
519
|
+
sharing: '共享',
|
|
520
|
+
sharingEnabled: '启用共享',
|
|
521
|
+
sharingVisibility: '可见性',
|
|
522
|
+
addRecordEnabled: '启用添加记录',
|
|
523
|
+
addRecordPosition: '位置',
|
|
524
|
+
addRecordMode: '模式',
|
|
525
|
+
addRecordFormView: '表单视图',
|
|
526
|
+
conditionalFormatting: '条件格式',
|
|
527
|
+
addRule: '添加规则',
|
|
528
|
+
quickFilters: '快速筛选',
|
|
529
|
+
addQuickFilter: '添加快速筛选',
|
|
530
|
+
showRecordCount: '显示记录计数',
|
|
531
|
+
allowPrinting: '允许打印',
|
|
532
|
+
virtualScroll: '虚拟滚动',
|
|
533
|
+
emptyStateTitle: '空状态标题',
|
|
534
|
+
emptyStateMessage: '空状态消息',
|
|
535
|
+
emptyStateIcon: '空状态图标',
|
|
536
|
+
ariaLabel: 'ARIA 标签',
|
|
537
|
+
ariaDescribedBy: 'ARIA 描述',
|
|
538
|
+
ariaLive: 'ARIA 实时区域',
|
|
539
|
+
accessibility: '无障碍',
|
|
540
|
+
viewTabs: '视图标签',
|
|
169
541
|
},
|
|
170
542
|
localeSwitcher: {
|
|
171
543
|
label: '语言',
|
package/src/provider.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides I18nProvider and useObjectTranslation hook for React components.
|
|
5
5
|
*/
|
|
6
|
-
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
6
|
+
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
8
8
|
import type { i18n as I18nInstance } from 'i18next';
|
|
9
9
|
import { createI18n, getDirection, type I18nConfig } from './i18n';
|
|
@@ -67,6 +67,10 @@ export function I18nProvider({ config, instance: externalInstance, loadLanguage,
|
|
|
67
67
|
const [language, setLanguage] = useState(i18nInstance.language || 'en');
|
|
68
68
|
const direction = getDirection(language);
|
|
69
69
|
|
|
70
|
+
// Track which languages have had app-specific translations loaded
|
|
71
|
+
// (separate from built-in locales that ship with the library)
|
|
72
|
+
const loadedAppLangs = useRef<Set<string>>(new Set());
|
|
73
|
+
|
|
70
74
|
useEffect(() => {
|
|
71
75
|
const handleLanguageChanged = (lng: string) => {
|
|
72
76
|
setLanguage(lng);
|
|
@@ -83,14 +87,39 @@ export function I18nProvider({ config, instance: externalInstance, loadLanguage,
|
|
|
83
87
|
};
|
|
84
88
|
}, [i18nInstance]);
|
|
85
89
|
|
|
90
|
+
// Load app-specific translations for the initial language on mount
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!loadLanguage) return;
|
|
93
|
+
const currentLang = i18nInstance.language || 'en';
|
|
94
|
+
if (loadedAppLangs.current.has(currentLang)) return;
|
|
95
|
+
loadedAppLangs.current.add(currentLang);
|
|
96
|
+
loadLanguage(currentLang).then((resources) => {
|
|
97
|
+
if (resources && Object.keys(resources).length > 0) {
|
|
98
|
+
i18nInstance.addResourceBundle(currentLang, 'translation', resources, true, true);
|
|
99
|
+
// Force re-render so components pick up newly loaded translations
|
|
100
|
+
setLanguage(currentLang);
|
|
101
|
+
}
|
|
102
|
+
}).catch((err) => {
|
|
103
|
+
// Allow retry on failure by removing from loaded set
|
|
104
|
+
loadedAppLangs.current.delete(currentLang);
|
|
105
|
+
console.warn(`[i18n] Failed to load app translations for '${currentLang}':`, err);
|
|
106
|
+
});
|
|
107
|
+
}, [i18nInstance, loadLanguage]);
|
|
108
|
+
|
|
86
109
|
const contextValue = useMemo<I18nContextValue>(
|
|
87
110
|
() => ({
|
|
88
111
|
language,
|
|
89
112
|
changeLanguage: async (lang: string) => {
|
|
90
113
|
// Dynamic language pack loading (v2.0.7)
|
|
91
|
-
if (loadLanguage && !
|
|
92
|
-
|
|
93
|
-
|
|
114
|
+
if (loadLanguage && !loadedAppLangs.current.has(lang)) {
|
|
115
|
+
loadedAppLangs.current.add(lang);
|
|
116
|
+
try {
|
|
117
|
+
const resources = await loadLanguage(lang);
|
|
118
|
+
i18nInstance.addResourceBundle(lang, 'translation', resources, true, true);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
loadedAppLangs.current.delete(lang);
|
|
121
|
+
console.warn(`[i18n] Failed to load app translations for '${lang}':`, err);
|
|
122
|
+
}
|
|
94
123
|
}
|
|
95
124
|
await i18nInstance.changeLanguage(lang);
|
|
96
125
|
},
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @object-ui/i18n - Convention-based Object & Field Label i18n
|
|
3
|
+
*
|
|
4
|
+
* Provides automatic translation resolution for object metadata labels
|
|
5
|
+
* using Salesforce-style convention-based key generation.
|
|
6
|
+
*
|
|
7
|
+
* The app namespace (e.g. "crm") is discovered dynamically from loaded
|
|
8
|
+
* i18next resources — no hardcoded app names in platform code.
|
|
9
|
+
*
|
|
10
|
+
* Convention Rules:
|
|
11
|
+
* | What | Auto-generated key | Fallback |
|
|
12
|
+
* |--------------------|-------------------------------------------------|-----------------------|
|
|
13
|
+
* | Object label | {ns}.objects.{objectName}.label | objectDef.label |
|
|
14
|
+
* | Object description | {ns}.objects.{objectName}.description | objectDef.description |
|
|
15
|
+
* | Field label | {ns}.fields.{objectName}.{fieldName} | field.label |
|
|
16
|
+
*
|
|
17
|
+
* @module useObjectLabel
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { useObjectTranslation } from './provider';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Built-in Object UI top-level locale keys — not app namespaces.
|
|
24
|
+
* Update this set when new top-level platform translation keys are added
|
|
25
|
+
* to `packages/i18n/src/locales/en.ts` to prevent them from being treated
|
|
26
|
+
* as app namespaces during dynamic namespace discovery.
|
|
27
|
+
*/
|
|
28
|
+
const BUILTIN_KEYS = new Set([
|
|
29
|
+
'common', 'validation', 'form', 'table', 'grid', 'calendar',
|
|
30
|
+
'list', 'kanban', 'chart', 'dashboard', 'configPanel',
|
|
31
|
+
'appDesigner', 'console', 'errors', 'detail',
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Hook for convention-based auto-resolution of object and field labels.
|
|
36
|
+
*
|
|
37
|
+
* Automatically constructs i18n keys from object/field names and looks up
|
|
38
|
+
* translations, falling back to the plain-string label when no translation exists.
|
|
39
|
+
*
|
|
40
|
+
* The app namespace is discovered dynamically from loaded i18next resources
|
|
41
|
+
* by finding top-level keys that contain an `objects` sub-key.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* const { objectLabel, fieldLabel } = useObjectLabel();
|
|
46
|
+
* <h1>{objectLabel(objectDef)}</h1>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function useObjectLabel() {
|
|
50
|
+
const { t, i18n } = useObjectTranslation();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Discover app namespace(s) from loaded i18next resources.
|
|
54
|
+
* Returns top-level keys (outside built-in Object UI keys) that contain
|
|
55
|
+
* an `objects` or `fields` sub-key — e.g. "crm" when resources include crm.objects.*.
|
|
56
|
+
*/
|
|
57
|
+
const getAppNamespaces = (): string[] => {
|
|
58
|
+
if (!i18n || typeof i18n.getResourceBundle !== 'function') return [];
|
|
59
|
+
const lang = i18n.language || 'en';
|
|
60
|
+
const bundle = i18n.getResourceBundle(lang, 'translation') as Record<string, any> | undefined;
|
|
61
|
+
if (!bundle) return [];
|
|
62
|
+
return Object.keys(bundle).filter(
|
|
63
|
+
(key) => !BUILTIN_KEYS.has(key) && bundle[key] && typeof bundle[key] === 'object'
|
|
64
|
+
&& (bundle[key].objects || bundle[key].fields),
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Try resolving a key across all discovered app namespaces. */
|
|
69
|
+
const resolve = (suffix: string, fallback: string): string => {
|
|
70
|
+
try {
|
|
71
|
+
const namespaces = getAppNamespaces();
|
|
72
|
+
for (const ns of namespaces) {
|
|
73
|
+
const key = `${ns}.${suffix}`;
|
|
74
|
+
const translated = t(key, { defaultValue: '' });
|
|
75
|
+
if (translated && translated !== key && translated !== '') {
|
|
76
|
+
return translated;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Graceful degradation when i18n provider is not available
|
|
81
|
+
}
|
|
82
|
+
return fallback;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
/**
|
|
87
|
+
* Resolve translated object label, falling back to objectDef.label.
|
|
88
|
+
*/
|
|
89
|
+
objectLabel: (objectDef: { name: string; label: string }) =>
|
|
90
|
+
resolve(`objects.${objectDef.name}.label`, objectDef.label),
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve translated object description, falling back to objectDef.description.
|
|
94
|
+
*/
|
|
95
|
+
objectDescription: (objectDef: { name: string; description?: string }) => {
|
|
96
|
+
if (!objectDef.description) return undefined;
|
|
97
|
+
return resolve(`objects.${objectDef.name}.description`, objectDef.description);
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve translated field label, falling back to the provided fallback string.
|
|
102
|
+
*/
|
|
103
|
+
fieldLabel: (objectName: string, fieldName: string, fallback: string) =>
|
|
104
|
+
resolve(`fields.${objectName}.${fieldName}`, fallback),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Safe wrapper for useObjectLabel that falls back to identity functions
|
|
110
|
+
* when no I18nProvider is available. Suitable for plugin components that
|
|
111
|
+
* may be rendered outside an i18n context.
|
|
112
|
+
*/
|
|
113
|
+
export function useSafeFieldLabel() {
|
|
114
|
+
try {
|
|
115
|
+
const { fieldLabel } = useObjectLabel();
|
|
116
|
+
return { fieldLabel };
|
|
117
|
+
} catch {
|
|
118
|
+
return {
|
|
119
|
+
fieldLabel: (_objectName: string, _fieldName: string, fallback: string) => fallback,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|