@object-ui/i18n 16.0.0 → 16.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.
@@ -204,6 +204,28 @@ export declare function useObjectLabel(): {
204
204
  * metadata's literal string; undefined when nothing resolves.
205
205
  */
206
206
  actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
207
+ /**
208
+ * Resolve a translated copy of an action's post-success RESULT DIALOG
209
+ * (`title` / `description` / `acknowledge` + per-field labels).
210
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`,
211
+ * falling back to `{ns}.globalActions.{actionName}.resultDialog.*` when
212
+ * objectName is omitted, then to the metadata's literal strings.
213
+ *
214
+ * The `fields` translation node is keyed by the LITERAL result-field path
215
+ * from the action metadata (may contain dots, e.g. `"user.email"`), so it
216
+ * is fetched whole with `returnObjects` and indexed directly — never
217
+ * resolved through a dotted i18next key.
218
+ */
219
+ actionResultDialog: <T extends {
220
+ title?: string;
221
+ description?: string;
222
+ acknowledge?: string;
223
+ fields?: Array<{
224
+ path: string;
225
+ label?: string;
226
+ [key: string]: any;
227
+ }>;
228
+ }>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
207
229
  /**
208
230
  * Resolve translated action-PARAMETER text (label / placeholder / helpText).
209
231
  * Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`.
@@ -224,17 +246,211 @@ export declare function useObjectLabel(): {
224
246
  * may be rendered outside an i18n context.
225
247
  */
226
248
  export declare function useSafeFieldLabel(): {
227
- fieldLabel: (_objectName: string, _fieldName: string, fallback: string) => string;
228
- translateOptions: (_objectName: string, _fieldName: string, options: Array<{
249
+ /**
250
+ * Resolve translated object label, falling back to objectDef.label.
251
+ */
252
+ objectLabel: (objectDef: {
253
+ name: string;
254
+ label: string;
255
+ }) => string;
256
+ /**
257
+ * Resolve translated object description, falling back to objectDef.description.
258
+ */
259
+ objectDescription: (objectDef: {
260
+ name: string;
261
+ description?: string;
262
+ }) => string | undefined;
263
+ /**
264
+ * Resolve translated field label, falling back to the provided fallback string.
265
+ */
266
+ fieldLabel: (objectName: string, fieldName: string, fallback: string) => string;
267
+ /**
268
+ * Resolve a translated select option label for a given object field.
269
+ * Falls back to the provided fallback (usually the English option label).
270
+ */
271
+ fieldOptionLabel: (objectName: string, fieldName: string, optionValue: string, fallback: string) => string;
272
+ /**
273
+ * Translate all options for a given field, returning a new options array
274
+ * with translated labels. Pass the objectName and fieldName to look up
275
+ * translations; the original label is used as fallback.
276
+ */
277
+ translateOptions: (objectName: string, fieldName: string, options: Array<{
229
278
  value: string;
230
279
  label: string;
231
280
  [key: string]: any;
232
- }>) => {
233
- [key: string]: any;
281
+ }>) => Array<{
234
282
  value: string;
235
283
  label: string;
236
- }[];
237
- fieldOptionLabel: (_objectName: string, _fieldName: string, _optionValue: string, fallback: string) => string;
238
- sectionLabel: (_objectName: string, _sectionName: string, fallback: string) => string;
239
- actionLabel: (_objectName: string | undefined, _actionName: string, fallback: string) => string;
284
+ [key: string]: any;
285
+ }>;
286
+ /**
287
+ * Resolve translated app label, falling back to appDef.label.
288
+ * Looks up `{ns}.apps.{appName}.label` from loaded i18next resources.
289
+ */
290
+ appLabel: (appDef: {
291
+ name: string;
292
+ label?: string;
293
+ }) => string;
294
+ /**
295
+ * Resolve translated app description, falling back to appDef.description.
296
+ * Returns the translated value even when metadata has no description —
297
+ * translation-only descriptions (defined only in i18n bundles) are common
298
+ * in examples where the app metadata is English-only.
299
+ */
300
+ appDescription: (appDef: {
301
+ name: string;
302
+ description?: string;
303
+ }) => string | undefined;
304
+ /**
305
+ * Resolve translated label for a navigation group within an app.
306
+ * Convention: `{ns}.apps.{appName}.navigation.{groupId}.label`.
307
+ *
308
+ * Mirrors `objectLabel`/`dashboardLabel` so app metadata can keep
309
+ * English fallbacks while translation packs supply localised
310
+ * sidebar group labels (e.g. "Sales" → "销售") without explicit
311
+ * I18nLabel `{ key, defaultValue }` annotations.
312
+ */
313
+ navGroupLabel: (appName: string, groupId: string, fallback: string) => string;
314
+ /**
315
+ * Resolve translated dashboard label, falling back to dashboardDef.label.
316
+ * Convention: `{ns}.dashboards.{dashboardName}.label`.
317
+ */
318
+ dashboardLabel: (dashboardDef: {
319
+ name: string;
320
+ label?: string;
321
+ }) => string;
322
+ /**
323
+ * Resolve translated dashboard description, falling back to
324
+ * dashboardDef.description. Returns undefined when neither metadata nor
325
+ * translation provides one.
326
+ * Convention: `{ns}.dashboards.{dashboardName}.description`.
327
+ */
328
+ dashboardDescription: (dashboardDef: {
329
+ name: string;
330
+ description?: string;
331
+ }) => string | undefined;
332
+ /**
333
+ * Resolve translated dashboard header-action label.
334
+ * Convention: `{ns}.dashboards.{dashboardName}.actions.{actionKey}.label`.
335
+ * The actionKey is typically the action's `actionUrl` (e.g.
336
+ * `create_opportunity`) or its English label slugified.
337
+ */
338
+ dashboardActionLabel: (dashboardName: string, actionKey: string, fallback: string) => string;
339
+ /**
340
+ * Resolve translated widget title within a dashboard.
341
+ * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.title`.
342
+ */
343
+ widgetTitle: (dashboardName: string, widgetId: string, fallback: string) => string;
344
+ /**
345
+ * Resolve translated widget description within a dashboard.
346
+ * Convention: `{ns}.dashboards.{dashboardName}.widgets.{widgetId}.description`.
347
+ * Returns undefined when neither metadata nor translation provides one.
348
+ */
349
+ widgetDescription: (dashboardName: string, widgetId: string, fallback?: string) => string | undefined;
350
+ /**
351
+ * Resolve translated page label, falling back to pageDef.label.
352
+ * Convention: `{ns}.pages.{pageName}.label`.
353
+ */
354
+ pageLabel: (pageDef: {
355
+ name: string;
356
+ label?: string;
357
+ }) => string;
358
+ /**
359
+ * Resolve translated report label, falling back to reportDef.label.
360
+ * Convention: `{ns}.reports.{reportName}.label`.
361
+ */
362
+ reportLabel: (reportDef: {
363
+ name: string;
364
+ label?: string;
365
+ }) => string;
366
+ /**
367
+ * Resolve translated list-view label.
368
+ * Convention (per @objectstack/spec): `{ns}.objects.{objectName}._views.{viewName}.label`.
369
+ */
370
+ viewLabel: (objectName: string, viewName: string, fallback: string) => string;
371
+ /**
372
+ * Resolve translated list-view description.
373
+ * Convention: `{ns}.objects.{objectName}._views.{viewName}.description`.
374
+ */
375
+ viewDescription: (objectName: string, viewName: string, fallback?: string) => string | undefined;
376
+ /**
377
+ * Resolve translated list-view emptyState. Returns a {title, message}
378
+ * tuple with each field independently translated. Convention:
379
+ * `{ns}.objects.{objectName}._views.{viewName}.emptyState.title`
380
+ * `{ns}.objects.{objectName}._views.{viewName}.emptyState.message`
381
+ */
382
+ viewEmptyState: (objectName: string, viewName: string, fallback: {
383
+ title?: string;
384
+ message?: string;
385
+ icon?: string;
386
+ } | undefined) => {
387
+ title: string | undefined;
388
+ message: string | undefined;
389
+ icon?: string;
390
+ } | undefined;
391
+ /**
392
+ * Resolve translated form-section label.
393
+ * Convention: `{ns}.objects.{objectName}._sections.{sectionName}.label`.
394
+ */
395
+ sectionLabel: (objectName: string, sectionName: string, fallback: string) => string;
396
+ /**
397
+ * Resolve translated action label.
398
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.label`.
399
+ * Falls back to `{ns}.globalActions.{actionName}.label` when objectName is omitted.
400
+ */
401
+ actionLabel: (objectName: string | undefined, actionName: string, fallback: string) => string;
402
+ /**
403
+ * Resolve translated action confirmation prompt.
404
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.confirmText`.
405
+ * Returns undefined when no translation and no fallback exist.
406
+ */
407
+ actionConfirm: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
408
+ /**
409
+ * Resolve translated action success message.
410
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.successMessage`.
411
+ */
412
+ actionSuccess: (objectName: string | undefined, actionName: string, fallback?: string) => string | undefined;
413
+ /**
414
+ * Resolve translated action description (the explanatory line shown in the
415
+ * action's param dialog / sheet / drawer header).
416
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.description`.
417
+ * Falls back to `{ns}.globalActions.{actionName}.description`, then the
418
+ * metadata's literal string; undefined when nothing resolves.
419
+ */
420
+ actionDescription: (objectName: string | undefined, actionName: string | undefined, fallback?: string) => string | undefined;
421
+ /**
422
+ * Resolve a translated copy of an action's post-success RESULT DIALOG
423
+ * (`title` / `description` / `acknowledge` + per-field labels).
424
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`,
425
+ * falling back to `{ns}.globalActions.{actionName}.resultDialog.*` when
426
+ * objectName is omitted, then to the metadata's literal strings.
427
+ *
428
+ * The `fields` translation node is keyed by the LITERAL result-field path
429
+ * from the action metadata (may contain dots, e.g. `"user.email"`), so it
430
+ * is fetched whole with `returnObjects` and indexed directly — never
431
+ * resolved through a dotted i18next key.
432
+ */
433
+ actionResultDialog: <T extends {
434
+ title?: string;
435
+ description?: string;
436
+ acknowledge?: string;
437
+ fields?: Array<{
438
+ path: string;
439
+ label?: string;
440
+ [key: string]: any;
441
+ }>;
442
+ }>(objectName: string | undefined, actionName: string | undefined, spec: T | undefined) => T | undefined;
443
+ /**
444
+ * Resolve translated action-PARAMETER text (label / placeholder / helpText).
445
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`.
446
+ * Falls back to the provided value (the metadata's literal string) when no
447
+ * translation exists, so untranslated params keep rendering as authored.
448
+ */
449
+ actionParamText: (objectName: string | undefined, actionName: string | undefined, paramName: string, attr: "label" | "placeholder" | "helpText", fallback?: string) => string | undefined;
450
+ /**
451
+ * Resolve a translated action-parameter SELECT OPTION label.
452
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.options.{optionValue}`.
453
+ * Falls back to the provided (English metadata) label when untranslated.
454
+ */
455
+ actionParamOptionLabel: (objectName: string | undefined, actionName: string | undefined, paramName: string, optionValue: string, fallback: string) => string;
240
456
  };
@@ -349,6 +349,67 @@ export function useObjectLabel() {
349
349
  const resolved = resolve(suffixes, fb);
350
350
  return resolved || undefined;
351
351
  },
352
+ /**
353
+ * Resolve a translated copy of an action's post-success RESULT DIALOG
354
+ * (`title` / `description` / `acknowledge` + per-field labels).
355
+ * Convention: `{ns}.objects.{objectName}._actions.{actionName}.resultDialog.*`,
356
+ * falling back to `{ns}.globalActions.{actionName}.resultDialog.*` when
357
+ * objectName is omitted, then to the metadata's literal strings.
358
+ *
359
+ * The `fields` translation node is keyed by the LITERAL result-field path
360
+ * from the action metadata (may contain dots, e.g. `"user.email"`), so it
361
+ * is fetched whole with `returnObjects` and indexed directly — never
362
+ * resolved through a dotted i18next key.
363
+ */
364
+ actionResultDialog: (objectName, actionName, spec) => {
365
+ if (!spec || !actionName)
366
+ return spec;
367
+ const suffixesFor = (attr) => {
368
+ const tail = `_actions.${actionName}.resultDialog.${attr}`;
369
+ return objectName
370
+ ? objectSuffixes(objectName, tail)
371
+ : [`globalActions.${actionName}.resultDialog.${attr}`];
372
+ };
373
+ const textFor = (attr) => {
374
+ const resolved = resolve(suffixesFor(attr), spec[attr] ?? '');
375
+ return resolved || spec[attr];
376
+ };
377
+ const fieldsMap = (() => {
378
+ try {
379
+ for (const ns of getAppNamespaces()) {
380
+ for (const suffix of suffixesFor('fields')) {
381
+ const node = t(`${ns}.${suffix}`, {
382
+ returnObjects: true,
383
+ defaultValue: null,
384
+ [I18N_PROBE_FLAG]: true,
385
+ });
386
+ if (node && typeof node === 'object' && !Array.isArray(node)) {
387
+ return node;
388
+ }
389
+ }
390
+ }
391
+ }
392
+ catch {
393
+ // Graceful degradation when i18n provider is not available
394
+ }
395
+ return undefined;
396
+ })();
397
+ const fields = Array.isArray(spec.fields)
398
+ ? spec.fields.map((field) => {
399
+ if (!field || typeof field.path !== 'string')
400
+ return field;
401
+ const label = fieldsMap?.[field.path];
402
+ return typeof label === 'string' && label.length > 0 ? { ...field, label } : field;
403
+ })
404
+ : spec.fields;
405
+ return {
406
+ ...spec,
407
+ title: textFor('title'),
408
+ description: textFor('description'),
409
+ acknowledge: textFor('acknowledge'),
410
+ fields,
411
+ };
412
+ },
352
413
  /**
353
414
  * Resolve translated action-PARAMETER text (label / placeholder / helpText).
354
415
  * Convention: `{ns}.objects.{objectName}._actions.{actionName}.params.{paramName}.{attr}`.
@@ -402,15 +463,10 @@ const SAFE_FIELD_LABEL_FALLBACK = {
402
463
  * may be rendered outside an i18n context.
403
464
  */
404
465
  export function useSafeFieldLabel() {
405
- let resolved = null;
406
- try {
407
- resolved = useObjectLabel();
408
- }
409
- catch {
410
- resolved = null;
411
- }
412
- // useObjectLabel already returns a stable (memoized) object per language —
413
- // pass-through avoids creating a fresh wrapper here. The module-level
414
- // fallback object is reused when no provider is mounted.
415
- return resolved ?? SAFE_FIELD_LABEL_FALLBACK;
466
+ // useObjectLabel delegates to the provider-safe useObjectTranslation (react-
467
+ // i18next falls back to the global instance and never throws), so it needs no
468
+ // try/catch — wrapping the hook call would violate rules-of-hooks. It already
469
+ // returns a stable memoized object; the module-level fallback stays as a
470
+ // defensive default, reached only if it ever returns nullish.
471
+ return useObjectLabel() ?? SAFE_FIELD_LABEL_FALLBACK;
416
472
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/i18n",
3
- "version": "16.0.0",
3
+ "version": "16.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "i18next": "^26.3.6",
34
- "react-i18next": "^17.0.9"
34
+ "react-i18next": "^17.0.10"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": "^18.0.0 || ^19.0.0"