@object-ui/i18n 17.6.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +5408 -0
  2. package/README.md +255 -13
  3. package/dist/LocalizationContext.d.ts +32 -9
  4. package/dist/LocalizationContext.js +32 -10
  5. package/dist/builtinAggregateLabels.d.ts +40 -0
  6. package/dist/builtinAggregateLabels.js +23 -0
  7. package/dist/currency.d.ts +27 -3
  8. package/dist/currency.js +28 -4
  9. package/dist/fallbackInterpolation.d.ts +79 -0
  10. package/dist/fallbackInterpolation.js +124 -0
  11. package/dist/i18n.d.ts +27 -1
  12. package/dist/i18n.js +99 -16
  13. package/dist/index.d.ts +8 -13
  14. package/dist/index.js +45 -17
  15. package/dist/locales/ar.d.ts +1504 -188
  16. package/dist/locales/ar.js +1711 -281
  17. package/dist/locales/de.d.ts +1080 -188
  18. package/dist/locales/de.js +1248 -261
  19. package/dist/locales/en.d.ts +1141 -249
  20. package/dist/locales/en.js +1714 -295
  21. package/dist/locales/es.d.ts +1186 -188
  22. package/dist/locales/es.js +1350 -255
  23. package/dist/locales/fr.d.ts +1186 -188
  24. package/dist/locales/fr.js +1351 -258
  25. package/dist/locales/index.d.ts +11940 -2033
  26. package/dist/locales/index.js +48 -9
  27. package/dist/locales/ja.d.ts +1080 -188
  28. package/dist/locales/ja.js +1240 -254
  29. package/dist/locales/ko.d.ts +1080 -188
  30. package/dist/locales/ko.js +1244 -258
  31. package/dist/locales/pt.d.ts +1186 -188
  32. package/dist/locales/pt.js +1350 -255
  33. package/dist/locales/registry.d.ts +102 -0
  34. package/dist/locales/registry.js +220 -0
  35. package/dist/locales/rtl.d.ts +24 -0
  36. package/dist/locales/rtl.js +26 -0
  37. package/dist/locales/ru.d.ts +1292 -188
  38. package/dist/locales/ru.js +1479 -272
  39. package/dist/locales/zh.d.ts +1144 -252
  40. package/dist/locales/zh.js +1245 -259
  41. package/dist/pickLocalized.d.ts +30 -0
  42. package/dist/pickLocalized.js +39 -0
  43. package/dist/provider.d.ts +101 -12
  44. package/dist/provider.js +335 -50
  45. package/dist/translateFn.d.ts +29 -0
  46. package/dist/translateFn.js +8 -0
  47. package/dist/useDisplayLocale.d.ts +11 -0
  48. package/dist/useDisplayLocale.js +11 -0
  49. package/dist/useObjectLabel.d.ts +143 -100
  50. package/dist/useObjectLabel.js +129 -82
  51. package/dist/useSafeTranslation.js +40 -42
  52. package/dist/utils/first-day-of-week.d.ts +71 -0
  53. package/dist/utils/first-day-of-week.js +61 -0
  54. package/dist/utils/formatting.d.ts +23 -1
  55. package/dist/utils/formatting.js +34 -5
  56. package/dist/utils/index.d.ts +1 -0
  57. package/dist/utils/index.js +1 -0
  58. package/dist/utils/spec-formatters.d.ts +13 -23
  59. package/dist/utils/spec-formatters.js +41 -2
  60. package/dist/utils/spec-translations.d.ts +16 -3
  61. package/dist/utils/spec-translations.js +52 -13
  62. package/package.json +12 -6
@@ -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
  */
