@object-ui/i18n 17.7.0 → 18.0.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/CHANGELOG.md +1088 -0
- package/README.md +64 -1
- package/dist/LocalizationContext.d.ts +32 -9
- package/dist/LocalizationContext.js +32 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/locales/ar.d.ts +591 -108
- package/dist/locales/ar.js +704 -208
- package/dist/locales/de.d.ts +475 -88
- package/dist/locales/de.js +589 -183
- package/dist/locales/en.d.ts +562 -175
- package/dist/locales/en.js +745 -208
- package/dist/locales/es.d.ts +504 -93
- package/dist/locales/es.js +617 -186
- package/dist/locales/fr.d.ts +504 -93
- package/dist/locales/fr.js +617 -187
- package/dist/locales/index.d.ts +5178 -1092
- package/dist/locales/ja.d.ts +475 -88
- package/dist/locales/ja.js +588 -182
- package/dist/locales/ko.d.ts +475 -88
- package/dist/locales/ko.js +588 -182
- package/dist/locales/pt.d.ts +504 -93
- package/dist/locales/pt.js +617 -186
- package/dist/locales/ru.d.ts +533 -98
- package/dist/locales/ru.js +646 -197
- package/dist/locales/zh.d.ts +563 -176
- package/dist/locales/zh.js +589 -197
- package/dist/useObjectLabel.d.ts +109 -88
- package/dist/useObjectLabel.js +86 -71
- package/dist/utils/first-day-of-week.d.ts +71 -0
- package/dist/utils/first-day-of-week.js +61 -0
- package/dist/utils/formatting.d.ts +7 -1
- package/dist/utils/formatting.js +11 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +2 -2
package/dist/useObjectLabel.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* | What | Auto-generated key | Fallback |
|
|
12
12
|
* |--------------------|-------------------------------------------------|-----------------------|
|
|
13
13
|
* | Object label | {ns}.objects.{objectName}.label | objectDef.label |
|
|
14
|
+
* | Object plural | {ns}.objects.{objectName}.pluralLabel | objectDef.pluralLabel, then the object label |
|
|
14
15
|
* | Object description | {ns}.objects.{objectName}.description | objectDef.description |
|
|
15
16
|
* | Field label | {ns}.fields.{objectName}.{fieldName} | field.label |
|
|
16
17
|
*
|
|
@@ -39,6 +40,32 @@ export declare function useObjectLabel(): {
|
|
|
39
40
|
name: string;
|
|
40
41
|
label: string;
|
|
41
42
|
}) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the object's PLURAL label — what a list of its records is called:
|
|
45
|
+
* an object list page's title and the breadcrumb segment that links to that
|
|
46
|
+
* list (objectui#11696). Record-scoped surfaces (the record page, a record
|
|
47
|
+
* drawer, "New …") keep {@link objectLabel}.
|
|
48
|
+
*
|
|
49
|
+
* Order: the translated plural (`{ns}.objects.{objectName}.pluralLabel`),
|
|
50
|
+
* else the declared `objectDef.pluralLabel`, else the singular as
|
|
51
|
+
* `objectLabel` resolves it — `pluralLabel` is optional in the spec, and an
|
|
52
|
+
* object that declares none is called by its label everywhere.
|
|
53
|
+
*
|
|
54
|
+
* The first two rungs are the client half of `translateObject` in
|
|
55
|
+
* `@objectstack/spec` (`system/i18n-resolver`), which serves `pluralLabel`
|
|
56
|
+
* as the catalog entry, else the authored value. There is deliberately NO
|
|
57
|
+
* rung that prefers a translated SINGULAR over a declared plural the bundle
|
|
58
|
+
* leaves untranslated: the server has none either, so every consumer of
|
|
59
|
+
* `/meta` shows the authored plural in that case, and this client showing
|
|
60
|
+
* something else would be a second dialect (see `viewSuffixes`). The gap is
|
|
61
|
+
* the bundle's — `os i18n extract` writes `pluralLabel` wherever the object
|
|
62
|
+
* declares one — and is fixed there.
|
|
63
|
+
*/
|
|
64
|
+
objectPluralLabel: (objectDef: {
|
|
65
|
+
name: string;
|
|
66
|
+
label: string;
|
|
67
|
+
pluralLabel?: string;
|
|
68
|
+
}) => string;
|
|
42
69
|
/**
|
|
43
70
|
* Resolve translated object description, falling back to objectDef.description.
|
|
44
71
|
*/
|
|
@@ -147,31 +174,13 @@ export declare function useObjectLabel(): {
|
|
|
147
174
|
* Resolve translated widget description within a dashboard.
|
|
148
175
|
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
|
|
149
176
|
* Returns undefined when neither metadata nor translation provides one.
|
|
150
|
-
*/
|
|
151
|
-
widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
|
|
152
|
-
/**
|
|
153
|
-
* Resolve a translated metric-widget SUB-CAPTION within a dashboard.
|
|
154
|
-
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.subCaption`.
|
|
155
|
-
* Returns undefined when neither metadata nor translation provides one.
|
|
156
|
-
*
|
|
157
|
-
* Deliberately its OWN key, not a second reader of `widgets.{id}.description`.
|
|
158
|
-
* The KPI card renders two authored strings from two different fields — the
|
|
159
|
-
* shared card header's `widget.description`, and the sub-caption under the
|
|
160
|
-
* value, which is authored as `widget.options.description` — and the
|
|
161
|
-
* objectstack#5428 item-4 ruling (2026-08-06) settled that they get two
|
|
162
|
-
* keys, not one: 「两个作者字段两个 key」. Collapsing them would make one
|
|
163
|
-
* translation entry silently retarget the other field on any widget type
|
|
164
|
-
* that renders both at once (`kpi`, `gauge`, `bullet` — every metric-family
|
|
165
|
-
* type except the self-contained `metric`).
|
|
166
177
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* through `/meta` and a document translated here land on the same string —
|
|
172
|
-
* this is the client half of one convention, not a second dialect.
|
|
178
|
+
* This is a widget's ONE translated description. The sibling sub-caption
|
|
179
|
+
* key, `widgets.{widgetId}.subCaption`, is retired with its resolver
|
|
180
|
+
* (objectui#11389, ruling C): `@objectstack/spec` 17.7.0 refuses it by
|
|
181
|
+
* name in a translation bundle, and nothing here reads it.
|
|
173
182
|
*/
|
|
174
|
-
|
|
183
|
+
widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
|
|
175
184
|
/**
|
|
176
185
|
* Resolve translated page label, falling back to pageDef.label.
|
|
177
186
|
* Convention: `{ns}.pages.{pageName}.label`.
|
|
@@ -218,32 +227,33 @@ export declare function useObjectLabel(): {
|
|
|
218
227
|
sectionLabel: (objectName: string, sectionName: string, fallback: string) => string;
|
|
219
228
|
/**
|
|
220
229
|
* Resolve translated action label.
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* objectName
|
|
224
|
-
*
|
|
225
|
-
* objectui#
|
|
230
|
+
* `objectName` is the action's KEY object (its declared `objectName`, else
|
|
231
|
+
* the host that embeds it). Convention:
|
|
232
|
+
* `{ns}.objects.{objectName}._actions.{actionName}.label`; with no key
|
|
233
|
+
* object, `{ns}.globalActions.{actionName}.label`. Never both: a bound
|
|
234
|
+
* action does not read `globalActions` (objectui#11439, see
|
|
235
|
+
* `actionSuffixes`). Every action resolver below follows the same rule.
|
|
226
236
|
*/
|
|
227
237
|
actionLabel: (objectName: string | undefined, actionName: string, fallback: string) => string;
|
|
228
238
|
/**
|
|
229
239
|
* Resolve translated action confirmation prompt.
|
|
230
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText
|
|
231
|
-
*
|
|
240
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`;
|
|
241
|
+
* with no key object, `{ns}.globalActions.{actionName}.confirmText`.
|
|
232
242
|
* Returns undefined when no translation and no fallback exist.
|
|
233
243
|
*/
|
|
234
244
|
actionConfirm: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
|
|
235
245
|
/**
|
|
236
246
|
* Resolve translated action success message.
|
|
237
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage
|
|
238
|
-
*
|
|
247
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`;
|
|
248
|
+
* with no key object, `{ns}.globalActions.{actionName}.successMessage`.
|
|
239
249
|
*/
|
|
240
250
|
actionSuccess: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
|
|
241
251
|
/**
|
|
242
252
|
* Resolve the translated success copy for ONE handler outcome — an entry of
|
|
243
253
|
* the action's `outcomeMessages` (`@objectstack/spec` 17.6.0, objectui#11344).
|
|
244
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}
|
|
245
|
-
*
|
|
246
|
-
* — the
|
|
254
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}`;
|
|
255
|
+
* with no key object, `{ns}.globalActions.{actionName}.outcomeMessages.{outcome}`
|
|
256
|
+
* — the addresses the spec's translation bundle declares for the key.
|
|
247
257
|
* Returns undefined when no translation and no fallback exist.
|
|
248
258
|
*
|
|
249
259
|
* Like `actionSuccess`, the answer is a TEMPLATE: a `${result.*}` token in a
|
|
@@ -255,18 +265,17 @@ export declare function useObjectLabel(): {
|
|
|
255
265
|
/**
|
|
256
266
|
* Resolve translated action description (the explanatory line shown in the
|
|
257
267
|
* action's param dialog / sheet / drawer header).
|
|
258
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description
|
|
259
|
-
*
|
|
260
|
-
* metadata's literal string; undefined when nothing resolves.
|
|
268
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`;
|
|
269
|
+
* with no key object, `{ns}.globalActions.{actionName}.description`. Falls
|
|
270
|
+
* back to the metadata's literal string; undefined when nothing resolves.
|
|
261
271
|
*/
|
|
262
272
|
actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
|
|
263
273
|
/**
|
|
264
274
|
* Resolve a translated copy of an action's post-success RESULT DIALOG
|
|
265
275
|
* (`title` / `description` / `acknowledge` + per-field labels).
|
|
266
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
* metadata's literal strings.
|
|
276
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`;
|
|
277
|
+
* with no key object, `{ns}.globalActions.{actionName}.resultDialog.*`.
|
|
278
|
+
* Falls back to the metadata's literal strings.
|
|
270
279
|
*
|
|
271
280
|
* The `fields` translation node is keyed by the LITERAL result-field path
|
|
272
281
|
* from the action metadata (may contain dots, e.g. `"user.email"`), so it
|
|
@@ -285,14 +294,16 @@ export declare function useObjectLabel(): {
|
|
|
285
294
|
}>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
|
|
286
295
|
/**
|
|
287
296
|
* Resolve translated action-PARAMETER text (label / placeholder / helpText).
|
|
288
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}
|
|
297
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`;
|
|
298
|
+
* with no key object, `{ns}.globalActions.{actionName}.params.{paramName}.{attr}`.
|
|
289
299
|
* Falls back to the provided value (the metadata's literal string) when no
|
|
290
300
|
* translation exists, so untranslated params keep rendering as authored.
|
|
291
301
|
*/
|
|
292
302
|
actionParamText: (objectName: string | undefined, actionName: string | undefined, paramName: string, attr: "label" | "placeholder" | "helpText", fallback?: string) => string | undefined;
|
|
293
303
|
/**
|
|
294
304
|
* Resolve a translated action-parameter SELECT OPTION label.
|
|
295
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}
|
|
305
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}`;
|
|
306
|
+
* with no key object, the same path under `{ns}.globalActions.{actionName}`.
|
|
296
307
|
* Falls back to the provided (English metadata) label when untranslated.
|
|
297
308
|
*/
|
|
298
309
|
actionParamOptionLabel: (objectName: string | undefined, actionName: string | undefined, paramName: string, optionValue: string, fallback: string) => string;
|
|
@@ -310,6 +321,32 @@ export declare function useSafeFieldLabel(): {
|
|
|
310
321
|
name: string;
|
|
311
322
|
label: string;
|
|
312
323
|
}) => string;
|
|
324
|
+
/**
|
|
325
|
+
* Resolve the object's PLURAL label — what a list of its records is called:
|
|
326
|
+
* an object list page's title and the breadcrumb segment that links to that
|
|
327
|
+
* list (objectui#11696). Record-scoped surfaces (the record page, a record
|
|
328
|
+
* drawer, "New …") keep {@link objectLabel}.
|
|
329
|
+
*
|
|
330
|
+
* Order: the translated plural (`{ns}.objects.{objectName}.pluralLabel`),
|
|
331
|
+
* else the declared `objectDef.pluralLabel`, else the singular as
|
|
332
|
+
* `objectLabel` resolves it — `pluralLabel` is optional in the spec, and an
|
|
333
|
+
* object that declares none is called by its label everywhere.
|
|
334
|
+
*
|
|
335
|
+
* The first two rungs are the client half of `translateObject` in
|
|
336
|
+
* `@objectstack/spec` (`system/i18n-resolver`), which serves `pluralLabel`
|
|
337
|
+
* as the catalog entry, else the authored value. There is deliberately NO
|
|
338
|
+
* rung that prefers a translated SINGULAR over a declared plural the bundle
|
|
339
|
+
* leaves untranslated: the server has none either, so every consumer of
|
|
340
|
+
* `/meta` shows the authored plural in that case, and this client showing
|
|
341
|
+
* something else would be a second dialect (see `viewSuffixes`). The gap is
|
|
342
|
+
* the bundle's — `os i18n extract` writes `pluralLabel` wherever the object
|
|
343
|
+
* declares one — and is fixed there.
|
|
344
|
+
*/
|
|
345
|
+
objectPluralLabel: (objectDef: {
|
|
346
|
+
name: string;
|
|
347
|
+
label: string;
|
|
348
|
+
pluralLabel?: string;
|
|
349
|
+
}) => string;
|
|
313
350
|
/**
|
|
314
351
|
* Resolve translated object description, falling back to objectDef.description.
|
|
315
352
|
*/
|
|
@@ -418,31 +455,13 @@ export declare function useSafeFieldLabel(): {
|
|
|
418
455
|
* Resolve translated widget description within a dashboard.
|
|
419
456
|
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
|
|
420
457
|
* Returns undefined when neither metadata nor translation provides one.
|
|
421
|
-
*/
|
|
422
|
-
widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
|
|
423
|
-
/**
|
|
424
|
-
* Resolve a translated metric-widget SUB-CAPTION within a dashboard.
|
|
425
|
-
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.subCaption`.
|
|
426
|
-
* Returns undefined when neither metadata nor translation provides one.
|
|
427
|
-
*
|
|
428
|
-
* Deliberately its OWN key, not a second reader of `widgets.{id}.description`.
|
|
429
|
-
* The KPI card renders two authored strings from two different fields — the
|
|
430
|
-
* shared card header's `widget.description`, and the sub-caption under the
|
|
431
|
-
* value, which is authored as `widget.options.description` — and the
|
|
432
|
-
* objectstack#5428 item-4 ruling (2026-08-06) settled that they get two
|
|
433
|
-
* keys, not one: 「两个作者字段两个 key」. Collapsing them would make one
|
|
434
|
-
* translation entry silently retarget the other field on any widget type
|
|
435
|
-
* that renders both at once (`kpi`, `gauge`, `bullet` — every metric-family
|
|
436
|
-
* type except the self-contained `metric`).
|
|
437
458
|
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
* through `/meta` and a document translated here land on the same string —
|
|
443
|
-
* this is the client half of one convention, not a second dialect.
|
|
459
|
+
* This is a widget's ONE translated description. The sibling sub-caption
|
|
460
|
+
* key, `widgets.{widgetId}.subCaption`, is retired with its resolver
|
|
461
|
+
* (objectui#11389, ruling C): `@objectstack/spec` 17.7.0 refuses it by
|
|
462
|
+
* name in a translation bundle, and nothing here reads it.
|
|
444
463
|
*/
|
|
445
|
-
|
|
464
|
+
widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
|
|
446
465
|
/**
|
|
447
466
|
* Resolve translated page label, falling back to pageDef.label.
|
|
448
467
|
* Convention: `{ns}.pages.{pageName}.label`.
|
|
@@ -489,32 +508,33 @@ export declare function useSafeFieldLabel(): {
|
|
|
489
508
|
sectionLabel: (objectName: string, sectionName: string, fallback: string) => string;
|
|
490
509
|
/**
|
|
491
510
|
* Resolve translated action label.
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* objectName
|
|
495
|
-
*
|
|
496
|
-
* objectui#
|
|
511
|
+
* `objectName` is the action's KEY object (its declared `objectName`, else
|
|
512
|
+
* the host that embeds it). Convention:
|
|
513
|
+
* `{ns}.objects.{objectName}._actions.{actionName}.label`; with no key
|
|
514
|
+
* object, `{ns}.globalActions.{actionName}.label`. Never both: a bound
|
|
515
|
+
* action does not read `globalActions` (objectui#11439, see
|
|
516
|
+
* `actionSuffixes`). Every action resolver below follows the same rule.
|
|
497
517
|
*/
|
|
498
518
|
actionLabel: (objectName: string | undefined, actionName: string, fallback: string) => string;
|
|
499
519
|
/**
|
|
500
520
|
* Resolve translated action confirmation prompt.
|
|
501
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText
|
|
502
|
-
*
|
|
521
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`;
|
|
522
|
+
* with no key object, `{ns}.globalActions.{actionName}.confirmText`.
|
|
503
523
|
* Returns undefined when no translation and no fallback exist.
|
|
504
524
|
*/
|
|
505
525
|
actionConfirm: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
|
|
506
526
|
/**
|
|
507
527
|
* Resolve translated action success message.
|
|
508
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage
|
|
509
|
-
*
|
|
528
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`;
|
|
529
|
+
* with no key object, `{ns}.globalActions.{actionName}.successMessage`.
|
|
510
530
|
*/
|
|
511
531
|
actionSuccess: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
|
|
512
532
|
/**
|
|
513
533
|
* Resolve the translated success copy for ONE handler outcome — an entry of
|
|
514
534
|
* the action's `outcomeMessages` (`@objectstack/spec` 17.6.0, objectui#11344).
|
|
515
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}
|
|
516
|
-
*
|
|
517
|
-
* — the
|
|
535
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}`;
|
|
536
|
+
* with no key object, `{ns}.globalActions.{actionName}.outcomeMessages.{outcome}`
|
|
537
|
+
* — the addresses the spec's translation bundle declares for the key.
|
|
518
538
|
* Returns undefined when no translation and no fallback exist.
|
|
519
539
|
*
|
|
520
540
|
* Like `actionSuccess`, the answer is a TEMPLATE: a `${result.*}` token in a
|
|
@@ -526,18 +546,17 @@ export declare function useSafeFieldLabel(): {
|
|
|
526
546
|
/**
|
|
527
547
|
* Resolve translated action description (the explanatory line shown in the
|
|
528
548
|
* action's param dialog / sheet / drawer header).
|
|
529
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description
|
|
530
|
-
*
|
|
531
|
-
* metadata's literal string; undefined when nothing resolves.
|
|
549
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`;
|
|
550
|
+
* with no key object, `{ns}.globalActions.{actionName}.description`. Falls
|
|
551
|
+
* back to the metadata's literal string; undefined when nothing resolves.
|
|
532
552
|
*/
|
|
533
553
|
actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
|
|
534
554
|
/**
|
|
535
555
|
* Resolve a translated copy of an action's post-success RESULT DIALOG
|
|
536
556
|
* (`title` / `description` / `acknowledge` + per-field labels).
|
|
537
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
* metadata's literal strings.
|
|
557
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`;
|
|
558
|
+
* with no key object, `{ns}.globalActions.{actionName}.resultDialog.*`.
|
|
559
|
+
* Falls back to the metadata's literal strings.
|
|
541
560
|
*
|
|
542
561
|
* The `fields` translation node is keyed by the LITERAL result-field path
|
|
543
562
|
* from the action metadata (may contain dots, e.g. `"user.email"`), so it
|
|
@@ -556,14 +575,16 @@ export declare function useSafeFieldLabel(): {
|
|
|
556
575
|
}>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
|
|
557
576
|
/**
|
|
558
577
|
* Resolve translated action-PARAMETER text (label / placeholder / helpText).
|
|
559
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}
|
|
578
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`;
|
|
579
|
+
* with no key object, `{ns}.globalActions.{actionName}.params.{paramName}.{attr}`.
|
|
560
580
|
* Falls back to the provided value (the metadata's literal string) when no
|
|
561
581
|
* translation exists, so untranslated params keep rendering as authored.
|
|
562
582
|
*/
|
|
563
583
|
actionParamText: (objectName: string | undefined, actionName: string | undefined, paramName: string, attr: "label" | "placeholder" | "helpText", fallback?: string) => string | undefined;
|
|
564
584
|
/**
|
|
565
585
|
* Resolve a translated action-parameter SELECT OPTION label.
|
|
566
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}
|
|
586
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}`;
|
|
587
|
+
* with no key object, the same path under `{ns}.globalActions.{actionName}`.
|
|
567
588
|
* Falls back to the provided (English metadata) label when untranslated.
|
|
568
589
|
*/
|
|
569
590
|
actionParamOptionLabel: (objectName: string | undefined, actionName: string | undefined, paramName: string, optionValue: string, fallback: string) => string;
|
package/dist/useObjectLabel.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* | What | Auto-generated key | Fallback |
|
|
12
12
|
* |--------------------|-------------------------------------------------|-----------------------|
|
|
13
13
|
* | Object label | {ns}.objects.{objectName}.label | objectDef.label |
|
|
14
|
+
* | Object plural | {ns}.objects.{objectName}.pluralLabel | objectDef.pluralLabel, then the object label |
|
|
14
15
|
* | Object description | {ns}.objects.{objectName}.description | objectDef.description |
|
|
15
16
|
* | Field label | {ns}.fields.{objectName}.{fieldName} | field.label |
|
|
16
17
|
*
|
|
@@ -108,9 +109,10 @@ export function useObjectLabel() {
|
|
|
108
109
|
* include crm.objects.* or crm.apps.*.
|
|
109
110
|
*
|
|
110
111
|
* `globalActions` is included so an app bundle whose only translated scope
|
|
111
|
-
* is global actions (no object/field entries) is still discovered —
|
|
112
|
-
* `globalActions.<action>.*`
|
|
113
|
-
* (objectui#3372
|
|
112
|
+
* is global actions (no object/field entries) is still discovered — the
|
|
113
|
+
* `globalActions.<action>.*` copy of its object-less actions would otherwise
|
|
114
|
+
* be unreachable (objectui#3372; object-less only since objectui#11439, see
|
|
115
|
+
* `actionSuffixes`).
|
|
114
116
|
*/
|
|
115
117
|
const getAppNamespaces = () => {
|
|
116
118
|
if (!hasUsableI18nInstance(i18n))
|
|
@@ -168,28 +170,35 @@ export function useObjectLabel() {
|
|
|
168
170
|
};
|
|
169
171
|
/**
|
|
170
172
|
* Build suffix candidates for an action-scoped key, mirroring the canonical
|
|
171
|
-
* `@objectstack/spec` resolver (`
|
|
173
|
+
* `@objectstack/spec` resolver (`actionTranslationNode` behind
|
|
174
|
+
* `lookupActionField`, in `system/i18n-resolver`): ONE node per action, chosen
|
|
175
|
+
* by its key object.
|
|
172
176
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* `globalActions.<action>.*` overlay when no object-scoped translation
|
|
178
|
-
* exists (objectui#3372). Without this fallback, a globalAction rendered on
|
|
179
|
-
* a record-detail action bar — where the caller passes `objectDef.name` for
|
|
180
|
-
* every action — misses `objects.<obj>._actions.<action>.label` and leaks the
|
|
181
|
-
* English metadata literal.
|
|
177
|
+
* `objectName` is the action's KEY object — the object the action belongs
|
|
178
|
+
* to: its declared `objectName`, else the host object that embeds it, which
|
|
179
|
+
* is what the spec's `translateObject` stamps on an embedded action. Callers
|
|
180
|
+
* pass that object (`useActionTextLocalizer` derives it from the action).
|
|
182
181
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
182
|
+
* - A key object reads `objects.<obj>._actions.<action>.<tail>` and nothing
|
|
183
|
+
* else.
|
|
184
|
+
* - No key object reads `globalActions.<action>.<tail>` and nothing else.
|
|
185
|
+
*
|
|
186
|
+
* There is deliberately NO object-then-global chain. objectui#3372 added one
|
|
187
|
+
* so `globalActions` copy reached actions drawn on an object's bar; the
|
|
188
|
+
* spec's `TranslationDataSchema.globalActions` is for actions "not bound to a
|
|
189
|
+
* specific object via `objectName`", `@objectstack/spec` 17.7.0 never reads it
|
|
190
|
+
* for a bound action, and objectui#11439 (triage's amended ruling) retired the
|
|
191
|
+
* chain so this client and the server resolve one bundle entry the same way.
|
|
192
|
+
* A bound action's copy filed under `globalActions` now misses on every
|
|
193
|
+
* surface alike and the authored text shows — the misfiling is what
|
|
194
|
+
* `os validate` already refuses, and it is fixed in the bundle, not here.
|
|
195
|
+
*
|
|
196
|
+
* The two object-name candidates (`objects.<ns__obj>` then `objects.<obj>`)
|
|
197
|
+
* are a separate axis and stay — see `stripNamespace`.
|
|
186
198
|
*/
|
|
187
|
-
const actionSuffixes = (objectName, actionName, tail) =>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
? [...objectSuffixes(objectName, `_actions.${actionName}.${tail}`), globalSuffix]
|
|
191
|
-
: [globalSuffix];
|
|
192
|
-
};
|
|
199
|
+
const actionSuffixes = (objectName, actionName, tail) => objectName
|
|
200
|
+
? objectSuffixes(objectName, `_actions.${actionName}.${tail}`)
|
|
201
|
+
: [`globalActions.${actionName}.${tail}`];
|
|
193
202
|
const fieldSuffixes = (objectName, fieldName) => {
|
|
194
203
|
const base = stripNamespace(objectName);
|
|
195
204
|
return base !== objectName
|
|
@@ -268,11 +277,37 @@ export function useObjectLabel() {
|
|
|
268
277
|
const bareViewName = matchedPrefix ? viewName.slice(matchedPrefix.length) : viewName;
|
|
269
278
|
return objectSuffixes(objectName, `_views.${bareViewName}.${tail}`);
|
|
270
279
|
};
|
|
280
|
+
/** The singular object label; `objectPluralLabel` falls back to it. */
|
|
281
|
+
const objectLabel = (objectDef) => resolve(objectSuffixes(objectDef.name, 'label'), objectDef.label);
|
|
271
282
|
return {
|
|
272
283
|
/**
|
|
273
284
|
* Resolve translated object label, falling back to objectDef.label.
|
|
274
285
|
*/
|
|
275
|
-
objectLabel
|
|
286
|
+
objectLabel,
|
|
287
|
+
/**
|
|
288
|
+
* Resolve the object's PLURAL label — what a list of its records is called:
|
|
289
|
+
* an object list page's title and the breadcrumb segment that links to that
|
|
290
|
+
* list (objectui#11696). Record-scoped surfaces (the record page, a record
|
|
291
|
+
* drawer, "New …") keep {@link objectLabel}.
|
|
292
|
+
*
|
|
293
|
+
* Order: the translated plural (`{ns}.objects.{objectName}.pluralLabel`),
|
|
294
|
+
* else the declared `objectDef.pluralLabel`, else the singular as
|
|
295
|
+
* `objectLabel` resolves it — `pluralLabel` is optional in the spec, and an
|
|
296
|
+
* object that declares none is called by its label everywhere.
|
|
297
|
+
*
|
|
298
|
+
* The first two rungs are the client half of `translateObject` in
|
|
299
|
+
* `@objectstack/spec` (`system/i18n-resolver`), which serves `pluralLabel`
|
|
300
|
+
* as the catalog entry, else the authored value. There is deliberately NO
|
|
301
|
+
* rung that prefers a translated SINGULAR over a declared plural the bundle
|
|
302
|
+
* leaves untranslated: the server has none either, so every consumer of
|
|
303
|
+
* `/meta` shows the authored plural in that case, and this client showing
|
|
304
|
+
* something else would be a second dialect (see `viewSuffixes`). The gap is
|
|
305
|
+
* the bundle's — `os i18n extract` writes `pluralLabel` wherever the object
|
|
306
|
+
* declares one — and is fixed there.
|
|
307
|
+
*/
|
|
308
|
+
objectPluralLabel: (objectDef) => resolve(objectSuffixes(objectDef.name, 'pluralLabel'), '')
|
|
309
|
+
|| objectDef.pluralLabel
|
|
310
|
+
|| objectLabel(objectDef),
|
|
276
311
|
/**
|
|
277
312
|
* Resolve translated object description, falling back to objectDef.description.
|
|
278
313
|
*/
|
|
@@ -373,39 +408,17 @@ export function useObjectLabel() {
|
|
|
373
408
|
* Resolve translated widget description within a dashboard.
|
|
374
409
|
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
|
|
375
410
|
* Returns undefined when neither metadata nor translation provides one.
|
|
411
|
+
*
|
|
412
|
+
* This is a widget's ONE translated description. The sibling sub-caption
|
|
413
|
+
* key, `widgets.{widgetId}.subCaption`, is retired with its resolver
|
|
414
|
+
* (objectui#11389, ruling C): `@objectstack/spec` 17.7.0 refuses it by
|
|
415
|
+
* name in a translation bundle, and nothing here reads it.
|
|
376
416
|
*/
|
|
377
417
|
widgetDescription: (dashboardName, widgetId, fallback) => {
|
|
378
418
|
const fb = fallback ?? '';
|
|
379
419
|
const resolved = resolve(dashboardSuffixes(dashboardName, `widgets.${widgetId}.description`), fb);
|
|
380
420
|
return resolved || undefined;
|
|
381
421
|
},
|
|
382
|
-
/**
|
|
383
|
-
* Resolve a translated metric-widget SUB-CAPTION within a dashboard.
|
|
384
|
-
* Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.subCaption`.
|
|
385
|
-
* Returns undefined when neither metadata nor translation provides one.
|
|
386
|
-
*
|
|
387
|
-
* Deliberately its OWN key, not a second reader of `widgets.{id}.description`.
|
|
388
|
-
* The KPI card renders two authored strings from two different fields — the
|
|
389
|
-
* shared card header's `widget.description`, and the sub-caption under the
|
|
390
|
-
* value, which is authored as `widget.options.description` — and the
|
|
391
|
-
* objectstack#5428 item-4 ruling (2026-08-06) settled that they get two
|
|
392
|
-
* keys, not one: 「两个作者字段两个 key」. Collapsing them would make one
|
|
393
|
-
* translation entry silently retarget the other field on any widget type
|
|
394
|
-
* that renders both at once (`kpi`, `gauge`, `bullet` — every metric-family
|
|
395
|
-
* type except the self-contained `metric`).
|
|
396
|
-
*
|
|
397
|
-
* `subCaption` is the member objectstack#8056 added to the widget
|
|
398
|
-
* translation node for exactly this, shipped in `@objectstack/spec@17.0.0`.
|
|
399
|
-
* The server-side resolver reads the SAME key and overlays it onto
|
|
400
|
-
* `options.description` (`translateDashboard`), so a document served
|
|
401
|
-
* through `/meta` and a document translated here land on the same string —
|
|
402
|
-
* this is the client half of one convention, not a second dialect.
|
|
403
|
-
*/
|
|
404
|
-
widgetSubCaption: (dashboardName, widgetId, fallback) => {
|
|
405
|
-
const fb = fallback ?? '';
|
|
406
|
-
const resolved = resolve(dashboardSuffixes(dashboardName, `widgets.${widgetId}.subCaption`), fb);
|
|
407
|
-
return resolved || undefined;
|
|
408
|
-
},
|
|
409
422
|
/**
|
|
410
423
|
* Resolve translated page label, falling back to pageDef.label.
|
|
411
424
|
* Convention: `{ns}.pages.{pageName}.label`.
|
|
@@ -469,17 +482,18 @@ export function useObjectLabel() {
|
|
|
469
482
|
sectionLabel: (objectName, sectionName, fallback) => resolve(objectSuffixes(objectName, `_sections.${sectionName}.label`), fallback),
|
|
470
483
|
/**
|
|
471
484
|
* Resolve translated action label.
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
* objectName
|
|
475
|
-
*
|
|
476
|
-
* objectui#
|
|
485
|
+
* `objectName` is the action's KEY object (its declared `objectName`, else
|
|
486
|
+
* the host that embeds it). Convention:
|
|
487
|
+
* `{ns}.objects.{objectName}._actions.{actionName}.label`; with no key
|
|
488
|
+
* object, `{ns}.globalActions.{actionName}.label`. Never both: a bound
|
|
489
|
+
* action does not read `globalActions` (objectui#11439, see
|
|
490
|
+
* `actionSuffixes`). Every action resolver below follows the same rule.
|
|
477
491
|
*/
|
|
478
492
|
actionLabel: (objectName, actionName, fallback) => resolve(actionSuffixes(objectName, actionName, 'label'), fallback),
|
|
479
493
|
/**
|
|
480
494
|
* Resolve translated action confirmation prompt.
|
|
481
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText
|
|
482
|
-
*
|
|
495
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`;
|
|
496
|
+
* with no key object, `{ns}.globalActions.{actionName}.confirmText`.
|
|
483
497
|
* Returns undefined when no translation and no fallback exist.
|
|
484
498
|
*/
|
|
485
499
|
actionConfirm: (objectName, actionName, fallback) => {
|
|
@@ -489,8 +503,8 @@ export function useObjectLabel() {
|
|
|
489
503
|
},
|
|
490
504
|
/**
|
|
491
505
|
* Resolve translated action success message.
|
|
492
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage
|
|
493
|
-
*
|
|
506
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`;
|
|
507
|
+
* with no key object, `{ns}.globalActions.{actionName}.successMessage`.
|
|
494
508
|
*/
|
|
495
509
|
actionSuccess: (objectName, actionName, fallback) => {
|
|
496
510
|
const fb = fallback ?? '';
|
|
@@ -500,9 +514,9 @@ export function useObjectLabel() {
|
|
|
500
514
|
/**
|
|
501
515
|
* Resolve the translated success copy for ONE handler outcome — an entry of
|
|
502
516
|
* the action's `outcomeMessages` (`@objectstack/spec` 17.6.0, objectui#11344).
|
|
503
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}
|
|
504
|
-
*
|
|
505
|
-
* — the
|
|
517
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.outcomeMessages.{outcome}`;
|
|
518
|
+
* with no key object, `{ns}.globalActions.{actionName}.outcomeMessages.{outcome}`
|
|
519
|
+
* — the addresses the spec's translation bundle declares for the key.
|
|
506
520
|
* Returns undefined when no translation and no fallback exist.
|
|
507
521
|
*
|
|
508
522
|
* Like `actionSuccess`, the answer is a TEMPLATE: a `${result.*}` token in a
|
|
@@ -518,9 +532,9 @@ export function useObjectLabel() {
|
|
|
518
532
|
/**
|
|
519
533
|
* Resolve translated action description (the explanatory line shown in the
|
|
520
534
|
* action's param dialog / sheet / drawer header).
|
|
521
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description
|
|
522
|
-
*
|
|
523
|
-
* metadata's literal string; undefined when nothing resolves.
|
|
535
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`;
|
|
536
|
+
* with no key object, `{ns}.globalActions.{actionName}.description`. Falls
|
|
537
|
+
* back to the metadata's literal string; undefined when nothing resolves.
|
|
524
538
|
*/
|
|
525
539
|
actionDescription: (objectName, actionName, fallback) => {
|
|
526
540
|
const fb = fallback ?? '';
|
|
@@ -532,10 +546,9 @@ export function useObjectLabel() {
|
|
|
532
546
|
/**
|
|
533
547
|
* Resolve a translated copy of an action's post-success RESULT DIALOG
|
|
534
548
|
* (`title` / `description` / `acknowledge` + per-field labels).
|
|
535
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
* metadata's literal strings.
|
|
549
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`;
|
|
550
|
+
* with no key object, `{ns}.globalActions.{actionName}.resultDialog.*`.
|
|
551
|
+
* Falls back to the metadata's literal strings.
|
|
539
552
|
*
|
|
540
553
|
* The `fields` translation node is keyed by the LITERAL result-field path
|
|
541
554
|
* from the action metadata (may contain dots, e.g. `"user.email"`), so it
|
|
@@ -588,7 +601,8 @@ export function useObjectLabel() {
|
|
|
588
601
|
},
|
|
589
602
|
/**
|
|
590
603
|
* Resolve translated action-PARAMETER text (label / placeholder / helpText).
|
|
591
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}
|
|
604
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`;
|
|
605
|
+
* with no key object, `{ns}.globalActions.{actionName}.params.{paramName}.{attr}`.
|
|
592
606
|
* Falls back to the provided value (the metadata's literal string) when no
|
|
593
607
|
* translation exists, so untranslated params keep rendering as authored.
|
|
594
608
|
*/
|
|
@@ -601,7 +615,8 @@ export function useObjectLabel() {
|
|
|
601
615
|
},
|
|
602
616
|
/**
|
|
603
617
|
* Resolve a translated action-parameter SELECT OPTION label.
|
|
604
|
-
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}
|
|
618
|
+
* Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}`;
|
|
619
|
+
* with no key object, the same path under `{ns}.globalActions.{actionName}`.
|
|
605
620
|
* Falls back to the provided (English metadata) label when untranslated.
|
|
606
621
|
*/
|
|
607
622
|
actionParamOptionLabel: (objectName, actionName, paramName, optionValue, fallback) => {
|