@@ -100,15 +127,17 @@ export declare function useObjectLabel(): {
100
127
  * reaches this client — so nav labels arrive already localized. One
101
128
  * owner, not two. To translate a sidebar group, translate it there.
102
129
  *
103
- * History (objectui#5197): until then this docstring promised
130
+ * History (`9c60144b5`): until then this docstring promised
104
131
  * `"Sales" → "销售"` for sidebar groups, and `NavigationRenderer`
105
132
  * accepted `resolveGroupLabel`/`resolveItemLabel` to wire it up. Those
106
133
  * props could never fire — the renderer's `isCustomized` guard compared a
107
134
  * node's authored label against its own `id` (`Workspace` vs
108
135
  * `grp_workspace`), which never match, so the guard was true for every
109
- * real entry. The props are gone; the promise was false, not merely
110
- * unused. This helper is kept as a plain key reader for a consumer that
111
- * renders navigation itself — it has no first-party caller.
136
+ * real entry. The props are gone, and so is the guard itself
137
+ * (objectui#11201: the renderer no longer matches a label's text against
138
+ * any name); the promise was false, not merely unused. This helper is kept
139
+ * as a plain key reader for a consumer that renders navigation itself — it
140
+ * has no first-party caller.
112
141
  */
113
142
  navGroupLabel: (appName: string, groupId: string, fallback: string) => string;
114
143
  /**
@@ -145,31 +174,13 @@ export declare function useObjectLabel(): {
145
174
  * Resolve translated widget description within a dashboard.
146
175
  * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
147
176
  * Returns undefined when neither metadata nor translation provides one.
148
- */
149
- widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
150
- /**
151
- * Resolve a translated metric-widget SUB-CAPTION within a dashboard.
152
- * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.subCaption`.
153
- * Returns undefined when neither metadata nor translation provides one.
154
- *
155
- * Deliberately its OWN key, not a second reader of `widgets.{id}.description`.
156
- * The KPI card renders two authored strings from two different fields — the
157
- * shared card header's `widget.description`, and the sub-caption under the
158
- * value, which is authored as `widget.options.description` — and the
159
- * objectstack#5428 item-4 ruling (2026-08-06) settled that they get two
160
- * keys, not one: 「两个作者字段两个 key」. Collapsing them would make one
161
- * translation entry silently retarget the other field on any widget type
162
- * that renders both at once (`kpi`, `gauge`, `bullet` — every metric-family
163
- * type except the self-contained `metric`).
164
177
  *
165
- * `subCaption` is the member objectstack#8056 added to the widget
166
- * translation node for exactly this, shipped in `@objectstack/spec@17.0.0`.
167
- * The server-side resolver reads the SAME key and overlays it onto
168
- * `options.description` (`translateDashboard`), so a document served
169
- * through `/meta` and a document translated here land on the same string —
170
- * 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.
171
182
  */
172
- widgetSubCaption: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
183
+ widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
173
184
  /**
174
185
  * Resolve translated page label, falling back to pageDef.label.
175
186
  * Convention: `{ns}.pages.{pageName}.label`.
@@ -194,11 +205,6 @@ export declare function useObjectLabel(): {
194
205
  * that spells the prefix out does not resolve (see `viewSuffixes`).
195
206
  */
196
207
  viewLabel: (objectName: string, viewName: string, fallback: string) => string;
197
- /**
198
- * Resolve translated list-view description.
199
- * Convention: `{ns}.objects.{objectName}._views.{viewName}.description`.
200
- */
201
- viewDescription: (objectName: string, viewName: string, fallback?: string) => string | undefined;
202
208
  /**
203
209
  * Resolve translated list-view emptyState. Returns a {title, message}
204
210
  * tuple with each field independently translated. Convention:
@@ -221,41 +227,55 @@ export declare function useObjectLabel(): {
221
227
  sectionLabel: (objectName: string, sectionName: string, fallback: string) => string;
222
228
  /**
223
229
  * Resolve translated action label.
224
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.label`,
225
- * falling back to `{ns}.globalActions.{actionName}.label` — both when
226
- * objectName is omitted AND when the object-scoped key misses (so a
227
- * globalAction surfaced on a record-detail action bar still resolves;
228
- * objectui#3372).
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.
229
236
  */
230
237
  actionLabel: (objectName: string | undefined, actionName: string, fallback: string) => string;
231
238
  /**
232
239
  * Resolve translated action confirmation prompt.
233
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`,
234
- * falling back to `{ns}.globalActions.{actionName}.confirmText`.
240
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`;
241
+ * with no key object, `{ns}.globalActions.{actionName}.confirmText`.
235
242
  * Returns undefined when no translation and no fallback exist.
236
243
  */
237
244
  actionConfirm: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
238
245
  /**
239
246
  * Resolve translated action success message.
240
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`,
241
- * falling back to `{ns}.globalActions.{actionName}.successMessage`.
247
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`;
248
+ * with no key object, `{ns}.globalActions.{actionName}.successMessage`.
242
249
  */
243
250
  actionSuccess: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
251
+ /**
252
+ * Resolve the translated success copy for ONE handler outcome — an entry of
253
+ * the action's `outcomeMessages` (`@objectstack/spec` 17.6.0, objectui#11344).
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.
257
+ * Returns undefined when no translation and no fallback exist.
258
+ *
259
+ * Like `actionSuccess`, the answer is a TEMPLATE: a `${result.*}` token in a
260
+ * bundle entry passes through untouched (i18next interpolates its own
261
+ * `{{…}}` form, never `${…}`), and the action runner fills it in from the
262
+ * handler's answer once the action has run.
263
+ */
264
+ actionOutcome: (objectName: string | undefined, actionName: string, outcome: string, fallback?: string) => string | undefined;
244
265
  /**
245
266
  * Resolve translated action description (the explanatory line shown in the
246
267
  * action's param dialog / sheet / drawer header).
247
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`.
248
- * Falls back to `{ns}.globalActions.{actionName}.description`, then the
249
- * 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.
250
271
  */
251
272
  actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
252
273
  /**
253
274
  * Resolve a translated copy of an action's post-success RESULT DIALOG
254
275
  * (`title` / `description` / `acknowledge` + per-field labels).
255
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`,
256
- * falling back to `{ns}.globalActions.{actionName}.resultDialog.*` (when
257
- * objectName is omitted OR the object-scoped key misses), then to the
258
- * 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.
259
279
  *
260
280
  * The `fields` translation node is keyed by the LITERAL result-field path
261
281
  * from the action metadata (may contain dots, e.g. `"user.email"`), so it
@@ -274,14 +294,16 @@ export declare function useObjectLabel(): {
274
294
  }>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
275
295
  /**
276
296
  * Resolve translated action-PARAMETER text (label / placeholder / helpText).
277
- * 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}`.
278
299
  * Falls back to the provided value (the metadata's literal string) when no
279
300
  * translation exists, so untranslated params keep rendering as authored.
280
301
  */
281
302
  actionParamText: (objectName: string | undefined, actionName: string | undefined, paramName: string, attr: "label" | "placeholder" | "helpText", fallback?: string) => string | undefined;
282
303
  /**
283
304
  * Resolve a translated action-parameter SELECT OPTION label.
284
- * 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}`.
285
307
  * Falls back to the provided (English metadata) label when untranslated.
286
308
  */
287
309
  actionParamOptionLabel: (objectName: string | undefined, actionName: string | undefined, paramName: string, optionValue: string, fallback: string) => string;
@@ -299,6 +321,32 @@ export declare function useSafeFieldLabel(): {
299
321
  name: string;
300
322
  label: string;
301
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;
302
350
  /**
303
351
  * Resolve translated object description, falling back to objectDef.description.
304
352
  */
@@ -360,15 +408,17 @@ export declare function useSafeFieldLabel(): {
360
408
  * reaches this client — so nav labels arrive already localized. One
361
409
  * owner, not two. To translate a sidebar group, translate it there.
362
410
  *
363
- * History (objectui#5197): until then this docstring promised
411
+ * History (`9c60144b5`): until then this docstring promised
364
412
  * `"Sales" → "销售"` for sidebar groups, and `NavigationRenderer`
365
413
  * accepted `resolveGroupLabel`/`resolveItemLabel` to wire it up. Those
366
414
  * props could never fire — the renderer's `isCustomized` guard compared a
367
415
  * node's authored label against its own `id` (`Workspace` vs
368
416
  * `grp_workspace`), which never match, so the guard was true for every
369
- * real entry. The props are gone; the promise was false, not merely
370
- * unused. This helper is kept as a plain key reader for a consumer that
371
- * renders navigation itself — it has no first-party caller.
417
+ * real entry. The props are gone, and so is the guard itself
418
+ * (objectui#11201: the renderer no longer matches a label's text against
419
+ * any name); the promise was false, not merely unused. This helper is kept
420
+ * as a plain key reader for a consumer that renders navigation itself — it
421
+ * has no first-party caller.
372
422
  */
373
423
  navGroupLabel: (appName: string, groupId: string, fallback: string) => string;
374
424
  /**
@@ -405,31 +455,13 @@ export declare function useSafeFieldLabel(): {
405
455
  * Resolve translated widget description within a dashboard.
406
456
  * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
407
457
  * Returns undefined when neither metadata nor translation provides one.
408
- */
409
- widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
410
- /**
411
- * Resolve a translated metric-widget SUB-CAPTION within a dashboard.
412
- * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.subCaption`.
413
- * Returns undefined when neither metadata nor translation provides one.
414
- *
415
- * Deliberately its OWN key, not a second reader of `widgets.{id}.description`.
416
- * The KPI card renders two authored strings from two different fields — the
417
- * shared card header's `widget.description`, and the sub-caption under the
418
- * value, which is authored as `widget.options.description` — and the
419
- * objectstack#5428 item-4 ruling (2026-08-06) settled that they get two
420
- * keys, not one: 「两个作者字段两个 key」. Collapsing them would make one
421
- * translation entry silently retarget the other field on any widget type
422
- * that renders both at once (`kpi`, `gauge`, `bullet` — every metric-family
423
- * type except the self-contained `metric`).
424
458
  *
425
- * `subCaption` is the member objectstack#8056 added to the widget
426
- * translation node for exactly this, shipped in `@objectstack/spec@17.0.0`.
427
- * The server-side resolver reads the SAME key and overlays it onto
428
- * `options.description` (`translateDashboard`), so a document served
429
- * through `/meta` and a document translated here land on the same string —
430
- * 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.
431
463
  */
432
- widgetSubCaption: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
464
+ widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
433
465
  /**
434
466
  * Resolve translated page label, falling back to pageDef.label.
435
467
  * Convention: `{ns}.pages.{pageName}.label`.
@@ -454,11 +486,6 @@ export declare function useSafeFieldLabel(): {
454
486
  * that spells the prefix out does not resolve (see `viewSuffixes`).
455
487
  */
456
488
  viewLabel: (objectName: string, viewName: string, fallback: string) => string;
457
- /**
458
- * Resolve translated list-view description.
459
- * Convention: `{ns}.objects.{objectName}._views.{viewName}.description`.
460
- */
461
- viewDescription: (objectName: string, viewName: string, fallback?: string) => string | undefined;
462
489
  /**
463
490
  * Resolve translated list-view emptyState. Returns a {title, message}
464
491
  * tuple with each field independently translated. Convention:
@@ -481,41 +508,55 @@ export declare function useSafeFieldLabel(): {
481
508
  sectionLabel: (objectName: string, sectionName: string, fallback: string) => string;
482
509
  /**
483
510
  * Resolve translated action label.
484
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.label`,
485
- * falling back to `{ns}.globalActions.{actionName}.label` — both when
486
- * objectName is omitted AND when the object-scoped key misses (so a
487
- * globalAction surfaced on a record-detail action bar still resolves;
488
- * objectui#3372).
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.
489
517
  */
490
518
  actionLabel: (objectName: string | undefined, actionName: string, fallback: string) => string;
491
519
  /**
492
520
  * Resolve translated action confirmation prompt.
493
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`,
494
- * falling back to `{ns}.globalActions.{actionName}.confirmText`.
521
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`;
522
+ * with no key object, `{ns}.globalActions.{actionName}.confirmText`.
495
523
  * Returns undefined when no translation and no fallback exist.
496
524
  */
497
525
  actionConfirm: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
498
526
  /**
499
527
  * Resolve translated action success message.
500
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`,
501
- * falling back to `{ns}.globalActions.{actionName}.successMessage`.
528
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`;
529
+ * with no key object, `{ns}.globalActions.{actionName}.successMessage`.
502
530
  */
503
531
  actionSuccess: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
532
+ /**
533
+ * Resolve the translated success copy for ONE handler outcome — an entry of
534
+ * the action's `outcomeMessages` (`@objectstack/spec` 17.6.0, objectui#11344).
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.
538
+ * Returns undefined when no translation and no fallback exist.
539
+ *
540
+ * Like `actionSuccess`, the answer is a TEMPLATE: a `${result.*}` token in a
541
+ * bundle entry passes through untouched (i18next interpolates its own
542
+ * `{{…}}` form, never `${…}`), and the action runner fills it in from the
543
+ * handler's answer once the action has run.
544
+ */
545
+ actionOutcome: (objectName: string | undefined, actionName: string, outcome: string, fallback?: string) => string | undefined;
504
546
  /**
505
547
  * Resolve translated action description (the explanatory line shown in the
506
548
  * action's param dialog / sheet / drawer header).
507
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`.
508
- * Falls back to `{ns}.globalActions.{actionName}.description`, then the
509
- * 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.
510
552
  */
511
553
  actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
512
554
  /**
513
555
  * Resolve a translated copy of an action's post-success RESULT DIALOG
514
556
  * (`title` / `description` / `acknowledge` + per-field labels).
515
- * Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`,
516
- * falling back to `{ns}.globalActions.{actionName}.resultDialog.*` (when
517
- * objectName is omitted OR the object-scoped key misses), then to the
518
- * 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.
519
560
  *
520
561
  * The `fields` translation node is keyed by the LITERAL result-field path
521
562
  * from the action metadata (may contain dots, e.g. `"user.email"`), so it
@@ -534,14 +575,16 @@ export declare function useSafeFieldLabel(): {
534
575
  }>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
535
576
  /**
536
577
  * Resolve translated action-PARAMETER text (label / placeholder / helpText).
537
- * 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}`.
538
580
  * Falls back to the provided value (the metadata's literal string) when no
539
581
  * translation exists, so untranslated params keep rendering as authored.
540
582
  */
541
583
  actionParamText: (objectName: string | undefined, actionName: string | undefined, paramName: string, attr: "label" | "placeholder" | "helpText", fallback?: string) => string | undefined;
542
584
  /**
543
585
  * Resolve a translated action-parameter SELECT OPTION label.
544
- * 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}`.
545
588
  * Falls back to the provided (English metadata) label when untranslated.
546
589
  */
547
590
  actionParamOptionLabel: (objectName: string | undefined, actionName: string | undefined, paramName: string, optionValue: string, fallback: string) => string;