@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
@@ -17,7 +17,7 @@ const en = {
17
17
  },
18
18
  },
19
19
  // objectui#2600 B5 — capability picker scope group headers (labels come from
20
- // the sys_capability registry; only these group titles are UI strings).
20
+ // the capability registry; only these group titles are UI strings).
21
21
  capability: {
22
22
  group: {
23
23
  platform: 'Platform',
@@ -30,38 +30,13 @@ const en = {
30
30
  manage_users: 'Manage Users',
31
31
  manage_org_users: 'Manage Organization Users',
32
32
  manage_metadata: 'Manage Metadata',
33
+ manage_mailboxes: 'Manage Mailboxes',
33
34
  manage_platform_settings: 'Manage Platform Settings',
34
35
  setup_access: 'Setup Access',
35
36
  setup_write: 'Write Settings',
36
37
  studio_access: 'Studio Access',
37
- },
38
- },
39
- // objectui#3546 slice six — the read-only facet summary + Studio deep-link a
40
- // `sys_permission_set` record shows for its six authorization facets
41
- // (ADR-0056 P1, plugin-detail's PermissionFacetLink).
42
- //
43
- // The four count labels are plural families. `_one` is the singular; the BASE
44
- // key (no suffix) is the form every OTHER CLDR plural category resolves to,
45
- // which is what keeps `ru` (few/many) and `ar` (two/few/many/zero) in their own
46
- // language instead of falling through to English — i18next only looks up the
47
- // one suffix a language's rules ask for, and falls back to the base key when
48
- // that suffix is absent. `perm-home-namespace-3546.test.tsx` renders every
49
- // language at counts 1/2/3/5/11/21/100 to hold this.
50
- perm: {
51
- facet: {
52
- none: 'None',
53
- more: '+{{count}} more',
54
- objects: '{{count}} objects',
55
- objects_one: '{{count}} object',
56
- fields: '{{count}} field rules',
57
- fields_one: '{{count}} field rule',
58
- rls: '{{count}} RLS policies',
59
- rls_one: '{{count}} RLS policy',
60
- tabs: '{{count}} tab rules',
61
- tabs_one: '{{count}} tab rule',
62
- adminScope: 'Delegated admin configured',
63
- designInStudio: 'Design in Studio →',
64
- designInStudioHint: 'Design in Studio',
38
+ manage_sharing: 'Manage Sharing',
39
+ view_all_audit_log: 'View All Audit Log',
65
40
  },
66
41
  },
67
42
  lookup: {
@@ -80,7 +55,8 @@ const en = {
80
55
  selectFirst: 'Select {{fields}} first',
81
56
  selectRecord: 'Select record',
82
57
  recordCount: '{{count}} records',
83
- recordCountOne: '1 record',
58
+ recordCount_one: '{{count}} record',
59
+ recordCount_other: '{{count}} records',
84
60
  pageOf: 'Page {{current}} of {{total}}',
85
61
  filters: 'Filters',
86
62
  clear: 'Clear',
@@ -137,17 +113,30 @@ const en = {
137
113
  closeChat: 'Close chat',
138
114
  closePanel: 'Close panel',
139
115
  resizeDrawer: 'Resize drawer',
140
- // Count badge on a tab / section header. Two keys, NOT an i18next
141
- // `_one`/`_other` pair — see the `reactionCount` note under `detail`.
116
+ // Count badge on a tab / section header. An i18next count family
117
+ // (objectui#11445): the badge passes `count` and i18next picks the slot
118
+ // each pack's CLDR rules select.
142
119
  itemCount: '{{count}} items',
143
- itemCountOne: '{{count}} item',
120
+ itemCount_one: '{{count}} item',
121
+ itemCount_other: '{{count}} items',
144
122
  toggleSidebar: 'Toggle sidebar',
123
+ breadcrumb: 'Breadcrumb',
145
124
  package: 'Package',
146
125
  done: 'Done',
147
126
  editInStudio: 'Edit in studio',
148
127
  record: 'Record',
149
128
  retry: 'Retry',
150
129
  printDialogHint: 'Opens your browser’s print dialog (not a PDF export)',
130
+ // The non-grid row ceiling's footnote (objectui#7210). Two keys because
131
+ // there are two conditions: a reported `total` states the fact with BOTH
132
+ // numbers, a missing one cannot name how many. Kept terse deliberately —
133
+ // this copy is eagerly loaded, and since objectui#7399 these bytes are
134
+ // budgeted by the `i18n-locales` chunk, not `framework`. ⛔ That ceiling's
135
+ // headroom is deliberately NOT restated here — it moves on every
136
+ // re-baseline, and the figure that was here went stale.
137
+ // `pnpm check:eager-closure` prints it.
138
+ rowCeilingNote: 'Showing the first {{shown}} of {{total}} records. Narrow the filter.',
139
+ rowCeilingNoteUnknownTotal: 'Showing the first {{shown}} records. Narrow the filter.',
151
140
  },
152
141
  actions: {
153
142
  decisionOutput: {
@@ -159,6 +148,38 @@ const en = {
159
148
  acknowledge: 'I have saved this',
160
149
  copyAll: 'Copy all',
161
150
  },
151
+ // The refusal notice for an action whose `autoTrigger` its own declared
152
+ // `visible` gate outranks (objectui#4191) — the deep link or host asked
153
+ // for it, but the author hid it on this surface.
154
+ notAvailableHere: '"{{action}}" is not available on the current page.',
155
+ // The reason a record action greyed out by its declared `disabled`
156
+ // predicate gives on hover, on focus and as its accessible description
157
+ // (objectui#11811): the generic one, since the action spec carries no
158
+ // author-written reason. Not `notAvailableHere` — that one is about the
159
+ // page, this one about the record.
160
+ notAvailableForRecord: 'Not available for this record',
161
+ // The success toast the action runner shows when no `outcomeMessages`
162
+ // entry applies to the answer and the action declares no `successMessage`;
163
+ // the server's message plays no part (objectui#11344). It is the one
164
+ // success-toast text the runner writes itself (objectui#10900).
165
+ completedSuccessfully: 'Action completed successfully',
166
+ // The runner's other own text (objectui#10969): the error fallbacks when no
167
+ // readable error message reached it, and the Undo label it hands the toast
168
+ // handler for an undoable success toast.
169
+ failed: 'Action failed',
170
+ parallelFailed: 'One or more parallel actions failed',
171
+ undo: 'Undo',
172
+ // The confirmation toast after that Undo has run — written by the console's
173
+ // two undo handlers (`useConsoleActionRuntime` and `RecordDetailView`), not
174
+ // by the runner (objectui#11056).
175
+ undone: 'Change undone',
176
+ // The console's global Ctrl+Z / Ctrl+Shift+Z toasts, raised by
177
+ // `AppContent`'s `useGlobalUndo` handlers once the operation has been
178
+ // undone / redone (objectui#11080). `{{description}}` is the operation's
179
+ // own description, passed through unchanged; the pack owns the words and
180
+ // the word order around it.
181
+ undoneOperation: 'Undo: {{description}}',
182
+ redoneOperation: 'Redo: {{description}}',
162
183
  },
163
184
  validation: {
164
185
  required: '{{field}} is required',
@@ -181,8 +202,26 @@ const en = {
181
202
  },
182
203
  form: {
183
204
  noPermissionToSave: "You don't have permission to save this record.",
205
+ // The notice a record form shows when every field is locked because the
206
+ // object's affordance for the form's mode is closed for this user
207
+ // (objectui#11000). `{{object}}` is the object's label.
208
+ noPermissionToCreate: "You don't have permission to create {{object}} records. The fields are read-only.",
209
+ noPermissionToEdit: "You don't have permission to edit {{object}} records. The fields are read-only.",
184
210
  submitFailed: 'Could not save. Please try again.',
185
- addItem: 'Add item',
211
+ uploadInFlight: 'Wait for the upload to finish before saving.',
212
+ // The notice the record form raises when a field's own `visibleWhen` turns
213
+ // it invisible and the form clears the value it held (objectui#8070 names
214
+ // the `6a449fc49` clear). `{{fields}}` is the cleared fields' labels joined
215
+ // with `validation.formInvalidJoiner`. A list after a colon, so no word has
216
+ // to agree with how many fields were cleared.
217
+ clearedOnHide: 'Cleared — no longer applicable given the current values: {{fields}}',
218
+ // The refusal a record form raises at SUBMIT when a field's own
219
+ // `visibleWhen` could not be evaluated (objectui#8069, ADR-0137 D2 as ruled:
220
+ // the client refuses the one rule no server judges). It names the rule and
221
+ // the fields — `{{fields}}` is their labels joined with
222
+ // `validation.formInvalidJoiner` — because the fix is to the metadata, not
223
+ // to anything the person filling the form can type.
224
+ visibleWhenFaulted: "Can't submit: the visibleWhen rule of {{fields}} could not be evaluated. The rule must be fixed before this form can be submitted.",
186
225
  removeItem: 'Remove item',
187
226
  fieldRequired: 'This field is required',
188
227
  invalidFormat: 'Invalid format',
@@ -197,6 +236,66 @@ const en = {
197
236
  // only displace it by authoring a `description`, which makes a VISIBLE
198
237
  // subtitle appear on every form — so the fallback has to come from here.
199
238
  dialogDescriptionFallback: 'Complete the form fields, then submit or cancel.',
239
+ // The hint under a field the caller may read but not write, shown when the
240
+ // field declares no description of its own (objectui#11071).
241
+ deniedDescription: 'You do not have edit access to this field.',
242
+ // The hint under an empty field on a create form whose default the server
243
+ // resolves at insert — a CEL expression or a runtime token the form cannot
244
+ // evaluate, so it leaves the field empty and omits it (objectui#12108).
245
+ // Shown only while the field is empty: a typed value is saved instead.
246
+ serverDefaultHint: 'Set automatically when saved.',
247
+ // The size-limit hint the flat `ObjectForm` appends to a `file` or `image`
248
+ // field's description when the field declares `maxSize` (objectui#12156).
249
+ // `{{size}}` is the formatted limit ("10.0 MB"); only the words translate.
250
+ maxFileSize: 'Max size: {{size}}',
251
+ // The master-detail form's own chrome (objectui#11071): the collection
252
+ // placeholder while its columns resolve, the document totals stack
253
+ // (`{{rate}}` is the header's tax rate), the row editor's title
254
+ // (`{{title}}` is the collection's authored title, or `lineItem` when it
255
+ // has none; `{{row}}` is the 1-based row number), its Apply button, and the
256
+ // sr-only description of the dialog that hosts the form. The verbs `Add`,
257
+ // `Close` and `Saving…` come from `detail.add`, `common.close` and
258
+ // `detail.saving`.
259
+ masterDetail: {
260
+ loadingColumns: 'Loading columns…',
261
+ subtotal: 'Subtotal',
262
+ tax: 'Tax ({{rate}}%)',
263
+ total: 'Total',
264
+ lineItem: 'Line item',
265
+ rowTitle: '{{title}} — row {{row}}',
266
+ applyRow: 'Apply',
267
+ editorDescription: 'Enter the record and its line items, then save.',
268
+ // A collection's configuration hints (objectui#11160): no `childObject`
269
+ // (the twin of `lineItems.noChildObject`, which names the panel), a
270
+ // child schema that failed to load, and no lookup or master_detail
271
+ // field on the child that references the parent. `{{property}}` is a
272
+ // property name and `{{object}}` / `{{parent}}` are object names; each
273
+ // is rendered as code and never translated. `lookup` and
274
+ // `master_detail` are field type names, so every pack keeps them as
275
+ // written.
276
+ noChildObject: 'This collection has no child object configured: set {{property}} to the object whose rows it lists.',
277
+ schemaUnavailable: 'Could not load the schema of {{object}}, so this collection has no columns to show. Check that the object exists and is readable, then reload.',
278
+ noRelationshipField: 'Could not work out how {{object}} links to {{parent}}: no lookup or master_detail field on it references the parent. Set {{property}} on this collection to the field that holds the parent record.',
279
+ },
280
+ // The record page's `record:line_items` panel (objectui#11131): its
281
+ // title when the author declared none. Its Save / Saving… button reads
282
+ // `common.save` / `detail.saving`. `MasterDetailForm` reads `title` too,
283
+ // for a collection that authors no heading (objectui#11145).
284
+ //
285
+ // The rest is the panel's own states (objectui#11145): no parent record
286
+ // bound yet, rows held for another parent, and the two fallbacks for a
287
+ // failed load or save, shown when the server sent no message of its own.
288
+ // Its loading line reads `common.loading`. `noChildObject` is the config
289
+ // hint for a panel with no `childObject`: `{{property}}` is filled with
290
+ // the property name, rendered as code and never translated.
291
+ lineItems: {
292
+ title: 'Line Items',
293
+ saveRecordFirst: 'Save the record first to add line items.',
294
+ notLoaded: 'This record’s line items have not been loaded.',
295
+ loadFailed: 'Failed to load line items',
296
+ saveFailed: 'Failed to save line items',
297
+ noChildObject: 'This panel has no child object configured: set {{property}} to the object whose rows it lists.',
298
+ },
200
299
  keepEditing: 'Keep editing',
201
300
  discard: 'Discard',
202
301
  conflictTitle: 'Save conflict',
@@ -214,6 +313,26 @@ const en = {
214
313
  createSuccess: '{{object}} created successfully',
215
314
  updateSuccess: '{{object}} updated successfully',
216
315
  deleteSuccess: '{{object}} deleted successfully',
316
+ // plugin-form's own feedback chrome (objectui#11039): the success toast
317
+ // a form raises when the author declared no `successMessage` (`created`
318
+ // after a create, `saved` after an edit), the master-detail form's edit
319
+ // toast when the form has a `title` (`{{title}}` is that title), and the
320
+ // console form page's toast after a submit. An authored message always
321
+ // wins; these are only the defaults. The form's loading line and its
322
+ // thank-you panel read `publicForm.loading` / `publicForm.thankYouTitle` /
323
+ // `publicForm.thankYouMessage`, which say the same thing.
324
+ created: 'Created',
325
+ saved: 'Saved',
326
+ savedNamed: '{{title}} saved',
327
+ submitted: 'Submitted',
328
+ // The heading of the panel a form shows when its object schema or its
329
+ // record could not be loaded; the error's own message follows it.
330
+ errorLoading: 'Error loading form',
331
+ // Rides the success toast when a DECLARED `navigateOnSuccess` produced no
332
+ // destination (objectui#5034): the write succeeded, the declared
333
+ // navigation did not happen. `navigateOnSuccess` is the metadata key's
334
+ // name, so every pack keeps it as written.
335
+ navigateRefused: 'The `navigateOnSuccess` destination declared for this form was refused, so the navigation did not happen.',
217
336
  fullscreen: {
218
337
  title: 'Edit text',
219
338
  description: 'Edit the full text value, then save or cancel your changes.',
@@ -225,6 +344,8 @@ const en = {
225
344
  fields: {
226
345
  relativeDate: {
227
346
  overdue: 'Overdue {{count}}d',
347
+ overdue_one: 'Overdue {{count}}d',
348
+ overdue_other: 'Overdue {{count}}d',
228
349
  },
229
350
  file: {
230
351
  dragDropHere: 'Drag & drop files here',
@@ -241,6 +362,9 @@ const en = {
241
362
  remove: 'Remove {{name}}',
242
363
  exceedsMaxSize: '"{{name}}" exceeds max size ({{max}} MB)',
243
364
  uploadFailed: 'Failed to upload "{{name}}": {{error}}',
365
+ uploadIncomplete: 'Upload of "{{name}}" did not complete: no file id was returned, so nothing was saved',
366
+ restricted: 'Restricted file',
367
+ restrictedHint: 'You do not have access to the details of this file',
244
368
  },
245
369
  image: {
246
370
  upload: 'Upload image',
@@ -251,10 +375,14 @@ const en = {
251
375
  remove: 'Remove image {{index}}',
252
376
  enlarge: 'Enlarge {{name}}',
253
377
  preview: 'Image preview',
378
+ cropPreview: 'Image crop preview',
254
379
  previous: 'Previous image',
255
380
  next: 'Next image',
256
381
  counter: '{{current}} / {{total}}',
257
382
  },
383
+ percent: {
384
+ slider: 'Percentage',
385
+ },
258
386
  richText: {
259
387
  format: 'Format: {{format}}',
260
388
  basicEditorHint: 'Rich text editor (basic)',
@@ -274,6 +402,10 @@ const en = {
274
402
  search: 'Search objects…',
275
403
  empty: 'No objects found',
276
404
  },
405
+ registryRef: {
406
+ loadFailed: 'The list could not be loaded. You can still type the name.',
407
+ notFound: '{{name}} (not found)',
408
+ },
277
409
  recipient: {
278
410
  selectTypeFirst: 'Select a recipient type first.',
279
411
  loading: 'Loading…',
@@ -285,6 +417,9 @@ const en = {
285
417
  selectBusinessUnit: 'Select a business unit',
286
418
  selectPosition: 'Select a position',
287
419
  selectUnitAndSubordinates: 'Select a business unit',
420
+ selectField: 'Select a user field',
421
+ noUserFields: 'No user fields on this object',
422
+ fieldNotUserTyped: '{{name}} — not a user field',
288
423
  },
289
424
  filterCondition: {
290
425
  selectObjectFirst: 'Select an object first.',
@@ -295,6 +430,93 @@ const en = {
295
430
  editAsJson: 'Edit as JSON',
296
431
  useVisualBuilder: 'Use visual builder',
297
432
  },
433
+ // objectui#6755 — a widget's OWN refusal sentence: `ObjectField`'s
434
+ // unparsable JSON draft, and `LocationField`'s format and range refusals
435
+ // (objectui#6716 / #6714). They were string literals in the widgets, so a
436
+ // zh / ja / ar user who mistyped a coordinate or a JSON blob was told why in
437
+ // English inside a form whose every other word was translated.
438
+ //
439
+ // KEYED, where objectui#4028 DROPPED the five address placeholders in the
440
+ // namespace below — and the difference is what that decision's own comment
441
+ // records about them: "The right example is a function of the address's
442
+ // COUNTRY, not the reader's language", and "Each box here already has a
443
+ // visible label naming exactly what it wants". Neither reaches a refusal
444
+ // sentence: nothing else on the screen says why the edit was refused, and
445
+ // what that sentence must say IS a function of the reader's language. What
446
+ // #4028 does establish for both is the COST — ten pack entries per key,
447
+ // bound from then on by `check:i18n-drift` — and the 2026-08-29 maintainer
448
+ // ruling accepted that cost for these three sentences.
449
+ //
450
+ // Values are byte-identical to the literals they replace (`FIELD_DEFAULTS`
451
+ // in `packages/fields/src/widgets/useFieldTranslation.ts` carries the same
452
+ // three), so English and provider-less rendering are unchanged.
453
+ //
454
+ // `{{detail}}` is the SPEC's own complaint about the refused pair.
455
+ // `LocationField` refuses to restate `LocationValueSchema`'s bounds — a
456
+ // hand-copied range is a second contract — so this key translates the frame
457
+ // and interpolates whatever the schema said.
458
+ object: {
459
+ invalidJson: 'Invalid JSON',
460
+ },
461
+ location: {
462
+ refusedFormat: 'Not saved: enter a latitude, longitude pair (example: 30.2741, 120.1551).',
463
+ refusedRange: 'Not saved: {{detail}}',
464
+ latitude: 'latitude',
465
+ longitude: 'longitude',
466
+ refusedResidueOne: 'Not saved: {{name}} "{{text}}" is not a number. Enter plain decimals (example: 30.2741, 120.1551).',
467
+ refusedResidue: 'Not saved: {{name}} "{{text}}" and {{otherName}} "{{otherText}}" are not numbers. Enter plain decimals (example: 30.2741, 120.1551).',
468
+ },
469
+ // objectui#8148 — the FIFTH refusal sentence of objectui#6755's class, and
470
+ // the only SHARED one. The four above are each ONE widget's; this one is
471
+ // produced by `numberBadInput.tsx` for `NumberField`, `CurrencyField`,
472
+ // `PercentField` and both of `GeolocationField`'s boxes. That last is the
473
+ // sharpest case: it sits beside `LocationField`, whose refusals are keyed
474
+ // right above, so two adjacent coordinate widgets refused bad input in two
475
+ // different languages on the same form.
476
+ //
477
+ // ⭐ `{{example}}` is a HOLE, and that is what keeps decimal numerals out
478
+ // of every pack. Five different values reach this one sentence (`1234`,
479
+ // `1234.56`, `12.5`, `30.2741`, `120.1551`), so keying the example per
480
+ // widget would be five keys times ten packs — and each pack would then
481
+ // hold a decimal it could legitimately re-punctuate, where `1234,56` reads
482
+ // as the `latitude, longitude` PAIR `refusedFormat` above asks for. The
483
+ // widget fills the hole in ASCII; no pack spells a digit.
484
+ //
485
+ // The English value is byte-identical to the literal it replaces
486
+ // (`FIELD_DEFAULTS` in `packages/fields/src/widgets/useFieldTranslation.ts`
487
+ // carries the same one), so English and provider-less rendering are
488
+ // unchanged.
489
+ // objectui#10567 — the editable date-only face's notice for a stored value
490
+ // written on a day that does not exist, the date-only sibling of
491
+ // `dateTime.impossibleDay` below: an `<input type="date">` can only paint
492
+ // such a value blank, so the widget names the stored string beside it.
493
+ // `FIELD_DEFAULTS` in `packages/fields/src/widgets/useFieldTranslation.ts`
494
+ // carries the same English value for provider-less rendering.
495
+ date: {
496
+ impossibleDay: 'The stored value "{{value}}" is not a real date. Pick a date to replace it.',
497
+ },
498
+ // objectui#10474 — the editable date-time face's notice for a stored value
499
+ // written on a day that does not exist. The `datetime-local` control can
500
+ // only paint such a value blank, so the widget names the stored string
501
+ // beside it; a blank control alone would hide it (objectui#3127).
502
+ // `FIELD_DEFAULTS` in `packages/fields/src/widgets/useFieldTranslation.ts`
503
+ // carries the same English value for provider-less rendering.
504
+ dateTime: {
505
+ impossibleDay: 'The stored value "{{value}}" is not a real date. Pick a date and time to replace it.',
506
+ },
507
+ // objectui#12622 — the record form's date picker: its two shortcuts,
508
+ // the clear / calendar buttons' accessible names, and the notice for
509
+ // typed text that names no date ({{format}} is the locale pattern).
510
+ datePicker: {
511
+ today: 'Today',
512
+ tomorrow: 'Tomorrow',
513
+ clear: 'Clear date',
514
+ openCalendar: 'Open calendar',
515
+ invalid: 'Not saved: "{{value}}" is not a date. Type it as {{format}}, or pick one from the calendar.',
516
+ },
517
+ number: {
518
+ badInput: 'Not saved: the text in this box is not a number. Enter a plain decimal (example: {{example}}).',
519
+ },
298
520
  // objectui#3342 — the tags widget's input hint, shown while the tag list
299
521
  // is empty. The author-declared `field.placeholder` always wins over this.
300
522
  tags: {
@@ -341,10 +563,39 @@ const en = {
341
563
  // to plural lookup before the base key, and these packs declare the base
342
564
  // key only; a sentence whose grammar does not bend on the number is
343
565
  // correct at 1 without ten plural entries per pack.
566
+ // objectui#11689 — `BooleanCellRenderer`'s own words (every list
567
+ // surface draws them): the badge a status-named column (`active`,
568
+ // `enabled`, …) shows for `false`, where `{{label}}` is the column label
569
+ // the face is handed, and the completion indicator's accessible names.
570
+ boolean: {
571
+ offBadge: '{{label}} — Off',
572
+ completed: 'Completed',
573
+ notCompleted: 'Not completed',
574
+ },
344
575
  textarea: {
345
576
  characterCount: 'Character count: {{count}} of {{max}}',
346
577
  charactersRemaining: 'Characters remaining: {{count}}',
347
578
  },
579
+ // objectui#11131 — the line-items grid's default chrome (`GridField`):
580
+ // its Add button, the read-only grid's empty state, and the list-mode
581
+ // grid's empty state. An authored `addLabel` still wins over `addLine`,
582
+ // and it fills the `{{label}}` hole (`detail.add` when none is authored).
583
+ //
584
+ // objectui#11145 — the rest of that chrome: the column chooser's heading,
585
+ // the computed cell's tooltip, and the row actions. Each row action has
586
+ // one key, read by both its `aria-label` and its `title`. The chooser's
587
+ // button reads `table.columns`, the footer `form.masterDetail.total` and
588
+ // the drag handle `view.dragToReorder`.
589
+ grid: {
590
+ addLine: 'Add line',
591
+ noItems: 'No items',
592
+ noItemsAddHint: 'No items yet — click “{{label}}” to begin.',
593
+ optionalColumns: 'Optional columns',
594
+ computed: 'Computed',
595
+ openRow: 'Open row',
596
+ duplicateRow: 'Duplicate row',
597
+ removeRow: 'Remove row',
598
+ },
348
599
  },
349
600
  table: {
350
601
  rowsPerPage: 'Rows per page',
@@ -360,10 +611,15 @@ const en = {
360
611
  selectAllRows: 'Select all rows',
361
612
  expandRow: 'Expand row',
362
613
  collapseRow: 'Collapse row',
614
+ rowActions: 'Row actions',
363
615
  hideColumn: 'Hide column',
364
616
  freezeColumn: 'Freeze column',
365
617
  unfreezeColumn: 'Unfreeze column',
366
618
  pageInfo: 'Page {{current}} of {{total}}',
619
+ firstPage: 'First page',
620
+ previousPage: 'Previous page',
621
+ nextPage: 'Next page',
622
+ lastPage: 'Last page',
367
623
  totalRecords: '{{count}} total',
368
624
  noResults: 'No results found',
369
625
  noResultsHint: 'Try adjusting your filters or search query.',
@@ -373,8 +629,15 @@ const en = {
373
629
  open: 'Open',
374
630
  search: 'Search…',
375
631
  modified: '{{count}} row modified',
632
+ modified_one: '{{count}} row modified',
633
+ modified_other: '{{count}} rows modified',
634
+ saved: '{{count}} row saved',
635
+ saved_one: '{{count}} row saved',
636
+ saved_other: '{{count}} rows saved',
376
637
  saveFailed: 'Save failed',
377
638
  selected: '{{count}} selected',
639
+ selected_one: '{{count}} selected',
640
+ selected_other: '{{count}} selected',
378
641
  edit: 'Edit',
379
642
  delete: 'Delete',
380
643
  },
@@ -389,18 +652,32 @@ const en = {
389
652
  pullToRefresh: 'Pull to refresh',
390
653
  refreshing: 'Refreshing…',
391
654
  openRecord: 'Open record',
655
+ recordUnavailable: '{{object}} · record unavailable',
392
656
  openMenu: 'Open menu',
393
657
  bulkSelected: '{{count}} selected',
658
+ bulkSelected_one: '{{count}} selected',
659
+ bulkSelected_other: '{{count}} selected',
394
660
  bulkSelectedAllMatches: '{{count}} selected (all matches)',
395
661
  bulkClear: 'Clear',
396
662
  bulkAllOnPage: 'All {{count}} on this page are selected.',
663
+ bulkAllOnPage_one: '{{count}} record on this page is selected.',
664
+ bulkAllOnPage_other: 'All {{count}} on this page are selected.',
397
665
  bulkSelectAllMatching: 'Select all {{count}} matching',
398
666
  bulkAllMatchingSelected: 'All {{count}} matching records are selected.',
667
+ bulkActions: 'Bulk actions',
399
668
  rowHeight: 'Row height: {{mode}}',
400
669
  empty: 'Empty',
401
670
  yes: 'Yes',
402
671
  no: 'No',
403
672
  systemFields: 'System',
673
+ // objectui#10881 — the grouped grid's two refusals: over a data source
674
+ // that cannot answer the group header query, and over rows a host
675
+ // handed in while declaring them one page of more. `queryGroupHeaders`
676
+ // is the member's name and stays untranslated.
677
+ grouping: {
678
+ needsHeaderQuery: 'This view is grouped, but its data source does not implement queryGroupHeaders, so the groups cannot be counted. Remove the grouping to show the records.',
679
+ needsWholeRows: 'Grouping needs every record, but this grid was handed one page of them, so the groups cannot be counted. Hand in every record, or let the grid fetch them from a data source that implements queryGroupHeaders.',
680
+ },
404
681
  // Column-footer aggregate prefixes, keyed by the spec's `ColumnSummary`
405
682
  // vocabulary (objectui#4024). The footer already formatted its NUMBER
406
683
  // through the locale-aware formatter and then put a hardcoded English
@@ -411,11 +688,18 @@ const en = {
411
688
  // right-to-left — so a pack owns the whole shape. Same reasoning as
412
689
  // `collaboration.resolvedSuffix` below.
413
690
  //
691
+ // `pagePattern` is `pattern` for a figure that covers only the page on
692
+ // screen of a larger matched set — the footer's fallback when the server
693
+ // cannot answer the whole set's figure (objectui#12081 item 3). A whole
694
+ // pattern rather than a suffix, for the same reason: where the scope sits
695
+ // is the pack's.
696
+ //
414
697
  // `countEmpty`/`percentEmpty` (and the filled pair) deliberately share a
415
698
  // word: the trailing `%` is what tells the two families apart on screen,
416
699
  // exactly as the renderer's own comment says.
417
700
  summary: {
418
701
  pattern: '{{label}}: {{value}}',
702
+ pagePattern: '{{label}} (this page): {{value}}',
419
703
  count: 'Count',
420
704
  countEmpty: 'Empty',
421
705
  countFilled: 'Filled',
@@ -446,8 +730,10 @@ const en = {
446
730
  dragDrop: 'Drag & drop a CSV or Excel file here, or click to browse',
447
731
  browseFiles: 'Browse Files',
448
732
  downloadTemplate: 'Download template',
449
- downloadTemplateHint: 'Get a CSV with the right columns (required fields marked *).',
733
+ downloadTemplateHint: 'Get an Excel file of the columns you can import (required fields marked *), with instructions for filling it in.',
450
734
  templateFileName: '{{object}}-import-template',
735
+ templateDownloadFailed: 'Could not download the template. Please try again.',
736
+ templateNotPermitted: 'You do not have permission to create records of this object, so its import template is not available.',
451
737
  parsing: 'Parsing…',
452
738
  pasteHint: 'or paste (Ctrl/⌘+V) rows copied from Excel or Google Sheets',
453
739
  legacyXls: 'Legacy .xls files aren\'t supported — please re-save as .xlsx.',
@@ -473,6 +759,8 @@ const en = {
473
759
  typeMismatch: 'Looks like {{type}}',
474
760
  autoMatched: 'Auto-matched',
475
761
  autoMatchedSummary: 'Auto-matched {{count}} column(s) — review and adjust below.',
762
+ autoMatchedSummary_one: 'Auto-matched {{count}} column — review and adjust below.',
763
+ autoMatchedSummary_other: 'Auto-matched {{count}} columns — review and adjust below.',
476
764
  confidence: {
477
765
  high: 'High confidence',
478
766
  medium: 'Medium confidence',
@@ -491,7 +779,11 @@ const en = {
491
779
  mapped: 'Mapped',
492
780
  skipped: 'Skipped',
493
781
  rowsWithErrors: '{{count}} row(s) with errors',
782
+ rowsWithErrors_one: '{{count}} row with errors',
783
+ rowsWithErrors_other: '{{count}} rows with errors',
494
784
  rowsCorrected: '{{count}} row(s) corrected',
785
+ rowsCorrected_one: '{{count}} row corrected',
786
+ rowsCorrected_other: '{{count}} rows corrected',
495
787
  clickToFix: '— click a highlighted cell to fix it inline.',
496
788
  showingRows: 'Showing {{shown}} of {{total}} rows',
497
789
  importing: 'Importing… {{progress}}%',
@@ -502,12 +794,22 @@ const en = {
502
794
  cancelImport: 'Cancel import',
503
795
  importCancelled: 'Import cancelled',
504
796
  resultsTruncated: 'Showing the first {{count}} row results (of {{total}}).',
797
+ resultsTruncated_one: 'Showing the first {{count}} row result (of {{total}}).',
798
+ resultsTruncated_other: 'Showing the first {{count}} row results (of {{total}}).',
505
799
  importComplete: 'Import Complete',
800
+ importPartial: 'Import finished with errors',
801
+ importFailed: 'Import failed',
506
802
  imported: '{{count}} imported',
507
803
  createdCount: '{{count}} created',
804
+ createdCount_one: '{{count}} created',
805
+ createdCount_other: '{{count}} created',
508
806
  updatedCount: '{{count}} updated',
807
+ updatedCount_one: '{{count}} updated',
808
+ updatedCount_other: '{{count}} updated',
509
809
  skippedCount: '{{count}} skipped',
510
810
  moreErrors: '…and {{count}} more errors',
811
+ moreErrors_one: '…and {{count}} more error',
812
+ moreErrors_other: '…and {{count}} more errors',
511
813
  downloadFailed: 'Download failed rows',
512
814
  options: 'Import options',
513
815
  writeMode: 'When a row matches an existing record',
@@ -522,9 +824,6 @@ const en = {
522
824
  needMatchFields: 'Select at least one field to match on.',
523
825
  matchOnlyField: '(match only)',
524
826
  optCreateOptions: 'Keep unknown option values',
525
- optRunAutomations: 'Run automations & triggers',
526
- optTreatHistorical: 'Import as historical data',
527
- optTreatHistoricalHint: '(import completed records as-is — skip state-machine checks and keep their original timestamps & author instead of stamping now)',
528
827
  optSkipBlankKey: 'Skip rows with a blank match value',
529
828
  optBackground: 'Import in the background',
530
829
  optBackgroundHint: '(runs as an undoable job)',
@@ -539,6 +838,7 @@ const en = {
539
838
  invalidBoolean: '"{{value}}" is not a valid true/false value',
540
839
  invalidNumber: '"{{value}}" is not a valid number',
541
840
  invalidDate: '"{{value}}" is not a valid date',
841
+ invalidTime: '"{{value}}" is not a valid time',
542
842
  invalidOption: '"{{value}}" is not one of the allowed options',
543
843
  requiredValue: 'This field is required',
544
844
  matchAmbiguous: 'Matches more than one existing record — use a unique value or the record id',
@@ -555,10 +855,15 @@ const en = {
555
855
  historyColResult: 'Result',
556
856
  historyColTime: 'When',
557
857
  errorCount: '{{count}} errors',
858
+ errorCount_one: '{{count}} error',
859
+ errorCount_other: '{{count}} errors',
558
860
  undoImport: 'Undo import',
559
861
  undoing: 'Undoing…',
560
862
  undoConfirm: 'Undo this import? Records it created will be deleted and records it updated will be restored to their previous values.',
561
863
  reverted: 'Undone',
864
+ undoPartial: '{{count}} rows were not undone. Undo again to retry only those rows.',
865
+ undoPartial_one: '{{count}} row was not undone. Undo again to retry only that row.',
866
+ undoPartial_other: '{{count}} rows were not undone. Undo again to retry only those rows.',
562
867
  jobStatus: {
563
868
  pending: 'Pending',
564
869
  running: 'Running',
@@ -571,6 +876,8 @@ const en = {
571
876
  next: 'Next',
572
877
  close: 'Close',
573
878
  importNRows: 'Import {{count}} Rows',
879
+ importNRows_one: 'Import {{count}} Row',
880
+ importNRows_other: 'Import {{count}} Rows',
574
881
  importingProgress: 'Importing…',
575
882
  required: 'Required',
576
883
  invalidType: 'Invalid {{type}}',
@@ -582,9 +889,13 @@ const en = {
582
889
  },
583
890
  bulk: {
584
891
  confirmDefault: 'This will apply to {{count}} record(s).',
892
+ confirmDefault_one: 'This will apply to {{count}} record.',
893
+ confirmDefault_other: 'This will apply to {{count}} records.',
585
894
  overLimit: 'Selection ({{count}}) exceeds the action limit ({{limit}}). Reduce the selection to proceed.',
586
895
  affectedRecords: 'Affected records ({{count}}):',
587
896
  skippedIneligible: '{{count}} selected record(s) are not eligible for this action and will be skipped.',
897
+ skippedIneligible_one: '{{count}} selected record is not eligible for this action and will be skipped.',
898
+ skippedIneligible_other: '{{count}} selected records are not eligible for this action and will be skipped.',
588
899
  rowFallback: 'Row {{index}}',
589
900
  andMore: '\u2026 and {{count}} more',
590
901
  processed: '{{count}} / {{total}} processed',
@@ -602,7 +913,6 @@ const en = {
602
913
  undo: 'Undo',
603
914
  undoing: 'Undoing\u2026',
604
915
  done: 'Done',
605
- selectPlaceholder: 'Select\u2026',
606
916
  loading: 'Loading\u2026',
607
917
  },
608
918
  },
@@ -611,26 +921,59 @@ const en = {
611
921
  month: 'Month',
612
922
  week: 'Week',
613
923
  day: 'Day',
614
- agenda: 'Agenda',
615
924
  allDay: 'All Day',
616
- noEvents: 'No events',
617
925
  newEvent: 'New event',
618
926
  moreEvents: '+{{count}} more',
927
+ unscheduled: 'Unscheduled ({{count}})',
928
+ loading: 'Loading calendar…',
929
+ loadError: 'Error: {{message}}',
930
+ configRequired: 'Calendar configuration required. Please specify startDateField, the calendar\'s one required key; the event title resolves without titleField.',
931
+ configRequiredHint: 'It belongs on the view\'s calendar block. An interface page has no calendar slot of its own: point its sourceView at a view that declares one.',
932
+ eventDetails: 'Event Details',
933
+ pullToRefresh: 'Pull to refresh',
934
+ refreshing: 'Refreshing…',
935
+ onDate: 'On {{date}}',
936
+ eventTitle: 'Title',
937
+ eventTitlePlaceholder: 'What\'s this event about?',
938
+ creating: 'Creating…',
939
+ titleRequired: 'Title is required',
940
+ a11y: {
941
+ region: 'Calendar',
942
+ grid: 'Calendar grid',
943
+ goToToday: 'Go to today',
944
+ previousPeriod: 'Previous period',
945
+ nextPeriod: 'Next period',
946
+ currentDate: 'Current date: {{date}}',
947
+ dayCell: '{{date}}, {{count}} events',
948
+ dayCell_one: '{{date}}, {{count}} event',
949
+ dayCell_other: '{{date}}, {{count}} events',
950
+ resizeEventEnd: 'Resize event end',
951
+ resizeEventEndHint: 'Drag to change end date',
952
+ resizeStart: 'Resize start',
953
+ resizeEnd: 'Resize end',
954
+ },
619
955
  },
620
956
  list: {
621
957
  loading: 'Loading records…',
622
958
  recordCount: '{{count}} records',
623
- recordCountOne: '{{count}} record',
959
+ recordCount_one: '{{count}} record',
960
+ recordCount_other: '{{count}} records',
624
961
  addRecord: 'Add record',
962
+ // objectui#12703 — the compact list's footer link to the object's list or the block's view.
963
+ viewAll: 'View all',
625
964
  tabs: 'Tabs',
626
965
  allRecords: 'All Records',
627
966
  search: 'Search',
628
967
  filter: 'Filter',
629
968
  filterRecords: 'Filter Records',
969
+ filterActive: 'Filter, {{count}} active',
970
+ filterActive_one: 'Filter, {{count}} active',
971
+ filterActive_other: 'Filter, {{count}} active',
630
972
  sort: 'Sort',
631
973
  sortRecords: 'Sort Records',
632
974
  sortByIdSuffix: '(by ID)',
633
975
  sortRelationalHint: 'Columns that link to another record are not listed: they can only be sorted by the stored ID, not by the name shown in the cell. To sort by that name, denormalize it onto this object as a stored field, written when the source changes, and sort by that. Not a formula field: it is virtual, so no column is stored for it and the server refuses to sort by one.',
976
+ sortRelationalPicked: 'A linked column sorts by the stored ID, not the name shown.',
634
977
  resetSortToDefault: 'Reset to view default',
635
978
  group: 'Group',
636
979
  groupBy: 'Group By',
@@ -648,6 +991,7 @@ const en = {
648
991
  firstRunMessage: 'Create your first record to get started.',
649
992
  noMatches: 'No matching records',
650
993
  noMatchesMessage: 'No records match your current filters or search. Try adjusting or clearing them.',
994
+ viewFilterNoMatchesMessage: 'No records match this view’s filter.',
651
995
  loadErrorTitle: 'Couldn\u2019t load records',
652
996
  loadErrorMessage: 'Something went wrong while loading this data. Check your connection and try again.',
653
997
  loadErrorForbiddenTitle: 'You don\u2019t have access',
@@ -689,14 +1033,15 @@ const en = {
689
1033
  print: 'Print',
690
1034
  hideFieldsTitle: 'Hide Fields',
691
1035
  dataLimitReached: 'Showing first {{limit}} records. More data may be available.',
692
- viewSettings: 'View settings',
693
- viewSettingsHint: 'Grouping, color, density, and visible fields.',
1036
+ display: 'Display',
1037
+ layout: 'Layout',
1038
+ rowNumbers: 'Row numbers',
1039
+ resetToViewDefault: 'Reset to view default',
694
1040
  addGroup: 'Add group field',
695
1041
  collapsedByDefault: 'Collapsed by default',
696
1042
  removeGroup: 'Remove',
697
1043
  inlineEditShort: 'Edit inline',
698
1044
  inlineEditLabel: 'Edit records inline (click a cell to edit)',
699
- recordEditingTitle: 'Record editing',
700
1045
  },
701
1046
  managedByBadge: {
702
1047
  config: {
@@ -732,15 +1077,31 @@ const en = {
732
1077
  deleteCard: 'Delete card',
733
1078
  deleteColumn: 'Delete column',
734
1079
  noCards: 'No cards',
1080
+ columnCards: '{{column}} cards',
1081
+ laneCards: '{{column}} - {{lane}} cards',
735
1082
  cardTitlePlaceholder: 'Enter card title…',
736
1083
  uncategorized: 'Uncategorized',
1084
+ loadMore: 'Load more',
737
1085
  columns: 'columns',
738
1086
  requiredFieldsTitle: 'Complete required fields',
739
1087
  requiredFieldsDescription: 'This move makes the fields below required. Fill them in to continue.',
1088
+ dnd: {
1089
+ start: 'Picked up {{card}} in {{column}}.',
1090
+ overCard: 'Moving {{card}} over {{target}} in {{column}}.',
1091
+ overColumn: 'Moving {{card}} over {{column}}.',
1092
+ overNone: 'Moving {{card}}. It is not over a column.',
1093
+ moved: 'Moved {{card}} to {{column}}.',
1094
+ dropped: 'Dropped {{card}}. It stays in {{column}}.',
1095
+ cancelled: 'Cancelled moving {{card}}. It stays in {{column}}.',
1096
+ untitledCard: 'an untitled card',
1097
+ unnamedColumn: 'an unnamed column',
1098
+ instructions: 'To move this card to another column, drag it with a pointer or by touch.',
1099
+ dragging: 'Dragging {{card}}',
1100
+ },
740
1101
  },
741
1102
  timeline: {
742
1103
  bucket: {
743
- overdue: 'Overdue',
1104
+ earlier: 'Earlier',
744
1105
  today: 'Today',
745
1106
  tomorrow: 'Tomorrow',
746
1107
  thisWeek: 'This week',
@@ -755,6 +1116,17 @@ const en = {
755
1116
  },
756
1117
  gantt: {
757
1118
  rowLabel: 'Items',
1119
+ unusableRange: {
1120
+ malformedDate: 'Unusable gantt date range — {{path}} is {{value}}, which is not a valid date. Every gantt date has to parse: the startDate and endDate on every row item, plus any minDate / maxDate pinned on the schema.',
1121
+ malformedRow: 'Unusable gantt rows — {{path}} is {{value}}, which is not a row shape. A gantt draws items as a list of rows, every row as an object with a label and its own items, and every row\'s items as a list of bars; a null, a number, a string or a plain object in any of those places cannot be drawn.',
1122
+ inverted: 'Unusable gantt date range — minDate {{minDate}} is after maxDate {{maxDate}}. A pinned minDate / maxDate overrides the range computed from the rows, so this axis has no columns and no bar can be placed on it; swap the two values.',
1123
+ },
1124
+ },
1125
+ unsupported: {
1126
+ objectBoundGantt: 'Unsupported variant "gantt" — an object-bound timeline renders the feed variants ({{variants}}). Gantt needs literal rows, each with its own nested items, so the gantt axis (scale) has no effect here.',
1127
+ },
1128
+ unconfigured: {
1129
+ noDateAxis: 'Timeline date axis required — this view declares no date field, and an object-bound timeline will not invent one. Declare one of: {{fields}}. The first is the spec spelling; the rest are legacy aliases.',
758
1130
  },
759
1131
  },
760
1132
  gantt: {
@@ -800,6 +1172,7 @@ const en = {
800
1172
  },
801
1173
  aria: {
802
1174
  taskList: 'Task list',
1175
+ refreshing: 'Refreshing…',
803
1176
  },
804
1177
  tooltip: {
805
1178
  days: 'd',
@@ -845,13 +1218,19 @@ const en = {
845
1218
  conflict: {
846
1219
  title: 'Schedule conflict',
847
1220
  body: 'This move conflicts with dependency constraints. Auto-reschedule {{count}} affected task(s)?',
1221
+ body_one: 'This move conflicts with dependency constraints. Auto-reschedule {{count}} affected task?',
1222
+ body_other: 'This move conflicts with dependency constraints. Auto-reschedule {{count}} affected tasks?',
848
1223
  confirm: 'Auto-reschedule',
849
1224
  cancel: 'Keep as is',
850
1225
  },
851
1226
  autoScheduleDlg: {
852
1227
  title: 'Auto-schedule',
853
1228
  body: 'Shift {{count}} task(s) later to satisfy dependency links?',
1229
+ body_one: 'Shift {{count}} task later to satisfy dependency links?',
1230
+ body_other: 'Shift {{count}} tasks later to satisfy dependency links?',
854
1231
  skipped: '{{count}} locked task(s) also violate links and were skipped.',
1232
+ skipped_one: '{{count}} locked task also violates links and was skipped.',
1233
+ skipped_other: '{{count}} locked tasks also violate links and were skipped.',
855
1234
  confirm: 'Apply',
856
1235
  cancel: 'Cancel',
857
1236
  none: 'All dependencies satisfied — nothing to reschedule.',
@@ -908,6 +1287,13 @@ const en = {
908
1287
  tabActionsFor: 'View actions for {{name}}',
909
1288
  readonlyAriaLabel: 'Read-only view',
910
1289
  readonlyTooltip: 'System view — defined in code, read-only.',
1290
+ malformedFilter: 'This view’s filter is malformed, so no records are shown: the {{subject}} condition cannot be applied.',
1291
+ // objectui#11605 — an object-bound block whose node names its object in
1292
+ // neither place (no own key, no `dataSource.object`). `{{property}}` is the
1293
+ // block's object key (`objectName`), interpolated and never translated; the
1294
+ // wording was `element.number.noObject`'s with the property as a hole,
1295
+ // before objectui#11880 dropped that notice's flat `object`.
1296
+ noObject: 'No object named: set {{property}} or dataSource.object.',
911
1297
  },
912
1298
  detail: {
913
1299
  back: 'Back',
@@ -921,9 +1307,12 @@ const en = {
921
1307
  lockedByApproval: 'Locked for approval',
922
1308
  lockedTooltip: 'This record has a pending approval request; editing is locked',
923
1309
  writeStrippedTitle: 'Saved — but some fields did not take effect',
1310
+ writeWarningsTitle: 'Saved — with warnings',
924
1311
  writeStrippedReadonly: 'Read-only, so it did not take effect: {{fields}}',
925
1312
  writeStrippedByState: "Not editable in this record's current state, so it did not take effect: {{fields}}",
926
1313
  writeStrippedPrimaryKey: "The record's identifier cannot be changed by a save, so it did not take effect: {{fields}}",
1314
+ writeStrippedComputed: 'Calculated by the server from a formula, so the value sent did not take effect: {{fields}}',
1315
+ writeStrippedStoredExpression: 'Computed from the record, so the computed value was saved instead of the one sent: {{fields}}',
927
1316
  writeStrippedUnknownReason: 'Not applied by the server: {{fields}}',
928
1317
  approvalPendingEditable: 'In approval · editable',
929
1318
  approvalPendingTooltip: 'This record has a pending approval request; this step still allows editing',
@@ -950,6 +1339,16 @@ const en = {
950
1339
  pathStageLostUpcoming: '{{stage}}, closed lost, not reached',
951
1340
  pathStageWonUpcoming: '{{stage}}, goal stage, not reached',
952
1341
  linkCopied: 'Link copied to clipboard',
1342
+ commentFailed: 'Your comment was not posted. Nothing was saved — please try again.',
1343
+ reactionFailed: 'Your reaction was not saved. Please try again.',
1344
+ // objectui#12268 — the author's Edit and Delete on their own comment in the
1345
+ // record feed: the two buttons' names, the delete confirm, and the two
1346
+ // refusals the record page raises.
1347
+ editComment: 'Edit comment',
1348
+ deleteComment: 'Delete comment',
1349
+ deleteCommentConfirm: 'Are you sure you want to delete this comment?',
1350
+ commentEditFailed: 'Your edit was not saved. The comment keeps its previous text.',
1351
+ commentDeleteFailed: 'The comment was not deleted. Please try again.',
953
1352
  linkCopyFailed: 'Failed to copy link',
954
1353
  cancel: 'Cancel',
955
1354
  cancelEdit: 'Discard changes',
@@ -976,7 +1375,8 @@ const en = {
976
1375
  pageHeaderActions: 'Page header actions',
977
1376
  emojiPicker: 'Emoji picker',
978
1377
  reactionCount: '{{emoji}} {{count}} reactions',
979
- reactionCountOne: '{{emoji}} {{count}} reaction',
1378
+ reactionCount_one: '{{emoji}} {{count}} reaction',
1379
+ reactionCount_other: '{{emoji}} {{count}} reactions',
980
1380
  // Record-overlay chrome (objectstack#5506). `recordDetail` is the VISIBLE
981
1381
  // heading the overlay falls back to when the host passes no title;
982
1382
  // `openAsFullPage` is the icon-only expand button's default accessible
@@ -1016,7 +1416,6 @@ const en = {
1016
1416
  viewAll: 'View All',
1017
1417
  new: 'New',
1018
1418
  add: 'Add',
1019
- emptyValue: '—',
1020
1419
  activity: 'Activity',
1021
1420
  history: 'History',
1022
1421
  historyEmpty: 'No history yet',
@@ -1031,6 +1430,7 @@ const en = {
1031
1430
  sortBy: 'Sort by',
1032
1431
  filterPlaceholder: 'Filter…',
1033
1432
  highlightFields: 'Key Fields',
1433
+ highlightsLabel: 'Record highlights',
1034
1434
  // Comments
1035
1435
  comments: 'Comments',
1036
1436
  searchComments: 'Search comments…',
@@ -1042,8 +1442,14 @@ const en = {
1042
1442
  unpin: 'Unpin',
1043
1443
  justNow: 'just now',
1044
1444
  minutesAgo: '{{count}}m ago',
1445
+ minutesAgo_one: '{{count}}m ago',
1446
+ minutesAgo_other: '{{count}}m ago',
1045
1447
  hoursAgo: '{{count}}h ago',
1448
+ hoursAgo_one: '{{count}}h ago',
1449
+ hoursAgo_other: '{{count}}h ago',
1046
1450
  daysAgo: '{{count}}d ago',
1451
+ daysAgo_one: '{{count}}d ago',
1452
+ daysAgo_other: '{{count}}d ago',
1047
1453
  // Activity feed actors
1048
1454
  systemActor: 'System',
1049
1455
  unknownUser: 'Unknown',
@@ -1057,7 +1463,8 @@ const en = {
1057
1463
  // Attachments
1058
1464
  dropFilesToUpload: 'Drop files here or click to upload',
1059
1465
  attachmentCount: '{{count}} attachment',
1060
- attachmentCountPlural: '{{count}} attachments',
1466
+ attachmentCount_one: '{{count}} attachment',
1467
+ attachmentCount_other: '{{count}} attachments',
1061
1468
  removeAttachment: 'Remove attachment',
1062
1469
  // Record Attachments panel (enable.files, objectstack#4358)
1063
1470
  attachments: 'Attachments',
@@ -1066,6 +1473,7 @@ const en = {
1066
1473
  noAttachments: 'No attachments yet. Upload a file to get started.',
1067
1474
  downloadAttachment: 'Download',
1068
1475
  deleteAttachment: 'Delete attachment',
1476
+ deleteAttachmentConfirm: 'Are you sure you want to delete the attachment "{{name}}"?',
1069
1477
  attachmentDeleteDenied: 'Only the uploader or someone who can edit this record may delete this attachment.',
1070
1478
  attachmentParentAccessDenied: "You don't have access to attach files to this record.",
1071
1479
  attachmentDownloadDenied: "You don't have access to download this attachment.",
@@ -1075,12 +1483,6 @@ const en = {
1075
1483
  attachmentsLoadFailed: "We couldn't load the attachments for this record.",
1076
1484
  attachmentsApiUnavailable: 'The attachments list is not available on this object.',
1077
1485
  retryLoadAttachments: 'Retry',
1078
- // Diff
1079
- unifiedDiff: 'Unified diff',
1080
- sideBySideDiff: 'Side-by-side diff',
1081
- noChanges: 'No changes',
1082
- previousVersion: 'Previous',
1083
- currentVersion: 'Current',
1084
1486
  // Discussion
1085
1487
  discussion: 'Discussion',
1086
1488
  showDiscussion: 'Show Discussion ({{count}})',
@@ -1098,23 +1500,16 @@ const en = {
1098
1500
  // Subscription
1099
1501
  subscribedTooltip: 'Subscribed — click to unsubscribe',
1100
1502
  unsubscribedTooltip: 'Subscribe to notifications',
1101
- // Navigation
1102
- firstRecord: 'First record (Home)',
1103
- previousRecordKey: 'Previous record (←)',
1104
- nextRecordKey: 'Next record (→)',
1105
- lastRecord: 'Last record (End)',
1503
+ // The reference rail's empty related-record list
1106
1504
  noRecords: 'No records',
1107
- // objectui#3863 — the BASE key is the slot every plural category a pack did not
1108
- // enumerate resolves to, keeping that pack in its own language instead of falling
1109
- // through `fallbackLng` to English. `en` itself can never reach it (its categories
1110
- // are exactly `one`/`other`), so this value is parity ballast — and it must stay
1111
- // byte-identical to the call site's inline `defaultValue` in
1112
- // `record-reference-rail.tsx`, which `check:i18n-keys` now judges as class 3.
1505
+ // objectui#3863 — the BASE key; since objectui#11432 every pack also spells out
1506
+ // each CLDR category its language selects, so with a count no pack reaches the
1507
+ // base. In `en` this value is parity ballast — and it must stay byte-identical to
1508
+ // the call site's inline `defaultValue` in `record-reference-rail.tsx`, which
1509
+ // `check:i18n-keys` now judges as class 3.
1113
1510
  showEmptyRelated: '+ {{count}} empty',
1114
1511
  showEmptyRelated_one: '+ {{count}} empty',
1115
1512
  showEmptyRelated_other: '+ {{count}} empty',
1116
- searchWhileNavigating: 'Search while navigating',
1117
- searchRecords: 'Search records…',
1118
1513
  // Activity timeline
1119
1514
  allActivity: 'All Activity',
1120
1515
  commentsOnly: 'Comments Only',
@@ -1122,13 +1517,29 @@ const en = {
1122
1517
  tasksOnly: 'Tasks Only',
1123
1518
  leaveCommentPlaceholder: 'Leave a comment… (Ctrl+Enter to submit)',
1124
1519
  noActivity: 'No activity recorded',
1520
+ activityAccessDenied: "You don't have permission to view activity on this record.",
1521
+ commentsAccessDenied: "You don't have permission to view comments on this record.",
1522
+ // objectui#7149 — the rest of `ActivityTimeline`. The four chip labels the
1523
+ // `detail.*` pack did not already name, and the `formatFieldChange`
1524
+ // sentences, which are assembled in code and so need interpolation holes.
1525
+ allFilter: 'All',
1526
+ createsFilter: 'Creates',
1527
+ deletesFilter: 'Deletes',
1528
+ statusChangesFilter: 'Status Changes',
1529
+ activityEmptyValue: '(empty)',
1530
+ activityFieldChanged: 'Changed {{field}} from "{{old}}" to "{{new}}"',
1531
+ activityCreated: 'Created this record',
1532
+ activityDeleted: 'Deleted this record',
1533
+ activityStatusChanged: 'Changed status to "{{value}}"',
1534
+ activityUpdated: 'Updated record',
1125
1535
  loadMore: 'Load more',
1126
1536
  edited: '(edited)',
1127
1537
  via: 'via {{source}}',
1128
1538
  viewSource: 'View source',
1129
1539
  // Replies
1130
1540
  replyCount: '{{count}} reply',
1131
- replyCountPlural: '{{count}} replies',
1541
+ replyCount_one: '{{count}} reply',
1542
+ replyCount_other: '{{count}} replies',
1132
1543
  replyPlaceholder: 'Reply…',
1133
1544
  // Aria labels
1134
1545
  filterActivity: 'Filter activity',
@@ -1141,13 +1552,54 @@ const en = {
1141
1552
  copyPhone: 'Copy phone number',
1142
1553
  copyRecordId: 'Copy record ID',
1143
1554
  showEmptyFields: 'Show {{count}} empty fields',
1555
+ showEmptyFields_one: 'Show {{count}} empty field',
1556
+ showEmptyFields_other: 'Show {{count}} empty fields',
1144
1557
  hideEmptyFields: 'Hide empty fields',
1145
1558
  noValue: 'No value',
1559
+ unresolvedReference: 'Unresolved reference: {{value}} was not resolved to a user',
1560
+ unresolvedLookupReference: 'Unresolved reference: {{value}} was not resolved to a record on this screen',
1561
+ // Cell-level COUNT phrases for the `repeater` and file cells
1562
+ // (objectui#8441). Both were written straight into the renderer before this
1563
+ // — `repeater` as a number plus a hardcoded Chinese unit word, which every
1564
+ // reader on every locale saw, and `FileCellRenderer` as an English-only
1565
+ // `count === 1 ? 'file' : 'files'`, which is not plural-safe anywhere else.
1566
+ // One channel, two call sites.
1567
+ //
1568
+ // REAL i18next plural families — the one count shape this repo uses since
1569
+ // objectui#11445 retired the code-selected `xxxCountOne` / `xxxCountPlural`
1570
+ // sibling keys. i18next asks `Intl.PluralRules` for the
1571
+ // ONE suffix a number needs, so each pack carries every category its language
1572
+ // selects (`ru` `_few`/`_many`, `ar` `_zero`/`_two`/`_few`/`_many` —
1573
+ // objectui#11432) plus the base key (objectui#3863).
1574
+ // `all-locales-key-parity.test.ts` owns both rules.
1575
+ repeaterItemCount: '{{count}} items',
1576
+ repeaterItemCount_one: '{{count}} item',
1577
+ repeaterItemCount_other: '{{count}} items',
1578
+ fileCount: '{{count}} files',
1579
+ fileCount_one: '{{count}} file',
1580
+ fileCount_other: '{{count}} files',
1146
1581
  },
1147
1582
  chart: {
1148
- noData: 'No chart data available',
1149
1583
  loading: 'Loading chart…',
1584
+ refreshing: 'Refreshing…',
1150
1585
  nullCategory: '(None)',
1586
+ // The refusal a scatter renders when handed more than one series
1587
+ // (objectui#7194): it binds ONE measure, so a second series was painted at
1588
+ // the first one's y values. One short sentence on purpose — this pack is
1589
+ // eagerly loaded and the `framework` chunk's gzip ceiling has ~0.2 KB of
1590
+ // headroom; the series keys are rendered by the chart as data, after it.
1591
+ scatterOneMeasure: 'A scatter plots one measure. Keep exactly one series:',
1592
+ // The refusal an object-bound chart renders when it declares no category
1593
+ // axis (objectui#8168) — `aggregate.groupBy` / `xAxisKey` / `xAxis.field`
1594
+ // all absent, so `runAggregate` would have grouped on a name nobody wrote.
1595
+ // The twin of `timeline.unconfigured.noDateAxis`. ONE short sentence, for
1596
+ // the same reason `scatterOneMeasure` above is one: the packs ship it ten
1597
+ // times, and the binding names are rendered by the component after it
1598
+ // rather than interpolated, so a rung added to or retired from the
1599
+ // resolver cannot leave this sentence naming a stale vocabulary.
1600
+ unconfigured: {
1601
+ noCategoryAxis: 'Chart category axis required — an object-bound chart will not invent one. Declare one of:',
1602
+ },
1151
1603
  },
1152
1604
  report: {
1153
1605
  total: 'Total',
@@ -1162,6 +1614,8 @@ const en = {
1162
1614
  loading: 'Loading…',
1163
1615
  failedToLoad: 'Failed to load matrix: {{message}}',
1164
1616
  needsAcross: 'Matrix report requires at least one `groupingsAcross` field.',
1617
+ // objectui#12330 — the report's no-access state; its title is `dashboard.widgetForbiddenTitle`.
1618
+ forbiddenMessage: 'You don’t have permission to view the data behind this report.',
1165
1619
  aggregate: {
1166
1620
  count: 'Count',
1167
1621
  countDistinct: 'Distinct Count',
@@ -1189,30 +1643,40 @@ const en = {
1189
1643
  panelTitle: 'Edit report',
1190
1644
  },
1191
1645
  },
1192
- map: {
1193
- searchLocations: 'Search locations…',
1194
- locationDetails: 'Location Details',
1195
- markersCount: '{{count}} markers',
1196
- invalidCoordinates: '{{count}} record with missing or invalid coordinates excluded from the map.',
1197
- invalidCoordinatesPlural: '{{count}} records with missing or invalid coordinates excluded from the map.',
1198
- },
1199
1646
  designer: {
1200
1647
  undo: 'Undo',
1201
1648
  redo: 'Redo',
1649
+ canvasMinimap: 'Canvas minimap',
1650
+ versionHistory: 'Version history',
1202
1651
  },
1203
1652
  dashboard: {
1204
1653
  addWidget: 'Add widget',
1205
- removeWidget: 'Remove widget',
1206
1654
  editLayout: 'Edit layout',
1207
1655
  saveLayout: 'Save layout',
1208
1656
  resetLayout: 'Reset layout',
1209
1657
  total: 'Total',
1210
- noDataAvailable: 'No data available',
1211
1658
  noDataSourceFor: 'No data source available for',
1212
- noRows: 'No rows',
1659
+ // objectui#7063 — the DEFAULT empty state every dashboard widget renders
1660
+ // when its query SUCCEEDED and returned nothing. It replaced the terse
1661
+ // per-widget `noRows` / `noDataAvailable` fragments at the render site;
1662
+ // objectui#7125 deleted those two keys from all ten packs once nothing
1663
+ // read them. The copy here has to read as a state rather than a failure,
1664
+ // which is why it says the widget loaded. `sourceLabel` carries its own
1665
+ // punctuation so the call site concatenates no separator (see
1666
+ // `WidgetEmptyState`).
1667
+ empty: {
1668
+ title: 'No data yet',
1669
+ message: 'This widget loaded successfully and its query returned no records yet.',
1670
+ sourceLabel: 'Source:',
1671
+ },
1213
1672
  loading: 'Loading…',
1673
+ refreshing: 'Refreshing…',
1674
+ refreshAll: 'Refresh All',
1675
+ refreshDashboard: 'Refresh dashboard',
1214
1676
  pickMeasures: 'Pick measures (values) for this dataset widget.',
1215
1677
  datasetUnsupported: 'This data source does not support dataset queries.',
1678
+ widgetForbiddenTitle: 'You don’t have access',
1679
+ widgetForbiddenMessage: 'You don’t have permission to view the data behind this widget.',
1216
1680
  details: 'Details',
1217
1681
  exportCsv: 'Export CSV',
1218
1682
  openInList: 'Open in list',
@@ -1359,10 +1823,6 @@ const en = {
1359
1823
  appDescription: 'Description',
1360
1824
  appIcon: 'Icon',
1361
1825
  template: 'Template',
1362
- layout: 'Layout',
1363
- layoutSidebar: 'Sidebar',
1364
- layoutHeader: 'Header',
1365
- layoutEmpty: 'Empty',
1366
1826
  selectObjects: 'Select Objects',
1367
1827
  searchObjects: 'Search objects…',
1368
1828
  selectAll: 'Select All',
@@ -1371,7 +1831,6 @@ const en = {
1371
1831
  addGroup: 'Add Group',
1372
1832
  addUrl: 'Add URL',
1373
1833
  addSeparator: 'Add Separator',
1374
- noNavItems: 'No navigation items yet.',
1375
1834
  logoUrl: 'Logo URL',
1376
1835
  primaryColor: 'Primary Color',
1377
1836
  faviconUrl: 'Favicon URL',
@@ -1398,13 +1857,12 @@ const en = {
1398
1857
  appearance: 'Appearance',
1399
1858
  rowHeight: 'Row Height',
1400
1859
  livePreview: 'Live Preview',
1401
- stepBasicDesc: 'Name, title, and layout',
1860
+ stepBasicDesc: 'Name, title, and icon',
1402
1861
  stepObjectsDesc: 'Select business objects',
1403
1862
  stepNavigationDesc: 'Build navigation tree',
1404
1863
  stepBrandingDesc: 'Logo, colors, and favicon',
1405
1864
  noObjectsFound: 'No objects found.',
1406
1865
  noNavItemsHint: 'No navigation items yet. Select objects in the previous step or add items manually.',
1407
- separator: 'Separator',
1408
1866
  separatorLabel: '— Separator —',
1409
1867
  newGroup: 'New Group',
1410
1868
  newLink: 'New Link',
@@ -1438,6 +1896,7 @@ const en = {
1438
1896
  navTypeSeparator: 'Separator',
1439
1897
  navTypeAction: 'Action',
1440
1898
  navTypeComponent: 'Component',
1899
+ navTypeDoc: 'Doc',
1441
1900
  navEditIcon: 'Edit icon',
1442
1901
  navToggleVisible: 'Toggle visibility',
1443
1902
  navHidden: 'Hidden',
@@ -1515,11 +1974,8 @@ const en = {
1515
1974
  defaultValue: 'Default Value',
1516
1975
  placeholder: 'Placeholder',
1517
1976
  referenceTo: 'Reference To',
1518
- formula: 'Formula',
1519
1977
  options: 'Options',
1520
1978
  addOption: 'Add Option',
1521
- validationRules: 'Validation Rules',
1522
- addRule: 'Add Rule',
1523
1979
  systemBadge: 'System',
1524
1980
  ungrouped: 'General',
1525
1981
  deleteConfirmTitle: 'Delete Field?',
@@ -1538,25 +1994,61 @@ const en = {
1538
1994
  },
1539
1995
  },
1540
1996
  console: {
1997
+ // The fixed crumb `AppHeader`'s `studio` variant draws on the Studio front
1998
+ // door (objectui#11863), as `organizations.title` is the `orgs` variant's.
1999
+ // A product name, which every pack writes as is.
2000
+ studio: {
2001
+ title: 'Studio',
2002
+ // What a principal without `studio.access` reads at the Studio URL they
2003
+ // opened (objectui#12035), in place of the silent redirect home.
2004
+ accessRequired: 'You need Studio access to open Studio.',
2005
+ },
1541
2006
  saveAdvisoryTitle: 'Saved — the authoring check raised {{count}} advisory finding(s)',
2007
+ saveAdvisoryTitle_one: 'Saved — the authoring check raised {{count}} advisory finding',
2008
+ saveAdvisoryTitle_other: 'Saved — the authoring check raised {{count}} advisory findings',
2009
+ publishAdvisoryTitle: 'Published — the authoring check raised {{count}} advisory finding(s)',
2010
+ publishAdvisoryTitle_one: 'Published — the authoring check raised {{count}} advisory finding',
2011
+ publishAdvisoryTitle_other: 'Published — the authoring check raised {{count}} advisory findings',
2012
+ importMappingsUnavailable: 'Saved import mappings for {{object}} could not be loaded',
2013
+ importMappingsRefused: 'The server refused this request, so this list is empty because it could not be read — not because nothing is registered. Sign in again, or ask an administrator for access.',
2014
+ importMappingsUnreadable: 'This list is empty because it could not be read, not because nothing is registered. Try again, and report this if it keeps happening.',
2015
+ savedViewsUnavailable: 'Saved views for {{object}} could not be loaded',
2016
+ savedViewsRefused: 'The server refused this request, so this list is empty because it could not be read — not because this object has no saved views. Sign in again, or ask an administrator for access.',
2017
+ savedViewsUnreadable: 'This list is empty because it could not be read, not because this object has no saved views. Try again, and report this if it keeps happening.',
1542
2018
  title: 'ObjectOS',
1543
- initializing: 'Initializing application…',
2019
+ initializing: 'Opening your console…',
2020
+ skipToContent: 'Skip to content',
1544
2021
  search: 'Search…',
1545
2022
  breadcrumb: {
1546
- dashboards: 'Dashboards',
1547
- pages: 'Pages',
1548
- reports: 'Reports',
1549
2023
  system: 'System',
2024
+ // The Setup system page segment after `System` (objectui#10900).
2025
+ marketplace: 'Marketplace',
2026
+ // The console app's own `system/*` pages after `System` (objectui#10969).
2027
+ settings: 'Settings',
2028
+ apps: 'Apps',
2029
+ profile: 'Profile',
2030
+ approvals: 'Approvals',
2031
+ aiApprovals: 'AI Approvals',
2032
+ auditLog: 'Audit Log',
1550
2033
  },
1551
2034
  nav: {
1552
2035
  pinItem: 'Pin {{name}}',
1553
2036
  unpinItem: 'Unpin {{name}}',
1554
- dragToReorder: 'Drag to reorder',
1555
2037
  favorites: 'Favorites',
2038
+ launcherLabel: 'App launcher',
2039
+ menuLabel: 'App navigation',
2040
+ menuEmpty: 'This app has no navigation entries you can open.',
2041
+ // objectui#12427 — the ⋯ menu a sidebar entry shows on hover (`SidebarItemMenu`).
2042
+ itemMenu: 'More actions for {{name}}',
2043
+ openInNewTab: 'Open in new tab',
2044
+ copyLink: 'Copy link',
2045
+ linkCopied: 'Link copied to clipboard',
2046
+ linkCopyFailed: 'Failed to copy link',
2047
+ design: 'Design',
1556
2048
  },
1557
2049
  settingsHub: {
1558
2050
  title: 'Settings',
1559
- subtitle: 'Configure your workspace, integrations, and feature flags.',
2051
+ subtitle: 'Configure your system, integrations, and feature flags.',
1560
2052
  loadError: 'Failed to load settings',
1561
2053
  empty: 'No settings registered. Plugins can register settings manifests via the SettingsService.',
1562
2054
  settingsCount: '{{n}} settings',
@@ -1564,7 +2056,7 @@ const en = {
1564
2056
  // Section headers, keyed by the manifest `category` value. Unknown
1565
2057
  // (plugin-authored) categories fall back to the literal category string.
1566
2058
  categories: {
1567
- Workspace: 'Workspace',
2059
+ Workspace: 'General',
1568
2060
  Communication: 'Communication',
1569
2061
  Security: 'Security',
1570
2062
  Infrastructure: 'Infrastructure',
@@ -1600,18 +2092,22 @@ const en = {
1600
2092
  discard: 'Discard',
1601
2093
  saveChanges: 'Save changes',
1602
2094
  // Save-bar counter. A REAL i18next plural family, not an English-only
1603
- // `change(s)` and not the two-sibling-key `xxxCountOne` shape used
1604
- // elsewhere in this file.
2095
+ // `change(s)`.
1605
2096
  //
1606
- // The BASE key is load-bearing (objectui#3863): i18next asks
1607
- // `Intl.PluralRules` for the one suffix a language needs and, finding no
1608
- // such slot, walks `fallbackLng` to `en`. `ru` has four categories and
1609
- // `ar` six; no pack here enumerates `_few`/`_many`/`_two`/`_zero`, so
1610
- // without this base key `ru` would render ENGLISH at counts 2-20.
1611
- // `all-locales-key-parity.test.ts` owns that rule.
2097
+ // i18next asks `Intl.PluralRules` for the one suffix a language needs, so
2098
+ // each pack spells out every category its language selects — `ru` has
2099
+ // four, `ar` six (objectui#11432) — and keeps the base key
2100
+ // (objectui#3863). `all-locales-key-parity.test.ts` owns both rules.
1612
2101
  unsavedCount: '{{count}} unsaved changes',
1613
2102
  unsavedCount_one: '{{count}} unsaved change',
1614
2103
  unsavedCount_other: '{{count}} unsaved changes',
2104
+ // The toast of a save the server refused field by field (objectui#13146).
2105
+ // `{{settings}}` is the refused settings' labels, as their rows draw them,
2106
+ // joined with `validation.formInvalidJoiner`; each row shows its own
2107
+ // reason, so the toast names the settings and gives no reason.
2108
+ fieldErrorsToast: '{{count}} settings need attention: {{settings}}',
2109
+ fieldErrorsToast_one: '{{count}} setting needs attention: {{settings}}',
2110
+ fieldErrorsToast_other: '{{count}} settings need attention: {{settings}}',
1615
2111
  // The fail-closed crypto refusal (objectstack#8396). objectui#4579 added
1616
2112
  // these as English literals on purpose — routing one string through i18n
1617
2113
  // would have left a single translated string among a dozen hardcoded
@@ -1623,13 +2119,38 @@ const en = {
1623
2119
  cryptoRefusalNoSubject: 'The declared-encrypted value was refused, so nothing was written.',
1624
2120
  cryptoRefusalToast: 'Cannot encrypt secrets: {{subject}}',
1625
2121
  cryptoRefusalToastNoSubject: 'Cannot encrypt secrets',
2122
+ // The source badge beside a settings row's label (objectui#12694): the
2123
+ // layer a value comes from. `{{source}}` is that layer's label, which the
2124
+ // server translates under `settingsCommon.sourceLabels`. One sentence per
2125
+ // badge, so a pack puts the layer where its own grammar wants it.
2126
+ sourceBadgeInherited: 'Inherited from {{source}}',
2127
+ sourceBadgeOverrides: 'Overrides {{source}}',
2128
+ // The action beside an `Overrides` badge (objectui#13145): it clears this
2129
+ // layer's value in the draft, and Save sends it as `null` (unset), so the
2130
+ // row falls back to the value the layer above carries.
2131
+ resetToDefault: 'Reset to default',
2132
+ // The chips on a settings row's header (objectui#12821): the env-lock
2133
+ // chip and the title it shows when the server sends no reason, and the
2134
+ // `Deprecated` chip, with `{{replacedBy}}` the key that replaces it.
2135
+ // `unsupportedSpecifierType` is the row drawn for a specifier type the
2136
+ // page has no control for, `{{type}}` that type.
2137
+ envLockBadge: 'Locked by env',
2138
+ envLockBadgeTitle: 'Pinned by environment variable — cannot be edited from the UI.',
2139
+ deprecatedBadge: 'Deprecated',
2140
+ deprecatedBadgeReplacedBy: 'Deprecated → {{replacedBy}}',
2141
+ unsupportedSpecifierType: 'Unsupported specifier type: {{type}}',
2142
+ },
2143
+ // The one-time workspace-timezone prompt (objectui#11758): asked of an
2144
+ // administrator while `localization.timezone` is still the manifest default.
2145
+ // `{{current}}` is that default, `{{zone}}` the zone the admin confirmed.
2146
+ workspaceTimezonePrompt: {
2147
+ title: 'Set the system timezone',
2148
+ description: 'This system still uses the platform default timezone, {{current}}. The system timezone decides what "today" means in formulas, how reports group dates and how dates and times are shown, for everyone in the system. Your browser timezone is filled in below.',
2149
+ laterHint: 'You can change it at any time on the Settings page.',
2150
+ decline: 'Keep the default',
2151
+ confirm: 'Set timezone',
2152
+ saved: 'System timezone set to {{zone}}',
1626
2153
  },
1627
- loadingSteps: {
1628
- connecting: 'Connecting to data source',
1629
- loadingConfig: 'Loading configuration',
1630
- preparingWorkspace: 'Preparing workspace',
1631
- },
1632
- loadingHint: 'Setting up a new environment can take a few moments.',
1633
2154
  error: {
1634
2155
  connectionFailed: 'Cannot connect to server',
1635
2156
  serverUnreachable: 'The server at {{url}} is unreachable.',
@@ -1655,21 +2176,20 @@ const en = {
1655
2176
  groups: {
1656
2177
  general: 'General',
1657
2178
  navigation: 'Navigation',
1658
- dataViews: 'Data Views',
1659
2179
  aiChat: 'AI assistant',
1660
- preferences: 'Preferences',
1661
2180
  },
1662
2181
  openCommandPalette: 'Open command palette',
1663
2182
  showShortcuts: 'Show keyboard shortcuts',
1664
2183
  closeDialog: 'Close dialog / panel',
1665
2184
  toggleSidebar: 'Toggle sidebar',
1666
- focusSearch: 'Focus search',
1667
- createRecord: 'Create new record',
1668
- refreshData: 'Refresh data',
1669
- editRecord: 'Edit selected record',
1670
2185
  newChat: 'New chat',
1671
2186
  toggleChatsList: 'Toggle conversations list',
1672
- toggleDarkMode: 'Toggle dark mode',
2187
+ goHome: 'Back to home',
2188
+ },
2189
+ reportIndex: {
2190
+ title: 'Reports',
2191
+ emptyTitle: 'No reports',
2192
+ emptyDescription: 'This app has no reports you can open.',
1673
2193
  },
1674
2194
  commandPalette: {
1675
2195
  title: 'Command palette',
@@ -1679,6 +2199,8 @@ const en = {
1679
2199
  records: 'Records',
1680
2200
  recentRecords: 'Recently viewed',
1681
2201
  objects: 'Objects',
2202
+ packages: 'Packages',
2203
+ flows: 'Flows',
1682
2204
  dashboards: 'Dashboards',
1683
2205
  pages: 'Pages',
1684
2206
  reports: 'Reports',
@@ -1702,13 +2224,27 @@ const en = {
1702
2224
  },
1703
2225
  usage: {
1704
2226
  title: 'AI usage',
1705
- meterBuild: 'Build',
1706
- meterAsk: 'Ask',
2227
+ // objectui#11658 — the popover's ONE figure: the share of the single
2228
+ // AI pool already used (a formatted percentage, never a token count).
2229
+ // It replaced the build / data-Q&A split, which could not attribute a
2230
+ // single-composer turn honestly.
2231
+ poolUsed: '{{percent}} used',
1707
2232
  statusOk: 'Plenty left',
1708
2233
  statusLow: 'Running low',
1709
2234
  statusFull: 'Limit reached',
1710
2235
  resetsDaily: 'Resets tonight',
1711
2236
  resetsMonthly: 'Resets next cycle',
2237
+ // `resetKind: 'weekly'` (free plan's rolling 7-day window, cloud PR
2238
+ // #1852): "N days" (or "N hours" inside the final day). A REAL
2239
+ // i18next plural family — see the `unsavedCount` note above — so the
2240
+ // BASE key carries no suffix and must stay in every pack's lookup
2241
+ // chain (`all-locales-key-parity.test.ts`'s base-key rule).
2242
+ resetsWeeklyDays: 'Resets in {{count}} days',
2243
+ resetsWeeklyDays_one: 'Resets in {{count}} day',
2244
+ resetsWeeklyDays_other: 'Resets in {{count}} days',
2245
+ resetsWeeklyHours: 'Resets in {{count}} hours',
2246
+ resetsWeeklyHours_one: 'Resets in {{count}} hour',
2247
+ resetsWeeklyHours_other: 'Resets in {{count}} hours',
1712
2248
  ctaUpgrade: 'Upgrade to keep going',
1713
2249
  ctaTopUp: 'Add credits to continue',
1714
2250
  ariaLabel: 'AI usage: {{status}}',
@@ -1721,6 +2257,38 @@ const en = {
1721
2257
  share: 'Share',
1722
2258
  shareTitle: 'Share this conversation',
1723
2259
  shareDisabledTitle: 'Start chatting to enable sharing',
2260
+ // The build conversation's Build Doctor button (its accessible name,
2261
+ // and its tooltip enabled / before the first message) and the drawer
2262
+ // title it opens (objectui#10900).
2263
+ buildDoctor: 'Build Doctor',
2264
+ buildDoctorTitle: 'Build Doctor — what actually landed?',
2265
+ buildDoctorDisabledTitle: 'Send a message first',
2266
+ // The Build Doctor drawer's body (objectui#10969): its description, the
2267
+ // loading and not-found states, the summary line, the verdict, and each
2268
+ // section's title and hint. What the report itself carries (tool, artifact
2269
+ // and status names, timeline text) is data and stays verbatim.
2270
+ buildDoctorDrawer: {
2271
+ description: 'What the agent claimed vs what is actually live. Read-only diagnostic.',
2272
+ reconciling: 'Reconciling…',
2273
+ notAvailable: 'Not available — the conversation was not found or you are not authorized.',
2274
+ untitled: '(untitled)',
2275
+ summary: '{{turns}} turn(s) · {{messages}} msgs · {{tokens}} tok · {{seconds}}s LLM',
2276
+ allLive: 'All {{count}} attempted change(s) are live — nothing evaporated.',
2277
+ discrepancies: "{{count}} discrepancy(ies) — what the chat said doesn't match what's live.",
2278
+ orphanedTitle: 'Proposed but never applied',
2279
+ orphanedHint: 'A confirm card the agent proposed but no later turn applied — the change silently evaporated.',
2280
+ missingTitle: 'Claimed but missing',
2281
+ missingHint: "A tool result said it was applied, but the artifact isn't live in sys_metadata.",
2282
+ toolErrorsTitle: 'Tool errors',
2283
+ toolErrorsHint: 'Tool calls that returned an error during the build.',
2284
+ verifyTitle: 'Build check (verify_build)',
2285
+ yourApp: 'Your app:',
2286
+ noIssues: '0 issues',
2287
+ issueCount: '{{count}} issue(s)',
2288
+ platformNoise: '{{count}} platform sys_* finding(s) hidden',
2289
+ pendingActions: 'Pending actions',
2290
+ timeline: 'Timeline ({{count}})',
2291
+ },
1724
2292
  newChat: 'New',
1725
2293
  searchChats: 'Search chats…',
1726
2294
  noChatsYet: 'No chats yet',
@@ -1743,14 +2311,39 @@ const en = {
1743
2311
  liveCanvasUnlisted: 'Live app — {{app}} (unlisted until published)',
1744
2312
  loadingAgents: 'Loading agents…',
1745
2313
  askAnything: 'Ask anything…',
2314
+ askOrChangeApp: 'Ask about your data, or ask me to change this app…',
2315
+ describeAppToBuild: 'Describe the app you want to build, or what to change…',
1746
2316
  emptyTitle: 'Start a conversation',
1747
2317
  emptyDescription: 'Ask anything — the assistant has access to your current app context.',
1748
2318
  switchAssistant: 'Switch assistant',
1749
2319
  chooseAgent: 'Choose assistant…',
2320
+ // objectui#8329 — the maker's empty-state prose, and the FIRST sentence a
2321
+ // new user reads on the build surface (it sits above the start chips).
2322
+ // Same ADR-0112 v1 boundary the chips answer to (cloud#1984 /
2323
+ // objectui#7709 for the starters, cloud#2022 for the model's own closing
2324
+ // suggestions): these two descriptions are the product PROMISING, so they
2325
+ // may only name what v1 BUILDS — objects, fields, views, pages,
2326
+ // dashboards, sample data, apps and navigation. `build.description` used
2327
+ // to offer "an app or workflow … objects, screens and automations" and
2328
+ // `editApp.description` "… view or automation"; v1 has no flows, actions
2329
+ // or schedules, so a user who took either at its word was refused
2330
+ // outright — the worst possible answer to the first line on the page.
2331
+ //
2332
+ // NOT the same thing as the platform's manual automation surfaces, which
2333
+ // are real and stay as they are: `home.build.subtitle` (that card opens
2334
+ // Studio, which does author flows), `engine.studio.landing.description`,
2335
+ // `packagedAutomation.*` and the
2336
+ // marketplace `automation` category. The boundary is v1 AI AUTHORING, not
2337
+ // the word "automation".
2338
+ //
2339
+ // REVERT both sentences when ADR-0112 v2 re-adds flows and actions — same
2340
+ // line of the version roadmap as the chips. Every pack's wording is
2341
+ // guarded, in its own script, by
2342
+ // `packages/i18n/src/__tests__/makerEmptyState-v1-scope-8329.test.ts`.
1750
2343
  empty: {
1751
2344
  build: {
1752
2345
  title: 'Build with AI',
1753
- description: 'Describe an app or workflow in plain language — I draft the objects, screens and automations, then you review and publish.',
2346
+ description: 'Describe an app in plain language — I draft the objects, screens and sample data, then you review and publish.',
1754
2347
  },
1755
2348
  ask: {
1756
2349
  title: 'Ask your data',
@@ -1759,17 +2352,26 @@ const en = {
1759
2352
  editApp: {
1760
2353
  title: 'Editing “{{app}}”',
1761
2354
  titleGeneric: 'Edit this app',
1762
- description: 'What would you like to change? I’ll modify this app in place — add a field, object, view or automation, or adjust what’s already there.',
2355
+ description: 'What would you like to change? I’ll modify this app in place — add a field, object, view or dashboard, or adjust what’s already there.',
1763
2356
  },
1764
2357
  },
1765
2358
  clearConversation: 'Clear',
1766
2359
  sendHint: 'to send',
2360
+ newlineHint: 'for a new line',
1767
2361
  agentActivity: 'Agent activity',
1768
2362
  toolCompleted: 'Completed',
1769
2363
  toolRunning: 'Running',
1770
2364
  toolAwaitingApproval: 'Awaiting approval',
1771
2365
  toolFailed: 'Failed',
1772
2366
  toolDetailsHidden: 'Tool inputs and raw results are hidden in this view.',
2367
+ // objectui#11667 — the inline HITL deny reason (stored on the pending
2368
+ // action as `rejection_reason`, read as prose) and the ADR-0057 P4
2369
+ // "Open in Builder" handoff card. The card's Approve / Reject buttons
2370
+ // borrow `aiApprovals.approveAndExecute` / `aiApprovals.reject`.
2371
+ toolDenyReason: 'Operator rejected from chat',
2372
+ builderHandoffTitle: 'Build this in the Builder',
2373
+ builderHandoffOpen: 'Open in Builder →',
2374
+ builderHandoffSuperseded: 'A newer request is available',
1773
2375
  copy: 'Copy',
1774
2376
  copied: 'Copied',
1775
2377
  regenerate: 'Regenerate',
@@ -1786,7 +2388,7 @@ const en = {
1786
2388
  publishOk: 'Published — objects are now live.',
1787
2389
  seedWarn: 'Published, but some sample data failed to load.',
1788
2390
  openBuiltApp: 'Open app',
1789
- designBuiltApp: 'Design in Studio',
2391
+ viewBuiltAppInStudio: 'View in Studio',
1790
2392
  previewDraft: 'Preview',
1791
2393
  previewApp: 'Preview app',
1792
2394
  resizeSplit: 'Resize chat and preview',
@@ -1843,23 +2445,41 @@ const en = {
1843
2445
  recentRecords: 'List the 5 most recently created records.',
1844
2446
  recordCounts: 'Count records for each object.',
1845
2447
  },
2448
+ // cloud#1984 — these five chips are the maker's own recommendations, so
2449
+ // they may only ask for what ADR-0112 v1 BUILDS: objects, fields, views
2450
+ // (grid/kanban/calendar/gallery), pages, dashboards and sample data. No
2451
+ // wording that promises autonomous behaviour (alert / remind / notify /
2452
+ // automate / status workflow) — v1 has no flows, actions or schedules, and
2453
+ // the model silently degrades such a request into a board or a filtered
2454
+ // view, so the chip would promise an alert and deliver a page. REVERT to
2455
+ // the automation wording when ADR-0112 v2 re-adds flows and actions.
1846
2456
  metadataAssistant: {
1847
- buildCrm: 'Build a sales CRM — customers, contacts, and a deal pipeline I can total by stage.',
1848
- buildApp: 'Create a project tracker — projects, tasks with owners and due dates, and a board by status.',
1849
- buildFlow: 'Design a support desk — tickets with priority, a status workflow, and customer links.',
1850
- buildInventory: 'Build an inventory app — products, stock levels, suppliers, and low-stock visibility.',
1851
- buildRecruiting: 'Make an applicant tracker — candidates, open roles, interview stages, and notes.',
2457
+ buildCrm: 'Build a sales CRM — customers, contacts, and deals with a stage field, plus a dashboard that totals deal value by stage.',
2458
+ buildApp: 'Create a project tracker — projects, tasks with owners and due dates, a board grouped by status, and a calendar of due dates.',
2459
+ buildFlow: 'Design a support desk — tickets with priority and status fields, a board grouped by status, and links to customers.',
2460
+ buildInventory: 'Build an inventory app — products, stock levels, suppliers, and a view that filters the items below their reorder point.',
2461
+ buildRecruiting: 'Make an applicant tracker — candidates, open roles, an interview-stage field, and a board grouped by stage.',
1852
2462
  },
1853
2463
  generic: {
1854
2464
  help: 'What can you help me with?',
1855
2465
  availableObjects: 'List the available data objects.',
1856
2466
  recentActivity: 'Summarize my recent activity.',
1857
2467
  },
2468
+ // objectui#7709 — the edit-mode starters, shown when the maker is bound
2469
+ // to an EXISTING app (`?package=`). Same rule as the five above: they
2470
+ // may only ask for what ADR-0112 v1 BUILDS. The fourth chip used to be
2471
+ // `addAutomation` ("an approval, a status flow, or a notification") and
2472
+ // every capability it named is refused by v1 (cloud#1956 / PR #1970), so
2473
+ // it now asks for sample data — `seed` IS on v1's whitelist, and having
2474
+ // no data is what an existing app most often lacks. REVERT: when
2475
+ // ADR-0112 v2 re-adds flows and actions, THIS chip's automation wording
2476
+ // comes back as `addAutomation`; the retired string is pinned for every
2477
+ // pack in `packages/i18n/src/__tests__/makerEditChips-v1-scope-7709.test.ts`.
1858
2478
  editApp: {
1859
2479
  addField: 'Add a field to one of the objects.',
1860
2480
  addObject: 'Add a new object and relate it to an existing one.',
1861
2481
  addDashboard: 'Add a dashboard for the key metrics.',
1862
- addAutomation: 'Add an automation — an approval, a status flow, or a notification.',
2482
+ addSampleData: 'Fill the existing objects with realistic sample records so I can demo the app.',
1863
2483
  },
1864
2484
  },
1865
2485
  // objectui#3546 slice four — the AI console surfaces: the /ai chat page's app switcher,
@@ -1905,6 +2525,8 @@ const en = {
1905
2525
  collapse: 'Collapse chat',
1906
2526
  maximize: 'Open full page',
1907
2527
  open: 'Open assistant',
2528
+ // objectui#11666 — the launchers' marker while a proposed plan awaits approval.
2529
+ planAwaitingApproval: 'A proposed plan is waiting for your approval',
1908
2530
  },
1909
2531
  group: {
1910
2532
  today: 'Today',
@@ -1914,6 +2536,29 @@ const en = {
1914
2536
  older: 'Older',
1915
2537
  },
1916
2538
  },
2539
+ // objectui#9954 — the environment admin's read-rate report (cloud#2333).
2540
+ // Two anomalous cases, two sets of words: an ABSENT `readsPerWrite` means the
2541
+ // environment wrote nothing at all, so the ratio is unbounded — the most
2542
+ // severe reading, and never a missing number. `{{threshold}}` always comes
2543
+ // from the wire; this repo holds no copy of the line. Report only — the copy
2544
+ // states that nothing is limited or blocked, and must keep doing so.
2545
+ readRate: {
2546
+ ratioTitle: 'Unusual read volume in this system',
2547
+ ratio: 'Reads are running at {{ratio}} rows for every row written. The platform flags anything above {{threshold}}. Nothing is limited or blocked; this is a report so the read pattern can be reviewed.',
2548
+ noWritesTitle: 'Reads with no writes at all in this system',
2549
+ noWrites: 'Rows are being read while none at all are being written, so the read rate has no upper bound. This is the most severe reading. The platform flags anything above {{threshold}}. Nothing is limited or blocked; this is a report so the read pattern can be reviewed.',
2550
+ },
2551
+ // objectui#10439 — the environment admin's storage-capacity banner (cloud#2135).
2552
+ // Raised by the tenant runtime's own verdict on `/api/v1/usage/storage`: `warn`
2553
+ // (80% and up) or `blocked` (uploads and bulk imports refused). `{{used}}` and
2554
+ // `{{limit}}` are that response's `usedMb` / `limitMb`, already formatted.
2555
+ storageUsage: {
2556
+ warningTitle: 'Storage is filling up',
2557
+ warning: '{{used}} MB of {{limit}} MB used. Uploads and imports pause once storage is full.',
2558
+ blockedTitle: 'Storage is full: uploads and imports are paused',
2559
+ blocked: '{{used}} MB of {{limit}} MB used. Existing data is untouched, and reading, exporting and editing single records still work.',
2560
+ upgrade: 'Upgrade to continue',
2561
+ },
1917
2562
  errors: {
1918
2563
  somethingWentWrong: 'Something went wrong',
1919
2564
  unexpectedError: 'An unexpected error occurred while rendering this view.',
@@ -1964,6 +2609,7 @@ const en = {
1964
2609
  objectNotFound: 'Object Not Found',
1965
2610
  objectNotFoundDescription: 'The object "{{objectName}}" does not exist in the current configuration.',
1966
2611
  objectNotFoundHint: 'Check your app navigation settings or select a different object from the sidebar.',
2612
+ objectPackageDisabled: 'The object "{{objectName}}" belongs to a package that is disabled, so it is not being served.',
1967
2613
  filterOrNotSavable: 'This filter uses OR between conditions, which a saved view cannot store yet. It still applies to this list — remove the OR grouping to save it to the view.',
1968
2614
  filterNestedNotSavable: 'This filter uses nested condition groups, which a saved view cannot store. Flatten it to a single list of conditions to save it to the view.',
1969
2615
  systemViewReadonly: 'System view defined in code — read-only.',
@@ -1971,6 +2617,8 @@ const en = {
1971
2617
  // (rename / pin / set-as-default / configure), so the copy has to cover
1972
2618
  // any change to a built-in view rather than name one operation.
1973
2619
  cannotEditMetaView: 'This view is built in and defined in code — it cannot be changed.',
2620
+ resetView: 'Reset view',
2621
+ resetViewTitle: 'Set this view back to its own sort, filters and column widths. Only yours change.',
1974
2622
  cannotDeleteMetaView: 'This view is built in and defined in code — it cannot be deleted.',
1975
2623
  expandToPage: 'Open as full page',
1976
2624
  allRecords: 'All Records',
@@ -1978,14 +2626,17 @@ const en = {
1978
2626
  import: 'Import',
1979
2627
  importTitle: 'Import from CSV',
1980
2628
  importedToast: 'Imported {{count}} row(s).',
2629
+ importedToast_one: 'Imported {{count}} row.',
2630
+ importedToast_other: 'Imported {{count}} rows.',
1981
2631
  importedWithSkipped: 'Imported {{ok}} row(s); skipped {{skipped}}.',
1982
2632
  configureView: 'Configure View',
1983
- toolbar: 'Toolbar',
1984
2633
  toolbarEnabledCount: '{{count}} of {{total}} enabled',
1985
2634
  searchFields: 'Search fields…',
1986
2635
  title: 'Title',
1987
2636
  viewType: 'View type',
1988
2637
  recordCount: '{{count}} records',
2638
+ recordCount_one: '{{count}} record',
2639
+ recordCount_other: '{{count}} records',
1989
2640
  save: 'Save',
1990
2641
  discard: 'Discard',
1991
2642
  createView: 'Create View',
@@ -1996,6 +2647,8 @@ const en = {
1996
2647
  deleteViewTitle: 'Delete view',
1997
2648
  deleteViewConfirm: 'Are you sure you want to delete the view "{{name}}"? This cannot be undone.',
1998
2649
  bulkDeleteConfirm: 'Delete {{count}} selected records? This cannot be undone.',
2650
+ bulkDeleteConfirm_one: 'Delete {{count}} selected record? This cannot be undone.',
2651
+ bulkDeleteConfirm_other: 'Delete {{count}} selected records? This cannot be undone.',
1999
2652
  duplicateViewName: 'A view with this name already exists.',
2000
2653
  viewTypeGrid: 'Grid',
2001
2654
  viewTypeGridDesc: 'A spreadsheet-style table of records.',
@@ -2048,16 +2701,25 @@ const en = {
2048
2701
  chartTypePie: 'Pie chart',
2049
2702
  chartTypeArea: 'Area chart',
2050
2703
  chartTypeScatter: 'Scatter chart',
2051
- xAxisField: 'X-axis field',
2052
- xAxisFieldHelp: 'The categorical or time dimension.',
2053
- yAxisField: 'Y-axis field',
2054
- yAxisFieldHelp: 'The numeric field to aggregate.',
2055
- groupBy: 'Group by',
2704
+ dataset: 'Dataset',
2705
+ datasetHelp: 'The chart reads its numbers from this dataset of the object.',
2706
+ chartMeasure: 'Measure',
2707
+ chartMeasureHelp: 'The dataset measure plotted on the value axis.',
2708
+ chartDimension: 'Dimension',
2709
+ chartDimensionHelp: 'The dataset dimension the values are grouped by (optional).',
2710
+ viewTypeUnavailableDataset: 'This object exposes no dataset to chart.',
2711
+ noDatasetMeasure: 'This dataset declares no measure to chart.',
2056
2712
  endDateField: 'End date field',
2057
2713
  ufTabs: 'Tabs',
2058
2714
  ufAddField: '+ Add filter field…',
2059
2715
  ufShowAllRecords: 'Show "All records" tab',
2060
2716
  },
2717
+ // The runtime draft bar's Publish / Discard refusals (RuntimeDraftBar,
2718
+ // objectui#11583). The toast's description is the refusal itself.
2719
+ runtimeDraft: {
2720
+ publishFailed: 'Publish failed',
2721
+ discardFailed: 'Discard failed',
2722
+ },
2061
2723
  localeSwitcher: {
2062
2724
  label: 'Language',
2063
2725
  },
@@ -2078,7 +2740,7 @@ const en = {
2078
2740
  signingIn: 'Signing you in…',
2079
2741
  ssoHandoff: 'Continue to {{target}}',
2080
2742
  // Phone/OTP sign-in labels. `LoginForm` interpolates `{seconds}` with a
2081
- // literal `.replace()` of its own (packages/auth/src/LoginForm.tsx:429),
2743
+ // literal `.replace('{seconds}', …)` of its own (in `LoginForm`),
2082
2744
  // so those SINGLE braces must survive translation — i18next never sees
2083
2745
  // them.
2084
2746
  emailOrPhoneLabel: 'Email or phone number',
@@ -2091,10 +2753,35 @@ const en = {
2091
2753
  resendOtpCountdownText: 'Resend in {seconds}s',
2092
2754
  usePhoneOtpText: 'Sign in with verification code',
2093
2755
  usePasswordSignInText: 'Sign in with password instead',
2756
+ // The social provider buttons and the divider under them
2757
+ // (`SocialSignInButtons`, fed through `LoginForm`'s labels).
2758
+ // `{provider}` is the component's own single-brace hole, filled by a
2759
+ // literal `.replace()` with the provider's name as the server reports
2760
+ // it — the same convention as `{seconds}` above (objectui#10900).
2761
+ socialButton: 'Continue with {provider}',
2762
+ orText: 'or continue with email',
2763
+ // SSO sign-in (objectui#12762): the SSO button, and under SSO-only
2764
+ // ("enforced") mode the line that says how to sign in, the break-glass
2765
+ // link to the password form and the link back. Each English value is
2766
+ // `LoginForm`'s own default for that label, so an English session reads
2767
+ // the same with or without the pack.
2768
+ ssoButton: 'Sign in with SSO',
2769
+ ssoOnlyDescription: "Sign in with your organization's single sign-on",
2770
+ usePasswordText: 'Use a password instead',
2771
+ backToSsoText: 'Back to single sign-on',
2772
+ // objectui#12600 — the console login card's own header, the password
2773
+ // field's reveal toggle, and the footer's legal links.
2774
+ welcomeTitle: 'Welcome back',
2775
+ welcomeDescription: 'Sign in to continue your work',
2776
+ showPasswordText: 'Show password',
2777
+ hidePasswordText: 'Hide password',
2778
+ footer: {
2779
+ privacy: 'Privacy',
2780
+ terms: 'Terms',
2781
+ },
2094
2782
  devAdminHint: {
2095
- title: 'Development instance',
2096
- body: 'Sign in with the seeded dev admin:',
2097
- dismiss: 'Dismiss',
2783
+ title: 'Local development instance',
2784
+ signIn: 'Sign in as dev admin',
2098
2785
  },
2099
2786
  errors: {
2100
2787
  invalidCredentials: 'Invalid email or password. Please try again.',
@@ -2119,8 +2806,13 @@ const en = {
2119
2806
  submittingButton: 'Creating account…',
2120
2807
  hasAccountText: 'Already have an account?',
2121
2808
  signInText: 'Sign in',
2809
+ // Same pair as `auth.login.socialButton` / `orText`, for sign-up.
2810
+ socialButton: 'Sign up with {provider}',
2811
+ orText: 'or continue with email',
2122
2812
  errors: {
2123
2813
  userExists: 'An account with this email already exists. Try signing in instead.',
2814
+ selfRegistrationClosed: 'Self-registration is not open on this system. Ask an administrator for an invitation.',
2815
+ emailDomainNotAllowed: "This email's domain is not allowed to register here. Use your organization email, or ask an administrator for an invitation.",
2124
2816
  },
2125
2817
  verifyInbox: {
2126
2818
  title: 'Check your inbox',
@@ -2192,7 +2884,7 @@ const en = {
2192
2884
  resentSuccess: 'Verification email sent!',
2193
2885
  resentDescription: 'Please check your inbox and click the verification link.',
2194
2886
  resendFailed: 'Cannot resend verification email',
2195
- resendUnavailable: 'Email delivery may not be configured for this environment. Contact support if this persists.',
2887
+ resendUnavailable: 'Email delivery may not be configured for this system. Contact support if this persists.',
2196
2888
  signInLink: 'Go to sign in',
2197
2889
  backToSignIn: 'Back to sign in',
2198
2890
  backToLogin: 'Back to login',
@@ -2242,14 +2934,14 @@ const en = {
2242
2934
  cancel: 'Cancel',
2243
2935
  },
2244
2936
  shell: {
2245
- tenantHostHint: 'You are signing in to this workspace',
2937
+ tenantHostHint: 'You are signing in to this system',
2246
2938
  },
2247
2939
  // These were previously supplied only as inline `defaultValue:` args at the
2248
2940
  // SetPasswordPage call sites, so `zh` was the only pack that could carry a
2249
2941
  // translation. Owning them here makes the page translatable everywhere.
2250
2942
  setPassword: {
2251
2943
  title: 'Set a recovery password',
2252
- description: 'You signed in via single sign-on. Set a local password so you can still sign in to this environment directly if SSO ever becomes unavailable.',
2944
+ description: 'You signed in via single sign-on. Set a local password so you can still sign in directly if SSO ever becomes unavailable.',
2253
2945
  email: 'Email',
2254
2946
  newPassword: 'New password',
2255
2947
  confirmPassword: 'Confirm password',
@@ -2271,6 +2963,7 @@ const en = {
2271
2963
  signOut: 'Sign out instead',
2272
2964
  password: {
2273
2965
  title: 'Your password has expired',
2966
+ resetTitle: 'You must change your password',
2274
2967
  fallbackMessage: 'Please set a new password to continue.',
2275
2968
  current: 'Current password',
2276
2969
  next: 'New password',
@@ -2296,6 +2989,14 @@ const en = {
2296
2989
  verifying: 'Verifying…',
2297
2990
  invalidCode: 'Invalid code. Try again.',
2298
2991
  },
2992
+ // objectui#13208 — the overlay on an IMPERSONATION session: the held
2993
+ // credential is the impersonated user's, so it offers the way out instead
2994
+ // of their forms. `{{user}}` is the impersonated user's display name.
2995
+ impersonation: {
2996
+ passwordTitle: '{{user}} must change their password',
2997
+ mfaTitle: '{{user}} must set up two-factor authentication',
2998
+ body: 'You are impersonating {{user}}, and only they can do this themselves. Stop impersonating to return to your own account.',
2999
+ },
2299
3000
  },
2300
3001
  },
2301
3002
  // The OAuth authorization-code consent screen (`/oauth/consent`). A third
@@ -2304,21 +3005,28 @@ const en = {
2304
3005
  // language — objectui#3546 slice three.
2305
3006
  oauth: {
2306
3007
  consent: {
2307
- // `{{appName}}` is the client's registered name, or `unknownApp` when the
2308
- // client metadata carries none. `{{suffix}}` is the parenthesised account
2309
- // hint the page passes (empty when no user is loaded), so it must stay
2310
- // attached to the sentence's last word.
3008
+ // `{{appName}}` is the client's registered name, or `unknownApp` while the
3009
+ // lookup is in flight or when it gives no name. `{{suffix}}` is the
3010
+ // parenthesised account hint the page passes (empty when no user is
3011
+ // loaded), so it must stay attached to the sentence's last word.
2311
3012
  title: '{{appName}} wants to access your account',
2312
3013
  request: '{{appName}} is requesting permission{{suffix}}.',
2313
3014
  unknownApp: 'an application',
3015
+ // Shown when the lookup gives no registered name, above the raw
3016
+ // `client_id` labelled `clientId` — the id is never shown as the name.
3017
+ unidentified: 'This application could not be identified.',
3018
+ clientId: 'Client ID',
2314
3019
  willAllow: 'This app will be able to:',
2315
- // Only the four scopes the page maps by name; anything else renders the
3020
+ // Only the seven scopes the page maps by name; anything else renders the
2316
3021
  // raw scope string, which is why there is no generic entry here.
2317
3022
  scope: {
2318
3023
  openid: 'Confirm your identity',
2319
3024
  profile: 'Read your basic profile (name, picture)',
2320
3025
  email: 'Read your email address',
2321
3026
  offlineAccess: 'Stay signed in (refresh access)',
3027
+ dataRead: 'Read records, within your own permissions',
3028
+ dataWrite: 'Create, change and delete records, within your own permissions',
3029
+ actionsExecute: 'Run actions on your behalf, within your own permissions',
2322
3030
  },
2323
3031
  deny: 'Deny',
2324
3032
  authorize: 'Authorize',
@@ -2349,11 +3057,20 @@ const en = {
2349
3057
  role: 'Role',
2350
3058
  save: 'Save Changes',
2351
3059
  },
3060
+ language: {
3061
+ title: 'Language',
3062
+ description: 'The language used for notifications and messages sent to you. The interface language is chosen separately, from the globe menu.',
3063
+ label: 'Preferred language',
3064
+ systemDefault: 'Use the deployment default',
3065
+ save: 'Save',
3066
+ saved: 'Language preference updated.',
3067
+ readOnly: 'Your administrator manages the language for your account.',
3068
+ },
2352
3069
  password: {
2353
3070
  changeTitle: 'Change Password',
2354
3071
  setTitle: 'Set Local Password',
2355
- changeDescription: 'Update the password you use to sign in to this environment.',
2356
- setDescription: 'You signed in via single sign-on. Set a local password to also sign in with email and password on this environment.',
3072
+ changeDescription: 'Update the password you use to sign in.',
3073
+ setDescription: 'You signed in via single sign-on. Set a local password to also sign in with email and password.',
2357
3074
  current: 'Current password',
2358
3075
  new: 'New password',
2359
3076
  password: 'Password',
@@ -2362,7 +3079,7 @@ const en = {
2362
3079
  mismatch: 'Passwords do not match',
2363
3080
  enterCurrent: 'Enter your current password',
2364
3081
  changed: 'Password changed.',
2365
- localSet: 'Local password set. You can now sign in with email and password on this environment.',
3082
+ localSet: 'Local password set. You can now sign in with email and password.',
2366
3083
  changeAction: 'Change password',
2367
3084
  setAction: 'Set password',
2368
3085
  },
@@ -2377,22 +3094,21 @@ const en = {
2377
3094
  unknown: 'An unexpected error occurred.',
2378
3095
  },
2379
3096
  workspace: {
2380
- label: 'Workspaces',
2381
- default: 'My Workspace',
2382
- switch: 'Switch workspace',
2383
- create: 'Create workspace',
2384
- createTitle: 'Create a workspace',
2385
- createDescription: 'A workspace is a shared space for your team to collaborate.',
2386
- createButton: 'Create workspace',
2387
- nameLabel: 'Workspace name',
3097
+ label: 'Organizations',
3098
+ default: 'My Organization',
3099
+ switch: 'Switch organization',
3100
+ createTitle: 'Create an organization',
3101
+ createDescription: 'An organization is a shared space for your team to collaborate.',
3102
+ createButton: 'Create organization',
3103
+ nameLabel: 'Organization name',
2388
3104
  namePlaceholder: 'e.g., Acme Inc',
2389
3105
  slugLabel: 'URL slug',
2390
3106
  slugHint: 'Used in URLs. Only lowercase letters, numbers, and hyphens.',
2391
3107
  invite: 'Invite member',
2392
3108
  members: 'Members',
2393
- settings: 'Workspace settings',
3109
+ settings: 'Organization settings',
2394
3110
  multiOrgDisabled: 'Creating new organizations is disabled on this instance.',
2395
- createFailed: 'Failed to create workspace',
3111
+ createFailed: 'Failed to create organization',
2396
3112
  },
2397
3113
  help: {
2398
3114
  onThisPage: 'On this page',
@@ -2400,10 +3116,15 @@ const en = {
2400
3116
  allDocs: 'All documentation',
2401
3117
  keyboardShortcuts: 'Keyboard shortcuts',
2402
3118
  onlineDocs: 'Online documentation',
3119
+ loadingDocumentation: 'Loading documentation',
3120
+ about: 'About',
3121
+ aboutTitle: 'About this console',
3122
+ aboutDescription: 'The build this console was compiled from.',
3123
+ aboutBuild: 'Console build',
3124
+ aboutBuildUnknown: 'Unknown build (no version was set when this console was built)',
2403
3125
  },
2404
3126
  sidebar: {
2405
3127
  settings: 'Settings',
2406
- help: 'Help',
2407
3128
  helpTooltip: 'Help & Documentation',
2408
3129
  activityFeed: 'Activity feed',
2409
3130
  notifications: 'Notifications',
@@ -2415,20 +3136,19 @@ const en = {
2415
3136
  packageManagement: 'Package management',
2416
3137
  searchNavigation: 'Search navigation…',
2417
3138
  recent: 'Recent',
2418
- favorites: 'Favorites',
2419
3139
  starred: 'Starred',
2420
3140
  removeFromFavorites: 'Remove {{name}} from favorites',
3141
+ allReports: 'All reports',
2421
3142
  },
2422
3143
  topbar: {
2423
3144
  aiAssistant: 'AI Assistant',
2424
3145
  designInStudio: 'Design in Studio',
3146
+ editApp: 'Edit app',
2425
3147
  openAssistant: 'Open {{name}} assistant',
2426
3148
  offline: 'Offline',
2427
3149
  usersOnline: 'Users currently online',
2428
3150
  switchView: 'Switch view',
2429
- switchObject: 'Switch Object',
2430
3151
  connection: {
2431
- connected: 'Connected',
2432
3152
  connecting: 'Connecting…',
2433
3153
  reconnecting: 'Reconnecting…',
2434
3154
  disconnected: 'Disconnected',
@@ -2443,48 +3163,69 @@ const en = {
2443
3163
  },
2444
3164
  home: {
2445
3165
  title: 'Home',
2446
- subtitle: 'Your workspace dashboard',
2447
3166
  nav: 'Home',
2448
3167
  allApps: 'All Applications',
2449
3168
  yourApps: 'Your apps',
2450
3169
  showMoreApps: 'More',
2451
3170
  showLess: 'Show less',
2452
3171
  continueEmpty: 'Items you open will show up here.',
2453
- actionCenter: {
2454
- title: 'Needs your attention',
2455
- empty: "You're all caught up",
3172
+ // objectui#12602 — Home's "My work" section: its heading, the link to the
3173
+ // full inbox, each column's title, "view all" link and empty line.
3174
+ myWork: {
3175
+ title: "My work",
3176
+ openInbox: "Open inbox",
3177
+ viewAll: "View all",
3178
+ approvals: "Awaiting approval",
3179
+ approvalsEmpty: "Nothing waiting for your approval",
3180
+ notifications: "Notifications",
3181
+ notificationsEmpty: "No unread notifications",
2456
3182
  },
2457
3183
  greetingMorning: 'Good morning',
2458
3184
  greetingAfternoon: 'Good afternoon',
2459
3185
  greetingEvening: 'Good evening',
2460
3186
  greetingNight: 'Working late',
2461
- heroTagline: 'Pick up where you left off, or explore something new.',
3187
+ // objectui#11689 — the greeting's two joiners: the separator between the
3188
+ // greeting and the person's name (rendered only when there is a name), and
3189
+ // the closing mark. Punctuation is a locale property: zh-CN and ja write
3190
+ // full-width marks, ar its own comma.
3191
+ greetingSeparator: ', ',
3192
+ greetingEnd: '.',
3193
+ // objectui#12601 — the field in Home's top bar that opens the command palette.
3194
+ search: 'Search apps, records, pages…',
3195
+ // objectui#12601 — the line under the greeting that sums up what is
3196
+ // waiting: the approvals count, or `none` at zero.
3197
+ summary: {
3198
+ approvals: 'You have {{count}} approval(s) waiting today',
3199
+ approvals_one: 'You have {{count}} approval waiting today',
3200
+ approvals_other: 'You have {{count}} approvals waiting today',
3201
+ none: 'Nothing is waiting for you today',
3202
+ },
3203
+ // objectui#12601 — an admin's trailing tile in the apps strip; opens the marketplace.
3204
+ addApp: 'Add app',
3205
+ // objectui#12601 — the line under a Home app tile: when the user last had
3206
+ // the app open, `time` a compact relative time ("2 hr. ago"). The line is
3207
+ // 11 px in a 112 px tile and never cut, so a pack adds a verb only where
3208
+ // verb and time still fit (zh "{{time}}打开"); English does not have the
3209
+ // room for "Opened". Held to its width budget in every pack by
3210
+ // `appTileOpenedLine-12601.test.ts` (app-shell).
3211
+ appOpened: '{{time}}',
2462
3212
  build: {
2463
3213
  title: 'Build an app',
2464
- subtitle: 'Start from scratch — design objects, forms, automations and interfaces.',
2465
- noCapability: 'Building apps isn\'t available in this workspace — it requires the “Manage Metadata” permission, which your account doesn\'t have.',
3214
+ noCapability: 'Building apps isn\'t available in this system — it requires the “Manage Metadata” permission, which your account doesn\'t have.',
2466
3215
  },
2467
3216
  template: {
2468
3217
  title: 'Start with a template',
2469
- subtitle: 'Install a template app from the marketplace and customize it.',
2470
3218
  marketplaceDisabled: 'This runtime has no app marketplace configured, so there are no templates to install here.',
2471
3219
  },
2472
3220
  open: 'Open',
2473
- stats: {
2474
- apps: 'Applications',
2475
- starred: 'Starred',
2476
- recent: 'Recent items',
2477
- },
2478
- loading: 'Loading workspace…',
2479
- recent: 'Recent',
2480
- starred: 'Starred',
3221
+ loading: 'Loading…',
2481
3222
  welcome: 'Build your business system with AI',
2482
3223
  welcomeDescription: 'Describe your business in one sentence — AI generates the objects, screens, APIs and agent tools. Or start from scratch.',
2483
3224
  welcomeAdminDescription: 'Describe your business in one sentence — AI generates the objects, screens, APIs and agent tools. Or set things up yourself from the menu on the left.',
2484
3225
  welcomeAdminDescriptionNoBuild: 'Set up your first application from the Administration menu on the left. Once you have data, the AI assistant can help you explore it.',
2485
3226
  welcomeAdminDescriptionNoAi: 'Set up your first application from the Administration menu on the left.',
2486
3227
  noAppsTitle: 'No applications yet',
2487
- noAppsDescription: 'Your workspace is being set up — apps your admin shares with you will show up here.',
3228
+ noAppsDescription: 'The system is being set up — apps your admin shares with you will show up here.',
2488
3229
  buildWithAI: 'Build with AI',
2489
3230
  askAI: 'Ask AI',
2490
3231
  recoveryReminder: {
@@ -2493,7 +3234,15 @@ const en = {
2493
3234
  dismiss: 'Dismiss',
2494
3235
  },
2495
3236
  pendingDrafts: {
2496
- message: 'You have {{count}} unpublished change(s) — publish to make them live.',
3237
+ // objectui#12602 — the drafts row in Home's "My work" section: the
3238
+ // count, then the first draft names joined by `nameSeparator`, in
3239
+ // their package when they share one.
3240
+ title: "{{count}} unpublished change(s)",
3241
+ title_one: "{{count}} unpublished change",
3242
+ title_other: "{{count}} unpublished changes",
3243
+ inPackage: "{{names}} waiting to publish in {{package}}",
3244
+ waiting: "{{names}} waiting to publish",
3245
+ nameSeparator: ", ",
2497
3246
  cta: 'Publish',
2498
3247
  publishing: 'Publishing…',
2499
3248
  published: 'Published! Your changes are live.',
@@ -2513,12 +3262,10 @@ const en = {
2513
3262
  browseMarketplace: 'Browse App Marketplace',
2514
3263
  quickActions: {
2515
3264
  title: 'Quick Actions',
2516
- createApp: 'Create App',
2517
- createAppDesc: 'Start with a new application',
2518
3265
  manageObjects: 'Manage Objects',
2519
3266
  manageObjectsDesc: 'Configure data models',
2520
3267
  systemSettings: 'System Settings',
2521
- systemSettingsDesc: 'Configure your workspace',
3268
+ systemSettingsDesc: 'Configure your system',
2522
3269
  },
2523
3270
  recentApps: {
2524
3271
  title: 'Recently Accessed',
@@ -2526,7 +3273,11 @@ const en = {
2526
3273
  object: 'Object',
2527
3274
  dashboard: 'Dashboard',
2528
3275
  page: 'Page',
3276
+ report: 'Report',
3277
+ package: 'Package',
3278
+ view: 'List view',
2529
3279
  record: 'Record',
3280
+ metadata: 'Metadata',
2530
3281
  },
2531
3282
  },
2532
3283
  starredApps: {
@@ -2553,6 +3304,11 @@ const en = {
2553
3304
  manageAllApps: 'Manage All Apps',
2554
3305
  systemConsole: 'System Console',
2555
3306
  noAppsConfigured: 'No apps configured',
3307
+ allApps: 'All apps',
3308
+ editAppInStudio: 'Edit {{app}} in Studio',
3309
+ backToHome: 'Back to home',
3310
+ settings: 'Settings',
3311
+ productHome: '{{product}} home',
2556
3312
  },
2557
3313
  systemNav: {
2558
3314
  systemSettings: 'System Settings',
@@ -2563,7 +3319,6 @@ const en = {
2563
3319
  organizations: 'Organizations',
2564
3320
  roles: 'Roles',
2565
3321
  configuration: 'Configuration',
2566
- createApp: 'Create App',
2567
3322
  administration: 'Administration',
2568
3323
  datasources: 'Datasources',
2569
3324
  documentation: 'Documentation',
@@ -2572,12 +3327,14 @@ const en = {
2572
3327
  title: 'Recent Activity',
2573
3328
  filter: 'Filter',
2574
3329
  empty: 'No recent activity',
3330
+ accessDenied: "You don't have permission to view activity.",
2575
3331
  viewAll: 'View all activity',
2576
3332
  ariaLabel: 'Activity feed',
2577
3333
  typeCreate: 'Create',
2578
3334
  typeUpdate: 'Update',
2579
3335
  typeDelete: 'Delete',
2580
3336
  typeComment: 'Comment',
3337
+ typeSystem: 'System',
2581
3338
  relativeJustNow: 'just now',
2582
3339
  relativeSecondsAgo: '{{count}}s ago',
2583
3340
  relativeMinutesAgo: '{{count}}m ago',
@@ -2598,8 +3355,11 @@ const en = {
2598
3355
  placeholder: 'Search objects, dashboards, pages, reports…',
2599
3356
  inputAriaLabel: 'Search objects, dashboards, pages, reports',
2600
3357
  resultsCount: '{{count}} result for "{{query}}"',
2601
- resultsCountPlural: '{{count}} results for "{{query}}"',
3358
+ resultsCount_one: '{{count}} result for "{{query}}"',
3359
+ resultsCount_other: '{{count}} results for "{{query}}"',
2602
3360
  itemsAvailable: '{{count}} items available',
3361
+ itemsAvailable_one: '{{count}} item available',
3362
+ itemsAvailable_other: '{{count}} items available',
2603
3363
  noResults: 'No results found',
2604
3364
  noResultsHint: 'Try adjusting your search terms',
2605
3365
  typeObjects: 'Objects',
@@ -2617,6 +3377,10 @@ const en = {
2617
3377
  interfacePageSourceMissing: 'This interface page references "{{name}}", which is not available.',
2618
3378
  recordNotFound: 'Record not found',
2619
3379
  recordNotFoundDescription: 'The record you are looking for does not exist or may have been deleted.',
3380
+ recordAccessDenied: 'You don’t have access to {{object}} records',
3381
+ recordAccessDeniedDescription: 'You don’t have permission to view records of this type. Contact your administrator if you think you should have access.',
3382
+ recordLoadFailed: 'Couldn’t load this record',
3383
+ recordLoadFailedDescription: 'Something went wrong while loading it. Check your connection and try again.',
2620
3384
  pageNotFound: 'Page Not Found',
2621
3385
  pageNotFoundDescription: 'The page "{{name}}" could not be found. It may have been removed or renamed.',
2622
3386
  dashboardNotFound: 'Dashboard Not Found',
@@ -2626,10 +3390,19 @@ const en = {
2626
3390
  noAppsConfigured: 'No Apps Configured',
2627
3391
  noAppsConfiguredDescription: 'No applications have been registered. Create your first app or visit System Settings to configure your environment.',
2628
3392
  appNotAvailable: 'App not available',
2629
- appNotAvailableDescription: 'This app is not available yet — it may still be publishing. Try again in a moment.',
3393
+ appNotAvailableDescription: 'This app is not available — try again in a moment.',
3394
+ appNotFound: "This app can't be opened",
3395
+ appNotFoundDescription: 'The server did not return this app for your account.',
3396
+ appUnreachable: "Couldn't reach the server",
3397
+ appUnreachableDescription: 'This app could not be checked. Try again in a moment.',
2630
3398
  appAccessDenied: "You don't have access to this app",
2631
3399
  appAccessDeniedDescription: 'This app exists, but your account is not authorized to open it. Ask an administrator to grant you access.',
2632
3400
  appAccessDeniedHome: 'Back to home',
3401
+ // objectui#12079 — the app root when the app serves this caller no navigation (`AppNoAccessEmptyState`).
3402
+ appNothingAvailable: 'Nothing in {{app}} is available to you yet',
3403
+ appNothingAvailableDescription: 'This app has no pages your account can open. Ask an administrator to grant you access.',
3404
+ appNothingAvailableGrantDescription: 'This app has no pages your account can open. You can grant access in Setup.',
3405
+ openSetup: 'Open Setup',
2633
3406
  createFirstApp: 'Create Your First App',
2634
3407
  systemSettings: 'System Settings',
2635
3408
  back: 'Back',
@@ -2650,6 +3423,10 @@ const en = {
2650
3423
  publishing: 'Publishing…',
2651
3424
  exit: 'Exit preview',
2652
3425
  changes: 'Changes',
3426
+ // objectui#12968 — what is pending on the surface the preview shows.
3427
+ pendingHere: 'Draft preview · {{count}} unpublished changes on this page',
3428
+ pendingHere_one: 'Draft preview · {{count}} unpublished change on this page',
3429
+ pendingHere_other: 'Draft preview · {{count}} unpublished changes on this page',
2653
3430
  // Magic-moment hint: a freshly-built draft renders empty (dashboards at 0,
2654
3431
  // lists "Nothing here yet") because seed/sample rows only load on publish.
2655
3432
  // Say so plainly so an empty preview never reads as a broken build.
@@ -2670,11 +3447,69 @@ const en = {
2670
3447
  detailNone: 'No differences detected — the draft matches the published version.',
2671
3448
  detailChangedKeys: 'Also changed:',
2672
3449
  confirmNote: 'Publishing releases all {{count}} pending drafts of this package atomically.',
3450
+ confirmNote_one: 'Publishing releases the {{count}} pending draft of this package atomically.',
3451
+ confirmNote_other: 'Publishing releases all {{count}} pending drafts of this package atomically.',
3452
+ // objectui#11591 — the package-less sheet: no package to batch, so each
3453
+ // draft publishes by itself and one that fails stays pending.
3454
+ confirmNoteSeparate: 'Publishing releases the {{count}} pending drafts one at a time: a draft that fails stays pending while the others go live.',
3455
+ confirmNoteSeparate_one: 'Publishing releases the {{count}} pending draft on its own: if it fails, it stays pending.',
3456
+ confirmNoteSeparate_other: 'Publishing releases the {{count}} pending drafts one at a time: a draft that fails stays pending while the others go live.',
2673
3457
  publishConfirm: 'Publish all',
2674
- // [objectui#5418] Pre-publish security-posture findings, shown next to
3458
+ // objectui#12517 — the confirm posture's summary line above the list:
3459
+ // the item count, then one `{{n}} {{type}}` per type group.
3460
+ summaryItems: '{{count}} items',
3461
+ summaryItems_one: '{{count}} item',
3462
+ summaryItems_other: '{{count}} items',
3463
+ summaryType: '{{n}} {{type}}',
3464
+ // objectui#13147 — one count family per known metadata type, so the summary
3465
+ // reads "2 flows", not "2 flow". A type with no row here keeps `summaryType`.
3466
+ typeCount: {
3467
+ object: '{{count}} objects',
3468
+ object_one: '{{count}} object',
3469
+ object_other: '{{count}} objects',
3470
+ view: '{{count}} views',
3471
+ view_one: '{{count}} view',
3472
+ view_other: '{{count}} views',
3473
+ page: '{{count}} pages',
3474
+ page_one: '{{count}} page',
3475
+ page_other: '{{count}} pages',
3476
+ dashboard: '{{count}} dashboards',
3477
+ dashboard_one: '{{count}} dashboard',
3478
+ dashboard_other: '{{count}} dashboards',
3479
+ app: '{{count}} apps',
3480
+ app_one: '{{count}} app',
3481
+ app_other: '{{count}} apps',
3482
+ action: '{{count}} actions',
3483
+ action_one: '{{count}} action',
3484
+ action_other: '{{count}} actions',
3485
+ report: '{{count}} reports',
3486
+ report_one: '{{count}} report',
3487
+ report_other: '{{count}} reports',
3488
+ flow: '{{count}} flows',
3489
+ flow_one: '{{count}} flow',
3490
+ flow_other: '{{count}} flows',
3491
+ permission: '{{count}} permission sets',
3492
+ permission_one: '{{count}} permission set',
3493
+ permission_other: '{{count}} permission sets',
3494
+ agent: '{{count}} agents',
3495
+ agent_one: '{{count}} agent',
3496
+ agent_other: '{{count}} agents',
3497
+ email_template: '{{count}} email templates',
3498
+ email_template_one: '{{count}} email template',
3499
+ email_template_other: '{{count}} email templates',
3500
+ },
3501
+ // objectui#12929 — the unchecked choice under an added object field whose
3502
+ // literal default the batch publish can write into the existing records.
3503
+ backfillOption: 'Backfill default ({{count}} existing records)',
3504
+ backfillOption_one: 'Backfill default ({{count}} existing record)',
3505
+ backfillOption_other: 'Backfill default ({{count}} existing records)',
3506
+ backfillOptionNoCount: 'Backfill default',
3507
+ // [`7a90afdf9`] Pre-publish security-posture findings, shown next to
2675
3508
  // the confirm button so a refusal the door would issue is read BEFORE
2676
3509
  // the click rather than as a toast after the batch rolled back.
2677
3510
  securityBlockTitle: 'Publishing will be refused — {{count}} item(s) need a decision first',
3511
+ securityBlockTitle_one: 'Publishing will be refused — {{count}} item needs a decision first',
3512
+ securityBlockTitle_other: 'Publishing will be refused — {{count}} items need a decision first',
2678
3513
  securityBlockWhere: 'Fix it on the object under Settings → Record sharing, then publish again.',
2679
3514
  },
2680
3515
  // ADR-0045 — the materialized-but-unlisted app banner
@@ -2702,7 +3537,19 @@ const en = {
2702
3537
  revertLabel: 'Reverted a change',
2703
3538
  applyLabel: 'Build change',
2704
3539
  revert: 'revert',
2705
- items: 'item(s)',
3540
+ // objectui#9266 — the count is INSIDE the value, one interpolated string
3541
+ // per pack. It used to be a bare unit word that `CommitTimeline` glued to
3542
+ // a number it rendered itself, which no pack could make agree: `de` read
3543
+ // `1 Elemente`, `ru` `1 элементов`, `ar` `1 عناصر`. Each pack now states
3544
+ // the count in its own count-INVARIANT idiom — `en` keeps the `(s)` marker
3545
+ // it already dodged with, `zh`/`ja`/`ko` keep the inline counter their
3546
+ // grammar needs no plural for, and `de`/`fr`/`es`/`pt`/`ru`/`ar` use the
3547
+ // label-colon form (`Elemente: 3`, `Элементов: 3`), which is the shape
3548
+ // `fields.textarea.charactersRemaining` already uses for this exact
3549
+ // reason. It needs no family: every pack's form reads at any number,
3550
+ // which is why `count-families-11445.test.ts` names it on its
3551
+ // count-neutral list rather than holding it to the family rule.
3552
+ items: '{{count}} item(s)',
2706
3553
  revertAction: 'Revert',
2707
3554
  reverted: 'Reverted — the change has been undone.',
2708
3555
  revertFailed: 'Revert failed',
@@ -2718,22 +3565,17 @@ const en = {
2718
3565
  requiredError: '{{label}} is required',
2719
3566
  lookupPlaceholder: 'Record id for {{label}}',
2720
3567
  lookupHelpText: 'No reference object is configured for this parameter, so the record picker is unavailable. Enter a record id, or ask an administrator to fix the action parameter.',
3568
+ unresolvedParam: 'This parameter cannot be shown: the field it is backed by is missing from the object metadata, so the control it needs cannot be built. Ask an administrator to fix the action definition.',
3569
+ carryOverHint: 'Carried over unchanged (read-only)',
2721
3570
  cancel: 'Cancel',
2722
3571
  confirm: 'Confirm',
2723
3572
  uploading: 'Uploading…',
2724
- defaultActionTitle: 'Action',
2725
- ok: 'OK',
2726
3573
  },
2727
3574
  actionConfirm: {
2728
3575
  title: 'Confirm Action',
2729
3576
  confirm: 'Continue',
2730
3577
  cancel: 'Cancel',
2731
3578
  },
2732
- rowAction: {
2733
- openMenu: 'Open menu',
2734
- edit: 'Edit',
2735
- delete: 'Delete',
2736
- },
2737
3579
  navigationSync: {
2738
3580
  addedPage: 'Navigation updated: added page "{{name}}"',
2739
3581
  addedDashboard: 'Navigation updated: added dashboard "{{name}}"',
@@ -2751,10 +3593,14 @@ const en = {
2751
3593
  deleteFailed: 'Failed to delete {{label}}',
2752
3594
  noRecordId: 'No record ID provided',
2753
3595
  deleteConfirm: 'Are you sure you want to delete this record?',
2754
- resetPackageSetConfirm: 'This permission set ships with an installed package and cannot be removed. ' +
2755
- 'Deleting resets it to the shipped baseline and discards your environment customization. Continue?',
2756
- resetPackageSetSuccess: 'Permission set reset to its shipped baseline',
3596
+ deleteConfirmTitle: 'Delete {{label}} "{{name}}"?',
3597
+ bulkDeleteConfirmTitle: 'Delete {{count}} {{label}} records?',
3598
+ bulkDeleteConfirmTitle_one: 'Delete {{count}} {{label}} record?',
3599
+ bulkDeleteConfirmTitle_other: 'Delete {{count}} {{label}} records?',
3600
+ deleteConfirmButton: 'Delete',
2757
3601
  bulkDeleteSuccess: 'Deleted {{count}} {{label}} records',
3602
+ bulkDeleteSuccess_one: 'Deleted {{count}} {{label}} record',
3603
+ bulkDeleteSuccess_other: 'Deleted {{count}} {{label}} records',
2758
3604
  bulkDeletePartial: '{{succeeded}} deleted, {{failed}} failed',
2759
3605
  },
2760
3606
  objectViewActions: {
@@ -2766,15 +3612,6 @@ const en = {
2766
3612
  exportFailed: 'Export failed: {{message}}',
2767
3613
  forecastSoon: 'Forecast view coming soon',
2768
3614
  },
2769
- recordDetail: {
2770
- viewersTooltip: 'Users viewing this record',
2771
- },
2772
- cellRender: {
2773
- empty: 'Empty',
2774
- yes: 'Yes',
2775
- no: 'No',
2776
- systemFields: 'System',
2777
- },
2778
3615
  user: {
2779
3616
  profile: 'Profile',
2780
3617
  settings: 'Settings',
@@ -2784,18 +3621,18 @@ const en = {
2784
3621
  language: 'Language',
2785
3622
  },
2786
3623
  organizations: {
2787
- mine: 'My Workspaces',
2788
- create: 'Create workspace',
2789
- title: 'Workspaces',
2790
- heading: 'Your Workspaces',
2791
- subtitle: 'Select a workspace to continue, or create a new one.',
2792
- searchPlaceholder: 'Search for a workspace',
2793
- new: 'New workspace',
2794
- current: 'Current workspace',
3624
+ mine: 'My Organizations',
3625
+ create: 'Create organization',
3626
+ title: 'Organizations',
3627
+ heading: 'Your Organizations',
3628
+ subtitle: 'Select an organization to continue, or create a new one.',
3629
+ searchPlaceholder: 'Search for an organization',
3630
+ new: 'New organization',
3631
+ current: 'Current organization',
2795
3632
  manage: 'Manage',
2796
- emptyTitle: 'No workspaces yet',
2797
- emptyDescription: 'Create your first workspace to get started.',
2798
- noMatches: 'No workspaces match your search.',
3633
+ emptyTitle: 'No organizations yet',
3634
+ emptyDescription: 'Create your first organization to get started.',
3635
+ noMatches: 'No organizations match your search.',
2799
3636
  },
2800
3637
  // Organization MANAGEMENT (packages/app-shell/src/console/organizations/**
2801
3638
  // plus the workspace switcher). Distinct from `organizations` above, which
@@ -2889,6 +3726,7 @@ const en = {
2889
3726
  // The org's URL segment. `deleteConfirmSlugLabel` asks the user to
2890
3727
  // retype it, so the two must keep naming the same thing.
2891
3728
  slugLabel: 'Slug',
3729
+ slugLockedNote: 'This organization has active environments, so its slug can’t be changed here: renaming it also moves their subdomains.',
2892
3730
  logoLabel: 'Logo',
2893
3731
  logoUpload: 'Upload',
2894
3732
  logoReplace: 'Replace',
@@ -2953,18 +3791,39 @@ const en = {
2953
3791
  viewAll: 'View all notifications',
2954
3792
  groupCount: '{{count}} notifications',
2955
3793
  groupUnread: '{{count}} unread',
3794
+ groupUnread_one: '{{count}} unread',
3795
+ groupUnread_other: '{{count}} unread',
2956
3796
  groupMarkRead: 'Mark read',
2957
3797
  groupMarkReadTitle: 'Mark all of this type read',
2958
3798
  approvalsPending: '{{count}} pending approvals',
3799
+ approvalsPending_one: '{{count}} pending approval',
3800
+ approvalsPending_other: '{{count}} pending approvals',
2959
3801
  viewApprovals: 'View approvals',
2960
3802
  noPendingApprovals: 'No pending approvals',
2961
3803
  openApprovalsInbox: 'Open Approvals Inbox',
2962
- // Bell-badge breakdown (#7233): the badge sums unread notification topics
3804
+ // Bell-badge breakdown (objectstack-ai/objectstack#7233): the badge sums unread notification topics
2963
3805
  // and pending approvals, then clamps at "9+". These three spell the sum
2964
3806
  // out inside the popover so the number is explainable.
2965
3807
  badgeTotal: '{{total}} total',
2966
- badgeNotifications: '{{unread}} notifications',
2967
- badgeApprovals: '{{approvals}} pending approvals',
3808
+ badgeNotifications: '{{count}} notifications',
3809
+ badgeNotifications_one: '{{count}} notification',
3810
+ badgeNotifications_other: '{{count}} notifications',
3811
+ badgeApprovals: '{{count}} pending approvals',
3812
+ badgeApprovals_one: '{{count}} pending approval',
3813
+ badgeApprovals_other: '{{count}} pending approvals',
3814
+ // objectui#7011 — the arrival announcement (toast / desktop notification)
3815
+ // and the two switches that govern it.
3816
+ arrivalMany: '{{count}} new messages',
3817
+ arrivalMany_one: '{{count}} new message',
3818
+ arrivalMany_other: '{{count}} new messages',
3819
+ arrivalRepeats: '{{count}} new messages on this topic',
3820
+ arrivalRepeats_one: '{{count}} new message on this topic',
3821
+ arrivalRepeats_other: '{{count}} new messages on this topic',
3822
+ arrivalOpen: 'View',
3823
+ toastEnabled: 'In-app alerts',
3824
+ desktopEnabled: 'Desktop notifications',
3825
+ desktopBlocked: 'Blocked. Allow notifications for this site in your browser settings.',
3826
+ desktopUnsupported: 'This browser does not support desktop notifications.',
2968
3827
  },
2969
3828
  publicForm: {
2970
3829
  submit: 'Submit',
@@ -2975,12 +3834,20 @@ const en = {
2975
3834
  thankYouTitle: 'Thank you!',
2976
3835
  thankYouMessage: 'Your submission has been received successfully.',
2977
3836
  redirecting: 'Redirecting in {{seconds}} seconds…',
3837
+ // The console form page's line for a submit whose declared redirect is
3838
+ // pending (objectui#11071). `redirecting` above is the embeddable form's
3839
+ // countdown and carries a `{{seconds}}` hole this page has no value for.
3840
+ redirectPending: 'Redirecting…',
2978
3841
  unavailableTitle: 'Form unavailable',
2979
3842
  unavailableDescription: 'No public form is available at this URL. Make sure the underlying view has anonymous sharing enabled and matches this slug.',
2980
3843
  tryDemo: 'Try the demo',
2981
3844
  retry: 'Retry',
2982
3845
  loading: 'Loading form…',
2983
3846
  requiredHint: '* Required field',
3847
+ // The console form page's client-side refusal when required rows are empty
3848
+ // (objectui#11071). `{{fields}}` is the empty rows' labels joined with a
3849
+ // comma; the colon, its spacing and the order are the pack's.
3850
+ requiredFields: 'Required: {{fields}}',
2984
3851
  consentLabelDefault: 'I agree to the privacy policy and consent to my data being processed for this request.',
2985
3852
  consentLink: 'Privacy policy',
2986
3853
  consentRequired: 'Please accept the privacy policy to continue.',
@@ -2993,10 +3860,10 @@ const en = {
2993
3860
  body: 'The MCP surface is off (OS_MCP_SERVER_ENABLED=false), so there is nothing to connect. Ask your operator to re-enable it.',
2994
3861
  },
2995
3862
  url: {
2996
- title: 'This environment\'s MCP endpoint',
3863
+ title: 'This system\'s MCP endpoint',
2997
3864
  body: 'Any MCP-capable AI client connects with this URL. Identity is self-serve: the deployment is its own OAuth 2.1 authorization server, so interactive clients just open a browser login — you connect as yourself, and every call runs under your own permissions and row-level security.',
2998
3865
  downloadSkill: 'Download SKILL.md',
2999
- skillHint: 'The portable agent skill: teaches any skills-capable agent how to work with this environment.',
3866
+ skillHint: 'The portable agent skill: teaches any skills-capable agent how to work with this system.',
3000
3867
  },
3001
3868
  claude: {
3002
3869
  body: 'Settings → Connectors → Add custom connector, then paste this URL. Sign in through the browser when prompted.',
@@ -3031,18 +3898,97 @@ const en = {
3031
3898
  noImportableFields: 'That object has no importable fields.',
3032
3899
  schemaReadFailed: 'Could not read the object schema.',
3033
3900
  imported: 'Imported {{count}} row(s) into {{object}}.',
3901
+ imported_one: 'Imported {{count}} row into {{object}}.',
3902
+ imported_other: 'Imported {{count}} rows into {{object}}.',
3034
3903
  importFrom: 'Import the real rows from',
3035
3904
  into: 'into',
3036
3905
  opening: 'Opening…',
3037
3906
  importAction: 'Import',
3038
3907
  dismiss: 'Dismiss',
3039
3908
  },
3040
- cloudOnboarding: {
3041
- hintCreate: 'Spin up your first environment — a private workspace with its own URL, database, and plan. Building happens inside it.',
3042
- hintReady: 'Your production environment is ready. Open it to build and run your apps — that all happens inside the environment.',
3043
- createEnvironment: 'Create your environment',
3044
- openProduction: 'Open Production',
3045
- manageEnvironments: 'Manage environments',
3909
+ // `cloud:workspace-timezone-notice` — the Cloud welcome page's line naming the
3910
+ // timezone a workspace was seeded with at creation (objectui#11930). `{{zone}}`
3911
+ // is the seed, an IANA id printed verbatim.
3912
+ cloudWorkspaceTimezoneNotice: {
3913
+ seeded: 'The environment timezone was set to {{zone}} from your browser when the environment was created. You can change it in Settings → Localization.',
3914
+ },
3915
+ // `@object-ui/plugin-ai` — the `nl-query`, `ai-form-assist` and
3916
+ // `ai-recommendations` components (objectui#10232). The two count labels are
3917
+ // i18next count families (objectui#11445): the component passes `count` and
3918
+ // each pack carries every CLDR category its language selects.
3919
+ ai: {
3920
+ nlQuery: {
3921
+ placeholder: 'Ask a question about your data…',
3922
+ ask: 'Ask',
3923
+ results: 'Results',
3924
+ match: '{{percent}} match',
3925
+ simulatedSummary: 'Results for: {{query}}',
3926
+ noResults: 'No matching records found',
3927
+ recentQueries: 'Recent Queries',
3928
+ },
3929
+ formAssist: {
3930
+ title: 'AI Suggestions',
3931
+ suggestionCount: '{{count}} suggestions',
3932
+ suggestionCount_one: '{{count}} suggestion',
3933
+ suggestionCount_other: '{{count}} suggestions',
3934
+ applyAll: 'Apply All',
3935
+ confidence: '{{percent}} confidence',
3936
+ appliedCount: '{{count}} suggestions applied',
3937
+ appliedCount_one: '{{count}} suggestion applied',
3938
+ appliedCount_other: '{{count}} suggestions applied',
3939
+ },
3940
+ recommendations: {
3941
+ title: 'Recommendations',
3942
+ generating: 'Generating recommendations…',
3943
+ empty: 'No recommendations available',
3944
+ },
3945
+ },
3946
+ // The AI HITL approval inbox (`@object-ui/plugin-chatbot`'s
3947
+ // `AiPendingActionsInbox`) — objectui#7173. Its four relative-time phrases
3948
+ // are NOT here: it borrows `detail.justNow` / `minutesAgo` / `hoursAgo` /
3949
+ // `daysAgo`, already translated in all ten packs, the way `ObjectGrid` and
3950
+ // `ObjectKanban` borrow `detail.recordDetail`. Distinct from
3951
+ // `approvalsInbox` above, which is the human approval-PROCESS inbox:
3952
+ // different surface, different feature, so no rows are shared with it.
3953
+ aiApprovals: {
3954
+ title: 'AI Approvals',
3955
+ description: 'Actions an AI agent proposed that need a human review before execution.',
3956
+ tabPending: 'Pending',
3957
+ tabDecided: 'Decided',
3958
+ tabAll: 'All',
3959
+ statusPending: 'Pending',
3960
+ statusApproved: 'Approved',
3961
+ statusExecuted: 'Executed',
3962
+ statusFailed: 'Failed',
3963
+ statusRejected: 'Rejected',
3964
+ colTool: 'Tool',
3965
+ colAction: 'Action',
3966
+ colObject: 'Object',
3967
+ colStatus: 'Status',
3968
+ colProposed: 'Proposed',
3969
+ colDecision: 'Decision',
3970
+ emptyTitle: 'No actions waiting',
3971
+ emptyDescription: 'When the AI proposes a sensitive action it will appear here for review.',
3972
+ view: 'View',
3973
+ approve: 'Approve',
3974
+ reject: 'Reject',
3975
+ working: 'Working…',
3976
+ approveAndExecute: 'Approve & Execute',
3977
+ outcomeApprove: 'Approve for {{id}}: {{message}}',
3978
+ outcomeReject: 'Reject for {{id}}: {{message}}',
3979
+ outcomeExecuteFailed: 'Action failed during execution',
3980
+ drawerFallbackTitle: 'Pending action',
3981
+ drawerSubtitle: 'Tool {{tool}} on {{object}}',
3982
+ fieldProposedBy: 'Proposed by',
3983
+ fieldDecidedBy: 'Decided by',
3984
+ fieldConversation: 'Conversation',
3985
+ fieldToolInput: 'Tool input',
3986
+ fieldResult: 'Result',
3987
+ fieldError: 'Error',
3988
+ fieldRejectionReason: 'Rejection reason',
3989
+ rejectTitle: 'Reject this action?',
3990
+ rejectBody: 'The reason is shown back to the AI so it can adjust its next response.',
3991
+ rejectPlaceholder: "Optional reason (e.g. 'Wrong record id — please confirm with the user first.')",
3046
3992
  },
3047
3993
  aiModelStatus: {
3048
3994
  summary: 'Build / Ask uses {{conversational}} ({{conversationalSource}}); structured uses {{structured}} ({{structuredSource}}).',
@@ -3052,8 +3998,8 @@ const en = {
3052
3998
  rowConversational: 'Build / Ask model',
3053
3999
  rowStructured: 'Structured (blueprint / seed)',
3054
4000
  rowReasoning: 'Reasoning effort',
3055
- readFailed: 'Couldn\'t read the effective AI model. This environment may not run an AI service, or you may lack the ai:read permission.',
3056
- readFailedWithStatus: 'Couldn\'t read the effective AI model (HTTP {{status}}). This environment may not run an AI service, or you may lack the ai:read permission.',
4001
+ readFailed: 'Couldn\'t read the effective AI model. This system may not run an AI service, or you may lack the ai:read permission.',
4002
+ readFailedWithStatus: 'Couldn\'t read the effective AI model (HTTP {{status}}). This system may not run an AI service, or you may lack the ai:read permission.',
3057
4003
  overridesInEffect: 'Overrides in effect: ',
3058
4004
  noOverrides: 'none — running the deployed code defaults.',
3059
4005
  adapter: 'Adapter: ',
@@ -3061,6 +4007,184 @@ const en = {
3061
4007
  sourceInherits: 'same as build/ask',
3062
4008
  sourcePinned: 'pinned by {{source}}',
3063
4009
  },
4010
+ // objectui#7254 — the AI copilot's tool cards. Three families, all of them
4011
+ // English-only until this landed while every other string on the same screen
4012
+ // was translated:
4013
+ //
4014
+ // `tool.*` — one entry per PLATFORM-PROVIDED tool name
4015
+ // (@objectstack/spec `PLATFORM_TOOLS_BY_PACKAGE`, the
4016
+ // closed registry those runtimes are conformance-tested
4017
+ // against). `humanizeToolName` looks each up as
4018
+ // `chatbot.tool.<tool_name>` and falls back to its English
4019
+ // title-caser for a custom / third-party tool, so a name
4020
+ // missing here is degraded, never broken. Every value, `en`
4021
+ // included, is a short sentence in the pack's language
4022
+ // saying what the step does (cloud#2927 Target 3, ruled
4023
+ // 「同意」; objectui#12724): never the tool id, and never
4024
+ // the title-caser's output for it, which is how the step
4025
+ // reads for a tool the pack has no entry for.
4026
+ // `toolLabels-locale-parity-7481` holds both.
4027
+ // `toolState.*` — the card-header badge + activity-chip vocabulary. ONE
4028
+ // set for both surfaces (they used to carry separate
4029
+ // tables and disagreed on casing).
4030
+ // `build.*` — the apply_blueprint BUILD PANEL's own copy (objectui#7388).
4031
+ // Everything else on that panel is fed by the host as an
4032
+ // already-translated prop; these strings were literals in
4033
+ // the component, so they stayed English in every language.
4034
+ // `building`/`built` interpolate `appFallback` when the
4035
+ // build has no app label, so each pack must keep BOTH
4036
+ // frames in the one case/gender that noun phrase is in.
4037
+ // `verifying`/`verifyStep`/`verified` are the post-apply
4038
+ // verification line and `unknownPhase` the warning for a
4039
+ // phase outside the spec's vocabulary (objectui#11988).
4040
+ // `plan.*` — the "N objects · N views · N dashboards" strip. Plural
4041
+ // FAMILIES: one slot per CLDR category the pack's language
4042
+ // selects (objectui#11432), plus the base key for a call
4043
+ // made without a count. `all-locales-key-parity.test.ts`
4044
+ // derives the required slots from `Intl.PluralRules`.
4045
+ //
4046
+ // objectui#7481 — five `tool.*` entries are NEWER than the registry above:
4047
+ // `get_authoring_rules` (cloud#1837), `load_tools`, `open_record`, `test_flow`
4048
+ // and `toggle_flow` are registered by cloud `service-ai-studio` but the pinned
4049
+ // `@objectstack/spec` snapshot does not list them yet. They are kept here on
4050
+ // purpose: a step label the user reads must not wait on a pin bump. When the
4051
+ // pin advances, `chatbotToolLabels-locale-parity-7481` starts checking them
4052
+ // against the registry instead of against its own hand-held list.
4053
+ chatbot: {
4054
+ answer: {
4055
+ showFull: 'Show full answer',
4056
+ collapse: 'Collapse',
4057
+ expandTable: 'Expand',
4058
+ exitExpandedTable: 'Close',
4059
+ },
4060
+ tool: {
4061
+ aggregate_data: 'Aggregate the data',
4062
+ get_record: 'Read the record',
4063
+ query_data: 'Query the data',
4064
+ query_records: 'Look up the records',
4065
+ search_knowledge: 'Search the knowledge base',
4066
+ visualize_data: 'Chart the data',
4067
+ add_field: 'Add a field',
4068
+ apply_blueprint: 'Build the app from the plan',
4069
+ apply_edit: 'Apply the change',
4070
+ create_metadata: 'Create a metadata item',
4071
+ create_object: 'Create an object',
4072
+ create_package: 'Create a package',
4073
+ create_seed: 'Generate sample data',
4074
+ delete_field: 'Delete a field',
4075
+ describe_metadata: 'Look at the metadata',
4076
+ describe_object: 'Look at the object structure',
4077
+ get_active_package: 'Check the active package',
4078
+ get_authoring_rules: 'Read the authoring rules',
4079
+ get_metadata_schema: 'Read the metadata format',
4080
+ get_package: 'Look at the package',
4081
+ list_metadata: 'List the existing metadata',
4082
+ list_objects: 'List the existing objects',
4083
+ list_packages: 'List the existing packages',
4084
+ load_tools: 'Load more tools',
4085
+ modify_field: 'Change a field',
4086
+ open_record: 'Open the record',
4087
+ propose_blueprint: 'Draft the app plan',
4088
+ set_active_package: 'Switch to the target package',
4089
+ suggest_builder: 'Suggest building it with AI',
4090
+ test_flow: 'Try out the flow',
4091
+ todo_write: 'Note follow-up checks',
4092
+ toggle_flow: 'Turn the flow on or off',
4093
+ update_metadata: 'Update a metadata item',
4094
+ validate_expression: 'Check the expression',
4095
+ verify_build: 'Check the build',
4096
+ },
4097
+ toolState: {
4098
+ agentActivity: 'Agent activity',
4099
+ pending: 'Pending',
4100
+ running: 'Running',
4101
+ awaitingApproval: 'Awaiting approval',
4102
+ responded: 'Responded',
4103
+ completed: 'Completed',
4104
+ error: 'Error',
4105
+ denied: 'Denied',
4106
+ failed: 'Failed',
4107
+ },
4108
+ phase: {
4109
+ build: 'Build',
4110
+ design: 'Design the plan',
4111
+ stepCount: '{{count}} steps',
4112
+ stepCount_one: '{{count}} step',
4113
+ stepCount_other: '{{count}} steps',
4114
+ expand: 'Show steps',
4115
+ collapse: 'Hide steps',
4116
+ },
4117
+ plan: {
4118
+ countObjects: '{{count}} objects',
4119
+ countObjects_one: '{{count}} object',
4120
+ countObjects_other: '{{count}} objects',
4121
+ countViews: '{{count}} views',
4122
+ countViews_one: '{{count}} view',
4123
+ countViews_other: '{{count}} views',
4124
+ countDashboards: '{{count}} dashboards',
4125
+ countDashboards_one: '{{count}} dashboard',
4126
+ countDashboards_other: '{{count}} dashboards',
4127
+ countSeedData: 'sample data',
4128
+ extendTarget: 'Adding to existing app: {{app}}',
4129
+ awaitingConfirm: 'Awaiting confirmation',
4130
+ objectsAndFields: 'Objects and fields',
4131
+ views: 'Views',
4132
+ suggestedField: 'suggested',
4133
+ nameField: 'Name: {{field}}',
4134
+ },
4135
+ build: {
4136
+ building: 'Building {{app}}…',
4137
+ built: 'Built {{app}}',
4138
+ builtAndLive: 'Built and live · {{app}}',
4139
+ appFallback: 'your app',
4140
+ addingSampleData: 'adding sample data',
4141
+ group: {
4142
+ object: 'Objects',
4143
+ view: 'Views',
4144
+ dashboard: 'Dashboards',
4145
+ app: 'App',
4146
+ seed: 'Sample data',
4147
+ },
4148
+ moreArtifacts: '+{{n}} more',
4149
+ verifying: 'Checking the change…',
4150
+ verifyStep: 'step {{n}}',
4151
+ verified: 'Checked the change',
4152
+ unknownPhase: 'Unknown build phase',
4153
+ nextStepsLead: 'Next, you can say: {{steps}}',
4154
+ nextStepQuote: '“{{step}}”',
4155
+ draftPosture: 'Later changes are saved as drafts, and you publish them in Studio.',
4156
+ checksPassed: 'Checks passed {{passed}}/{{total}}',
4157
+ },
4158
+ changes: {
4159
+ planTitle: 'Change plan · {{count}} changes',
4160
+ planTitle_one: 'Change plan · {{count}} change',
4161
+ planTitle_other: 'Change plan · {{count}} changes',
4162
+ saveDraft: 'Save as draft',
4163
+ reviewAndPublish: 'Review changes and publish',
4164
+ undo: 'Undo',
4165
+ undoWithOthers: 'Undo (and {{count}} other drafts)',
4166
+ undoWithOthers_one: 'Undo (and {{count}} other draft)',
4167
+ undoWithOthers_other: 'Undo (and {{count}} other drafts)',
4168
+ undoConfirm: 'Undo discards all {{total}} unpublished drafts of this package, including {{count}} that are not part of this change.',
4169
+ undoConfirm_one: 'Undo discards all {{total}} unpublished drafts of this package, including {{count}} that is not part of this change.',
4170
+ undoConfirm_other: 'Undo discards all {{total}} unpublished drafts of this package, including {{count}} that are not part of this change.',
4171
+ undoConfirmUnknown: 'Undo discards every unpublished draft of this package, not only this change.',
4172
+ undoConfirmAction: 'Undo all',
4173
+ undoCancel: 'Cancel',
4174
+ undoing: 'Undoing…',
4175
+ undone: 'Undone',
4176
+ undoFailed: 'Undo failed',
4177
+ kind: {
4178
+ field: 'Field',
4179
+ object: 'Object',
4180
+ view: 'View',
4181
+ dashboard: 'Dashboard',
4182
+ app: 'App',
4183
+ seed: 'Sample data',
4184
+ package: 'App package',
4185
+ },
4186
+ },
4187
+ },
3064
4188
  chatbotError: {
3065
4189
  title: 'Response failed',
3066
4190
  fallbackDetail: 'Something went wrong. Please try again.',
@@ -3074,10 +4198,18 @@ const en = {
3074
4198
  buyCredits: 'Buy a credit pack',
3075
4199
  upgradePlan: 'Upgrade plan',
3076
4200
  },
4201
+ // Plan-tier labels, keyed by the TIER rather than by the surface that draws
4202
+ // them, so every plan-locked mark reuses one string (objectui#12190). The zh
4203
+ // value follows the cloud control plane's own zh plan label.
4204
+ planTier: {
4205
+ teamAndUp: 'Team plan and up',
4206
+ },
3077
4207
  environment: {
3078
- addEnvironment: 'Add environment',
3079
4208
  setUpProduction: 'Set up your production environment',
3080
4209
  addDevelopment: 'Add development environment',
4210
+ // The plan-locked stand-in for the create action (objectui#12190).
4211
+ // `{{tier}}` is a `planTier.*` label.
4212
+ developmentLocked: 'Development environments · {{tier}}',
3081
4213
  // Plan/capacity dialog (EnvironmentEntitlementDialog). Shown on a paid
3082
4214
  // conversion path, so it must read in the user's language — cloud#959.
3083
4215
  entitlement: {
@@ -3126,14 +4258,14 @@ const en = {
3126
4258
  },
3127
4259
  unbound: {
3128
4260
  title: 'Not connected',
3129
- body: 'Connect this runtime to an ObjectStack control plane to browse your organization\'s private packages and install them here. Approval is a single click in your cloud account — no ids or credentials are typed into this page.',
4261
+ body: 'This runtime is not connected to a control plane. Connect opens your cloud account in a new window, where one click approves the link — no ids or credentials are typed into this page.',
3130
4262
  connect: 'Connect',
3131
4263
  },
3132
4264
  },
3133
4265
  marketplace: {
3134
4266
  title: 'App Marketplace',
3135
4267
  subtitle: 'Browse approved apps published to the ObjectStack catalog. Click an app to view details and install it into one of your environments.',
3136
- searchPlaceholder: 'Search apps by name or manifest ID…',
4268
+ searchPlaceholder: 'Search apps by name or app ID…',
3137
4269
  searchAria: 'Search marketplace apps',
3138
4270
  installed: 'Installed',
3139
4271
  installedCount: 'Installed ({{count}})',
@@ -3143,21 +4275,26 @@ const en = {
3143
4275
  noMatchFilters: 'No apps match your filters.',
3144
4276
  noDescription: 'No description provided.',
3145
4277
  back: 'Back to marketplace',
3146
- installedTitle: 'Installed Apps',
3147
- installedSubtitle: 'Marketplace packages currently installed into this runtime\'s kernel. Cached manifests live in <code>.objectstack/installed-packages/</code> and survive restarts.',
3148
- installedEmpty: 'No marketplace apps installed in this runtime yet.',
3149
4278
  browseLink: 'Browse the marketplace →',
3150
- installedAdditiveNote: '<strong>Note:</strong> The kernel API is additive only — uninstall removes the on-disk manifest so the package won\'t load on next boot, but the running kernel keeps the app registered until you restart the runtime.',
3151
- installedAt: 'Installed {{when}}',
3152
- installedBy: 'by {{user}}',
3153
- installedPackageId: 'package',
3154
- cachedAs: 'Cached as <code>{{path}}</code>',
4279
+ installedAdditiveNote: 'Uninstall removes the app from the running kernel at once, together with its permission sets and their grants, and deletes the cached manifest so the package does not load at the next start. Records stored in its objects stay in the database.',
3155
4280
  versionBadge: 'v{{version}}',
3156
4281
  installedBadge: 'Installed v{{version}}',
4282
+ // objectui#11645 — an install-local entry this runtime refused to load at
4283
+ // startup (the listing's `notLoaded` marker), read on Installed Apps.
4284
+ notLoaded: {
4285
+ badge: 'Not loaded',
4286
+ protocolIncompatible: 'This runtime did not load this package: it targets protocol {{requiredRange}}, which this runtime does not support.',
4287
+ otherReason: 'This runtime did not load this package ({{code}}).',
4288
+ },
3157
4289
  load: {
3158
4290
  failed: 'Failed to load marketplace',
3159
4291
  failedHintConfigured: 'This runtime reaches the marketplace through the control plane at {{url}}. Check that it is online and reachable from here.',
3160
- failedHintSameOrigin: 'This runtime serves the marketplace catalog itself. Check that the runtime is online.',
4292
+ failedHintSameOrigin: 'The marketplace catalog is reached through this runtime. Check that the runtime is online and can reach the catalog.',
4293
+ loading: 'Loading the marketplace catalog…',
4294
+ slow: 'Still loading — the marketplace catalog is slow to answer.',
4295
+ retry: 'Retry',
4296
+ unreachable: 'The marketplace catalog could not be reached.',
4297
+ answeredError: 'The marketplace catalog answered with an error (HTTP {{status}}).',
3161
4298
  packageFailed: 'Failed to load package',
3162
4299
  notFound: 'Not found.',
3163
4300
  },
@@ -3181,10 +4318,15 @@ const en = {
3181
4318
  purgeSampleData: 'Purge sample data',
3182
4319
  purgeConfirm: 'Delete all sample records seeded by this package? User-added records will NOT be touched.',
3183
4320
  purgeSuccess: 'Removed {{count}} sample record(s).',
4321
+ purgeSuccess_one: 'Removed {{count}} sample record.',
4322
+ purgeSuccess_other: 'Removed {{count}} sample records.',
3184
4323
  purgeNoData: 'No sample records found to purge.',
3185
4324
  reseedQueued: 'Sample data will be re-seeded on next environment access.',
3186
4325
  reseedLocalSuccess: 'Re-seeded sample data: {{inserted}} inserted, {{updated}} updated.',
3187
4326
  reseedPartialErrors: '({{count}} record(s) failed to write)',
4327
+ reseedPartialErrors_one: '({{count}} record failed to write)',
4328
+ reseedPartialErrors_other: '({{count}} records failed to write)',
4329
+ sampleDataKernelUnavailable: 'This control plane has no environment kernel, so sample data cannot be re-seeded or purged from here. Do it from the environment\'s own runtime.',
3188
4330
  updateAvailable: 'Update available',
3189
4331
  },
3190
4332
  action: {
@@ -3194,6 +4336,7 @@ const en = {
3194
4336
  installToCloud: 'Install to cloud…',
3195
4337
  installed: 'Installed',
3196
4338
  installing: 'Installing…',
4339
+ updating: 'Updating…',
3197
4340
  uninstall: 'Uninstall',
3198
4341
  uninstalling: 'Uninstalling…',
3199
4342
  details: 'Details',
@@ -3211,15 +4354,50 @@ const en = {
3211
4354
  environmentPlaceholder: 'Pick an environment',
3212
4355
  includeSampleData: 'Include sample data',
3213
4356
  noEnvs: 'No environments found in your active organization.',
3214
- noPermission: 'You do not have permission to install apps in any environment. Only organization owners and admins can install — ask your workspace admin.',
4357
+ noPermission: 'You do not have permission to install apps in any environment. Only organization owners and admins can install — ask your organization admin.',
3215
4358
  signInFirst: 'You need to sign into ObjectStack Cloud first. Click "Open on cloud" below.',
3216
4359
  success: 'Installed successfully. Open the environment to see the new app.',
3217
4360
  localSuccess: 'Installed v{{version}} to this runtime. "{{name}}" should now appear in the app switcher.',
4361
+ // objectui#12098 — the install answered `hotLoaded: false`: installed, and
4362
+ // the running kernel did not register it (objectstack#22695), so nothing
4363
+ // of it is served before the next restart.
4364
+ localLoadsAtRestart: 'Installed v{{version}} to this runtime. "{{name}}" loads when the runtime next restarts; it does not appear in the app switcher until then.',
3218
4365
  localManifestConflict: '{{message}}\nTip: a local app already owns this manifest_id. Remove it from objectstack.config.ts first.',
3219
4366
  localUnauthorized: 'Sign in to this runtime first, then try again.',
3220
4367
  localMarketplaceUnavailable: 'This runtime has no OS_CLOUD_URL configured, so the marketplace catalog is unreachable.',
3221
4368
  updateTo: 'Update → v{{version}}',
3222
4369
  installedVersion: 'Installed v{{version}}',
4370
+ // objectui#12087 — an install into THIS environment, from its answer
4371
+ // until the runtime serves the package's app (MarketplacePackagePage).
4372
+ deploying: 'Deploying the app…',
4373
+ deployed: 'The app is deployed and ready.',
4374
+ openApp: 'Open {{name}}',
4375
+ deployTimeout: 'The install was recorded, but this environment is not serving the app yet. It may still be deploying — check again shortly. If it never appears, the package may not have loaded, may include no app, or its app may need a permission you do not hold.',
4376
+ checkAgain: 'Check again',
4377
+ // ADR-0131 D6 — the install modes a package's manifest permits, and
4378
+ // what an unmanaged install answers (MarketplacePackagePage).
4379
+ mode: {
4380
+ label: 'Install mode',
4381
+ managed: 'Managed',
4382
+ managedHint: 'The package stays the authority: it is registered as code, receives its upgrades, and its items are not edited here.',
4383
+ unmanaged: 'Unmanaged copy',
4384
+ unmanagedHint: 'Copied once into this runtime as its own metadata: every item is editable here, and the copy receives no upgrade.',
4385
+ required: 'Choose an install mode to install this package.',
4386
+ },
4387
+ unmanagedSuccess: 'Copied v{{version}} of "{{name}}" into this runtime as its own metadata, in the base {{base}}. The copy is editable and receives no upgrade.',
4388
+ unmanagedStrippedLocks: 'The package locked these items. The copy is editable, so their locks were not copied:',
4389
+ unmanagedIgnored: 'This runtime does not support unmanaged installs, so "{{name}}" was installed managed (registered as code) instead of copied. Upgrade the runtime, then uninstall the package and install it again as an unmanaged copy.',
4390
+ },
4391
+ // objectui#12146 — the install dialog opened as an UPDATE of the copy
4392
+ // this environment holds (MarketplacePackagePage).
4393
+ update: {
4394
+ dialogTitle: 'Update {{name}}',
4395
+ dialogDescCurrent: 'Update the copy installed in this environment ({{host}}).',
4396
+ dialogDescPicker: 'Choose the environment whose installed copy to update. You need to be signed into ObjectStack Cloud.',
4397
+ majorNotice: 'This is a major version update (v{{fromMajor}} → v{{toMajor}}), which may include breaking changes.',
4398
+ releaseNotes: 'Read the v{{version}} release notes',
4399
+ success: 'Updated to v{{version}}.',
4400
+ deployTimeout: 'The update was recorded, but this environment is not serving v{{version}} yet. It may still be deploying — check again shortly.',
3223
4401
  },
3224
4402
  // objectui#3546 — the MarketplacePage "Your organization" strip.
3225
4403
  org: {
@@ -3237,7 +4415,8 @@ const en = {
3237
4415
  reviewed: 'Reviewed & approved',
3238
4416
  unreviewed: 'Not yet reviewed',
3239
4417
  signed: 'Signed',
3240
- grantsIntro: 'On install, this package will be granted:',
4418
+ grantsIntro: 'This package requests:',
4419
+ notEnforced: 'Recorded at install, and re-confirmed if a later version asks for more — but the runtime does not yet restrict the package to this list.',
3241
4420
  services: 'Platform services',
3242
4421
  hooks: 'Lifecycle hooks',
3243
4422
  network: 'Network access',
@@ -3261,11 +4440,20 @@ const en = {
3261
4440
  dismiss: 'Dismiss',
3262
4441
  confirmedToast: '"{{set}}" is now granted to {{anchor}}.',
3263
4442
  dismissedToast: 'Suggestion for "{{set}}" dismissed.',
4443
+ guestUnsupported: 'Cannot be granted here: the Guest position takes no sets from accepted suggestions.',
4444
+ acceptedState: '"{{set}}" was accepted for all signed-in users (the Everyone position).',
4445
+ notInForceState: '"{{set}}" was accepted but is not in force yet: the Everyone position did not change on this request. The server log says why.',
4446
+ dismissedState: '"{{set}}" is not granted: the suggestion was dismissed or revoked.',
4447
+ revoke: 'Revoke',
4448
+ revoking: 'Revoking…',
4449
+ revokedToast: 'Acceptance of "{{set}}" revoked.',
3264
4450
  },
3265
4451
  uninstall: {
3266
- confirm: 'Uninstall {{manifestId}} v{{version}} from this runtime?\n\nThe cached manifest will be removed. The app will remain loaded in the running kernel until the next restart.',
3267
- successInList: 'Removed {{manifestId}}. Restart the runtime to fully unload it from the running kernel.',
3268
- successInDetail: 'Removed cached manifest for {{manifestId}}. Restart the runtime to fully unload the app from the running kernel.',
4452
+ confirm: 'Uninstall {{manifestId}} v{{version}} from this runtime?\n\nThe app will leave the running kernel at once, together with its permission sets and their grants, and its cached manifest will be removed. Records stored in its objects stay in the database.',
4453
+ successInList: 'Removed {{manifestId}}: the app has left the running kernel, with its permission sets and their grants. Records stored in its objects stay in the database.',
4454
+ successInDetail: 'Removed {{manifestId}} and its cached manifest: the app has left the running kernel, with its permission sets and their grants. Records stored in its objects stay in the database.',
4455
+ confirmNotLoaded: 'Uninstall {{manifestId}} v{{version}} from this runtime?\n\nThe cached manifest will be removed. This runtime did not load the package, so none of it is running.',
4456
+ successNotLoaded: 'Removed {{manifestId}}. It was not loaded, so no restart is needed.',
3269
4457
  },
3270
4458
  accessDenied: {
3271
4459
  title: 'App Marketplace is admin-only',
@@ -3288,30 +4476,19 @@ const en = {
3288
4476
  storage: 'Storage',
3289
4477
  other: 'Other',
3290
4478
  },
3291
- pricing: {
3292
- free: 'Free',
3293
- freemium: 'Freemium',
3294
- paid: 'Paid',
3295
- subscription: 'Subscription',
3296
- 'usage-based': 'Usage-based',
3297
- 'contact-sales': 'Contact Sales',
3298
- },
3299
4479
  relativeTime: {
3300
4480
  today: 'today',
3301
4481
  daysAgo: '{{count}}d ago',
3302
4482
  monthsAgo: '{{count}}mo ago',
4483
+ monthsAgo_one: '{{count}}mo ago',
4484
+ monthsAgo_other: '{{count}}mo ago',
3303
4485
  yearsAgo: '{{count}}y ago',
4486
+ yearsAgo_one: '{{count}}y ago',
4487
+ yearsAgo_other: '{{count}}y ago',
3304
4488
  },
3305
4489
  },
3306
- approvals: {
3307
- approve: 'Approve',
3308
- reject: 'Reject',
3309
- comment: 'Comment (optional)',
3310
- approveSuccess: 'Approved',
3311
- rejectSuccess: 'Rejected',
3312
- rejectConfirm: 'Reject this approval request?',
3313
- },
3314
4490
  approvalsInbox: {
4491
+ recordUnresolvable: 'This record cannot be opened',
3315
4492
  loadMore: 'Load more',
3316
4493
  loadingMore: 'Loading…',
3317
4494
  loadedOf: 'Loaded {{loaded}} of {{total}}',
@@ -3330,6 +4507,8 @@ const en = {
3330
4507
  requestInfoSent: 'Sent back to the requester for more information',
3331
4508
  remindBtn: 'Send reminder',
3332
4509
  remindSuccess: 'Reminder sent to {{count}} approver(s)',
4510
+ remindSuccess_one: 'Reminder sent to {{count}} approver',
4511
+ remindSuccess_other: 'Reminder sent to {{count}} approvers',
3333
4512
  remindThrottled: 'A reminder was sent recently — try again later.',
3334
4513
  replyPlaceholder: 'Reply on this request…',
3335
4514
  slaRemaining: 'SLA {{dur}} left',
@@ -3346,8 +4525,11 @@ const en = {
3346
4525
  progressApprovals: 'Approvals — {{got}} of {{need}}',
3347
4526
  progressGroups: 'Sign-off progress — {{got}} of {{need}} groups',
3348
4527
  progressEligible: '{{count}} eligible approver(s)',
4528
+ progressEligible_one: '{{count}} eligible approver',
4529
+ progressEligible_other: '{{count}} eligible approvers',
3349
4530
  progressBar: 'Decision progress',
3350
4531
  declaredActions: 'Actions',
4532
+ decisionPanelOffRequestPage: 'The approval decision panel only works on approval request pages.',
3351
4533
  attachmentChip: 'Attachment',
3352
4534
  attachmentOpenFailed: 'Could not open the attachment — please try again',
3353
4535
  approveOneTitle: 'Approve "{{title}}"?',
@@ -3377,6 +4559,8 @@ const en = {
3377
4559
  filterCount: '{{shown}} of {{total}}',
3378
4560
  selected: 'selected',
3379
4561
  actionableCount: '({{count}} actionable)',
4562
+ actionableCount_one: '({{count}} actionable)',
4563
+ actionableCount_other: '({{count}} actionable)',
3380
4564
  selectAll: 'Select all',
3381
4565
  selectRow: 'Select request',
3382
4566
  colRequest: 'Request',
@@ -3411,6 +4595,9 @@ const en = {
3411
4595
  emptyPending: "You're all caught up — nothing is waiting on you.",
3412
4596
  emptyOther: 'Nothing here yet.',
3413
4597
  emptyViewAll: 'Browse all requests',
4598
+ waitingOnOthers: '{{count}} requests are waiting on other approvers',
4599
+ waitingOnOthers_one: '{{count}} request is waiting on other approvers',
4600
+ waitingOnOthers_other: '{{count}} requests are waiting on other approvers',
3414
4601
  noMatches: 'No matches for current filters.',
3415
4602
  keyboardHint: 'Keyboard: j/k move · Enter open · x select · a approve · r reject',
3416
4603
  drawerTitle: 'Approval Request',
@@ -3428,12 +4615,12 @@ const en = {
3428
4615
  overrideActionLabel: 'Override {{action}}',
3429
4616
  overrideNotice: 'You hold no approver slot on this step. Continuing uses your admin override: it finalises the step immediately, bypassing any approver who has not acted. The decision is recorded as an admin override.',
3430
4617
  overrideNoticeWho: 'You hold no approver slot on this step. Continuing uses your admin override: it finalises the step immediately and bypasses the approvers who have not acted — {{who}}. The decision is recorded as an admin override.',
4618
+ overrideReassignNotice: 'You hold no approver slot on this step. Continuing uses your admin override: the step moves to the new approver you pick, replacing any approver who has not acted. The request stays pending and waits on the new approver. The reassignment is recorded as an admin override.',
4619
+ overrideReassignNoticeWho: 'You hold no approver slot on this step. Continuing uses your admin override: the step moves to the new approver you pick, replacing the approvers who have not acted — {{who}}. The request stays pending and waits on the new approver. The reassignment is recorded as an admin override.',
4620
+ overrideRecallNotice: 'You did not submit this request. Continuing uses your admin override: the request is withdrawn, any approver who has not acted can no longer act on it, and the record is unlocked.',
4621
+ overrideRecallNoticeWho: 'You did not submit this request. Continuing uses your admin override: the request is withdrawn, the record is unlocked, and the approvers who have not acted can no longer act on it — {{who}}.',
3431
4622
  viaOverrideChip: 'Admin override',
3432
4623
  viaOverrideHint: 'The actor held no approver slot on this step — admitted by the admin-override path.',
3433
- rawData: 'Raw data (JSON)',
3434
- copy: 'Copy',
3435
- copied: 'Copied',
3436
- copyFailed: 'Copy failed',
3437
4624
  overrideActor: 'Act as another identity (admin)',
3438
4625
  actor: 'Actor',
3439
4626
  auto: 'Auto',
@@ -3450,11 +4637,19 @@ const en = {
3450
4637
  approveN: 'Approve {{count}}',
3451
4638
  rejectN: 'Reject {{count}}',
3452
4639
  bulkApproveTitle: 'Approve {{count}} requests?',
4640
+ bulkApproveTitle_one: 'Approve {{count}} request?',
4641
+ bulkApproveTitle_other: 'Approve {{count}} requests?',
3453
4642
  bulkApproveBody: 'Each request is approved with your identity and its flow continues down the approve branch.',
3454
4643
  bulkRejectTitle: 'Reject {{count}} requests?',
4644
+ bulkRejectTitle_one: 'Reject {{count}} request?',
4645
+ bulkRejectTitle_other: 'Reject {{count}} requests?',
3455
4646
  bulkRejectBody: 'This rejects the selected requests and notifies their submitters.',
3456
4647
  bulkApproved: 'Approved {{count}} requests',
4648
+ bulkApproved_one: 'Approved {{count}} request',
4649
+ bulkApproved_other: 'Approved {{count}} requests',
3457
4650
  bulkRejected: 'Rejected {{count}} requests',
4651
+ bulkRejected_one: 'Rejected {{count}} request',
4652
+ bulkRejected_other: 'Rejected {{count}} requests',
3458
4653
  bulkPartial: '{{ok}} succeeded, {{fail}} failed: {{which}}',
3459
4654
  rejectOneTitle: 'Reject "{{title}}"?',
3460
4655
  rejectOneBody: 'This rejects the request and notifies the submitter.',
@@ -3479,8 +4674,14 @@ const en = {
3479
4674
  alreadyDecided: 'This request was already decided. Refresh the list.',
3480
4675
  justNow: 'just now',
3481
4676
  minutesAgo: '{{count}}m ago',
4677
+ minutesAgo_one: '{{count}}m ago',
4678
+ minutesAgo_other: '{{count}}m ago',
3482
4679
  hoursAgo: '{{count}}h ago',
4680
+ hoursAgo_one: '{{count}}h ago',
4681
+ hoursAgo_other: '{{count}}h ago',
3483
4682
  daysAgo: '{{count}}d ago',
4683
+ daysAgo_one: '{{count}}d ago',
4684
+ daysAgo_other: '{{count}}d ago',
3484
4685
  },
3485
4686
  filterBuilder: {
3486
4687
  where: 'Where',
@@ -3504,28 +4705,32 @@ const en = {
3504
4705
  rangeEnd: 'To',
3505
4706
  operators: {
3506
4707
  equals: 'Equals',
3507
- notEquals: 'Does not equal',
4708
+ not_equals: 'Does not equal',
3508
4709
  contains: 'Contains',
3509
- containsCaseInsensitive: 'Contains (ignore case)',
3510
- notContains: 'Does not contain',
3511
- isEmpty: 'Is empty',
3512
- isNotEmpty: 'Is not empty',
3513
- greaterThan: 'Greater than',
3514
- lessThan: 'Less than',
3515
- greaterOrEqual: 'Greater than or equal',
3516
- lessOrEqual: 'Less than or equal',
4710
+ icontains: 'Contains (ignore case)',
4711
+ not_contains: 'Does not contain',
4712
+ is_empty: 'Is empty',
4713
+ is_not_empty: 'Is not empty',
4714
+ greater_than: 'Greater than',
4715
+ less_than: 'Less than',
4716
+ greater_than_or_equal: 'Greater than or equal',
4717
+ less_than_or_equal: 'Less than or equal',
3517
4718
  before: 'Before',
3518
4719
  after: 'After',
3519
4720
  between: 'Between',
3520
4721
  in: 'In',
3521
- notIn: 'Not in',
3522
- startsWith: 'Starts with',
3523
- endsWith: 'Ends with',
3524
- isNull: 'Is null',
3525
- isNotNull: 'Is not null',
4722
+ not_in: 'Not in',
4723
+ starts_with: 'Starts with',
4724
+ ends_with: 'Ends with',
4725
+ is_null: 'Is null',
4726
+ is_not_null: 'Is not null',
3526
4727
  exists: 'Is set',
3527
4728
  notExists: 'Is not set',
3528
4729
  },
4730
+ emptyCheckHint: {
4731
+ text: '"{{isEmpty}}" also matches blank text; "{{isNull}}" matches only a missing value.',
4732
+ list: '"{{isEmpty}}" also matches an empty list; "{{isNull}}" matches only a missing value.',
4733
+ },
3529
4734
  },
3530
4735
  sortBuilder: {
3531
4736
  sortBy: 'Sort by',
@@ -3545,13 +4750,11 @@ const en = {
3545
4750
  // The generic action words (Save / Cancel / Edit / Delete) are NOT repeated
3546
4751
  // here — the thread reads them from `common`.
3547
4752
  collaboration: {
3548
- // Thread header. `commentCount`/`commentCountOne` are two keys, NOT an
3549
- // i18next `_one`/`_other` pair — see the `reactionCount` note under
3550
- // `detail`: zh/ja/ko have no separate singular form, so those packs would
3551
- // legitimately omit the `_one` half and `all-locales-key-parity` reads a
3552
- // legitimately-absent half as a lost key.
4753
+ // Thread header. An i18next count family (objectui#11445): the component
4754
+ // passes `count` and i18next picks the slot each pack's CLDR rules select.
3553
4755
  commentCount: '{{count}} comments',
3554
- commentCountOne: '{{count}} comment',
4756
+ commentCount_one: '{{count}} comment',
4757
+ commentCount_other: '{{count}} comments',
3555
4758
  // Appended to the count, separator included, so a translator owns the
3556
4759
  // whole phrase rather than inheriting an English-shaped ` · ` glue.
3557
4760
  resolvedSuffix: ' · Resolved',
@@ -3564,14 +4767,21 @@ const en = {
3564
4767
  // existing minute/hour/day buckets and no date library was introduced.
3565
4768
  justNow: 'just now',
3566
4769
  minutesAgo: '{{count}}m ago',
4770
+ minutesAgo_one: '{{count}}m ago',
4771
+ minutesAgo_other: '{{count}}m ago',
3567
4772
  hoursAgo: '{{count}}h ago',
4773
+ hoursAgo_one: '{{count}}h ago',
4774
+ hoursAgo_other: '{{count}}h ago',
3568
4775
  daysAgo: '{{count}}d ago',
4776
+ daysAgo_one: '{{count}}d ago',
4777
+ daysAgo_other: '{{count}}d ago',
3569
4778
  edited: '(edited)',
3570
4779
  // Reaction-chip tooltip. A DEDICATED pair rather than `detail.reactionCount`:
3571
4780
  // that one interpolates `{{emoji}}`, and here the emoji is the chip's
3572
4781
  // visible label with nothing to hand the placeholder.
3573
4782
  reactionCount: '{{count}} reactions',
3574
- reactionCountOne: '{{count}} reaction',
4783
+ reactionCount_one: '{{count}} reaction',
4784
+ reactionCount_other: '{{count}} reactions',
3575
4785
  addThumbsUp: 'Add thumbs up',
3576
4786
  reply: 'Reply',
3577
4787
  // Accessible names for the three emoji-only controls (objectui#3441).
@@ -3597,12 +4807,14 @@ const en = {
3597
4807
  send: 'Send',
3598
4808
  // Presence avatar stack (objectui#3440). `presentUserCount*` is the avatar
3599
4809
  // group's `aria-label` — with only images and initials inside, that label
3600
- // IS the control for a screen reader. Two keys, same reason as
3601
- // `commentCount` above.
4810
+ // IS the control for a screen reader. Count families, like `commentCount`
4811
+ // above.
3602
4812
  presentUserCount: '{{count}} users present',
3603
- presentUserCountOne: '{{count}} user present',
4813
+ presentUserCount_one: '{{count}} user present',
4814
+ presentUserCount_other: '{{count}} users present',
3604
4815
  moreUserCount: '{{count}} more users',
3605
- moreUserCountOne: '{{count}} more user',
4816
+ moreUserCount_one: '{{count}} more user',
4817
+ moreUserCount_other: '{{count}} more users',
3606
4818
  // Avatar tooltip. The parentheses are part of the translation, spacing
3607
4819
  // included, so the CJK packs can drop the space English puts before `(`.
3608
4820
  userStatusTitle: '{{name}} ({{status}})',
@@ -3617,6 +4829,25 @@ const en = {
3617
4829
  // comma-joined, already-truncated label list built at the call site.
3618
4830
  wizard: {
3619
4831
  missingRequired: 'Please complete the required fields: {{fields}}',
4832
+ // The wizard's footer and step chrome (objectui#10999). The generic verbs
4833
+ // it shows come from `common.cancel`, `common.next`, `form.create`,
4834
+ // `form.update` and `form.stepOf`; these five are the wizard's own.
4835
+ // The Previous-step button. Not `common.back`: some packs say a wizard's
4836
+ // step back with a different word from a page's back (zh reads "previous
4837
+ // step" here and "return" for `common.back`), and the wizard spelling is
4838
+ // the one `grid.import.back` and `grid.bulk.back` already use.
4839
+ back: 'Back',
4840
+ // The final button while the record is being written.
4841
+ submitting: 'Submitting…',
4842
+ // The step indicator's label for a step that declares no `label`.
4843
+ // `{{n}}` is the 1-based step number.
4844
+ stepFallback: 'Step {{n}}',
4845
+ // The step indicator's `aria-label` — what a screen reader calls the list
4846
+ // of steps.
4847
+ progressLabel: 'Progress',
4848
+ // Shown in place of the fields on a step that has none (a final review
4849
+ // step is the usual case).
4850
+ emptyStep: 'No fields configured for this step',
3620
4851
  },
3621
4852
  // The screen-flow runner dialog (`app-shell/views/FlowRunner`) — the modal a
3622
4853
  // `type: 'flow'` action opens when its run pauses at a `screen` node.
@@ -3704,6 +4935,11 @@ const en = {
3704
4935
  setDefaultFailed: 'Failed to set default app: {{reason}}',
3705
4936
  appDeleted: '{{name}} deleted',
3706
4937
  deleteFailed: 'Failed to delete app: {{reason}}',
4938
+ // A `reset: false` answer: no customization row existed, so nothing was
4939
+ // removed. `{{reason}}` is the server's own sentence, passed through raw.
4940
+ nothingToDelete: 'Nothing to delete for {{name}}: {{reason}}',
4941
+ // `{{reason}}` above when that answer carried no sentence of its own.
4942
+ alreadyAtDefault: 'it is already at its package default',
3707
4943
  // `{{n}}` is the count that actually LANDED, not the selection size — a
3708
4944
  // bulk toggle is N independent writes with no transaction behind them.
3709
4945
  bulkEnabled: '{{n}} apps enabled',
@@ -3720,5 +4956,188 @@ const en = {
3720
4956
  bulkOperationFailed: 'Bulk operation failed: {{reason}}',
3721
4957
  },
3722
4958
  },
4959
+ // objectui#6301 — Setup › Packaged automation (ADR-0126 §7.4): the
4960
+ // operational surface for the flows an installed package ships. Authoring
4961
+ // stays in Studio, so this group has no editing vocabulary at all; it is
4962
+ // on/off, clone, and the states those two produce.
4963
+ //
4964
+ // ⛔ No drift or ancestry wording anywhere in this group (ADR-0126 §9). There
4965
+ // is deliberately no "customized", no "based on v3", no "out of date" — the
4966
+ // platform does not track that lineage, and a translatable string for it is
4967
+ // the cheapest way for the surface to grow one.
4968
+ //
4969
+ // ⛔ Server refusals are NOT in this group either. The posture gate, the
4970
+ // subflow guard and the clone name conflict all arrive as server-authored
4971
+ // prose and are rendered verbatim; the four `*Failed*` keys below are the
4972
+ // last-resort fallbacks for a response that carried no message at all.
4973
+ packagedAutomation: {
4974
+ title: 'Packaged automation',
4975
+ // Covers BOTH sections since ADR-0126 §8 item 2 put packaged actions on
4976
+ // this page: only flows can be cloned, so the clone clause names them.
4977
+ subtitle: 'Flows and actions shipped by installed packages. Turn one off for this deployment, or clone a flow under a new name to customize it. Editing happens in Studio.',
4978
+ refresh: 'Refresh',
4979
+ // Section headings — the page carries two tables.
4980
+ flowsHeading: 'Packaged flows',
4981
+ actionsHeading: 'Packaged actions',
4982
+ colFlow: 'Flow',
4983
+ colActivation: 'Activation',
4984
+ colActions: 'Actions',
4985
+ // The switch's accessible name — the only place a row's label is spoken.
4986
+ toggleLabel: 'Activation for {{label}}',
4987
+ on: 'On',
4988
+ off: 'Off',
4989
+ // The Activation badge on a flow whose switch is ON but whose trigger the
4990
+ // engine has not armed on this deployment (objectui#13159): the switch
4991
+ // keeps the flag, this says the flow does not run. The same words as
4992
+ // Studio's rail for the same state (`engine.studio.auto.notRunning`).
4993
+ notRunning: 'Not running here',
4994
+ clone: 'Clone',
4995
+ cloneTitle: 'Clone packaged flow',
4996
+ cloneBody: 'The copy carries the whole definition and takes a new machine name and label. Edit the copy in Studio.',
4997
+ cloneName: 'New machine name',
4998
+ cloneLabel: 'New label',
4999
+ cancel: 'Cancel',
5000
+ cloneConfirm: 'Create clone',
5001
+ cloneCreated: 'Created flow "{{name}}".',
5002
+ // The clone result's next steps (objectui#13270): switch the flow the
5003
+ // admin cloned from off through its own toggle, or open the clone where
5004
+ // it is edited. The sharing-rule clone's words for the same offer.
5005
+ cloneSwitchSourceOff: 'Switch "{{label}}" off',
5006
+ cloneSourceOff: 'The packaged flow "{{label}}" is switched off.',
5007
+ cloneOpen: 'Open the clone in Studio',
5008
+ emptyTitle: 'No packaged flows',
5009
+ emptyBody: 'No installed package ships an automation flow on this deployment. Flows you author yourself live in Studio.',
5010
+ loadFailed: 'Could not load packaged automation.',
5011
+ // Two keys per action, not one: the response arm has an HTTP status to
5012
+ // name and the transport-exception arm does not, and a single key cannot
5013
+ // carry a hole only half its call sites can fill.
5014
+ // Artifact-neutral by wording, and shared by BOTH sections' toggles: the
5015
+ // sentence says nothing about flows, so the actions half reuses it rather
5016
+ // than defining a second key with the same English in ten packs.
5017
+ toggleFailedHttp: 'Could not change activation (HTTP {{status}}).',
5018
+ toggleFailed: 'Could not change activation.',
5019
+ cloneFailedHttp: 'Could not clone this flow (HTTP {{status}}).',
5020
+ cloneFailed: 'Could not clone this flow.',
5021
+ // The chip an unarmed flow row's platform sentence opens from
5022
+ // (objectui#12609). ⛔ Generic on purpose: the same word on every row that
5023
+ // carries a sentence, whatever the sentence says. The sentence itself is
5024
+ // the server's and is never kept in a pack; a label naming its cause would
5025
+ // be the console classifying a field the contract says not to parse.
5026
+ reasonDetails: 'Details',
5027
+ // ── Packaged ACTIONS section (ADR-0126 §8 item 2, ruling 3) ────────────
5028
+ // ⛔ No clone key here on purpose: the action-clone half is unchartered,
5029
+ // and a string is the cheapest way for an unchartered surface to appear.
5030
+ actionsSubtitle: 'Actions shipped by installed packages. Turn one off for this deployment and it stops running everywhere it is offered. Authoring your own action alongside it stays open in Studio.',
5031
+ colAction: 'Action',
5032
+ colObject: 'Object',
5033
+ // The object is part of the accessible name because it is part of the
5034
+ // identity — two objects may declare the same action name.
5035
+ actionToggleLabel: 'Activation for {{label}} on {{object}}',
5036
+ actionsEmptyTitle: 'No packaged actions',
5037
+ actionsEmptyBody: 'No installed package declares an action on this deployment. Actions you author yourself live in Studio.',
5038
+ actionsLoadFailed: 'Could not load packaged actions.',
5039
+ },
5040
+ element: {
5041
+ // objectui#10951 — `element:number` authored with an aggregate and no
5042
+ // `dataSource.object` (objectui#11880: the flat `object` is not read). Kept
5043
+ // terse: this pack is eager, and the console closure budget weighs it.
5044
+ number: {
5045
+ noObject: 'No object named: set dataSource.object.',
5046
+ // objectui#12910 — the title of the panel this block draws when its
5047
+ // `dataSource.view` names nothing.
5048
+ sourceUnresolved: 'This metric’s data source could not be resolved',
5049
+ },
5050
+ // objectui#12910 — `element:image` with no `src`.
5051
+ image: {
5052
+ noSource: 'No image source',
5053
+ },
5054
+ // objectui#12910 — `element:record_picker`'s panel title, same case.
5055
+ recordPicker: {
5056
+ sourceUnresolved: 'This record picker’s data source could not be resolved',
5057
+ },
5058
+ // objectui#12920 — the same panel title for every other data-bound block,
5059
+ // one key per block kind. `element:number` above also titles the
5060
+ // dashboard's `object-metric`; `component` is the @object-ui/react
5061
+ // panel's own default, for a block that passes no title.
5062
+ calendar: {
5063
+ sourceUnresolved: 'This calendar’s data source could not be resolved',
5064
+ },
5065
+ chart: {
5066
+ sourceUnresolved: 'This chart’s data source could not be resolved',
5067
+ },
5068
+ pivotTable: {
5069
+ sourceUnresolved: 'This pivot table’s data source could not be resolved',
5070
+ },
5071
+ detailView: {
5072
+ sourceUnresolved: 'This detail view’s data source could not be resolved',
5073
+ },
5074
+ relatedList: {
5075
+ sourceUnresolved: 'This related list’s data source could not be resolved',
5076
+ // The panel drawn when the list's `parentDataSource` read fails.
5077
+ parentUnreadable: 'This related list’s parent record could not be read',
5078
+ },
5079
+ form: {
5080
+ sourceUnresolved: 'This form’s data source could not be resolved',
5081
+ },
5082
+ lineItems: {
5083
+ sourceUnresolved: 'This line-items panel’s data source could not be resolved',
5084
+ },
5085
+ gantt: {
5086
+ sourceUnresolved: 'This gantt chart’s data source could not be resolved',
5087
+ },
5088
+ grid: {
5089
+ sourceUnresolved: 'This grid’s data source could not be resolved',
5090
+ },
5091
+ board: {
5092
+ sourceUnresolved: 'This board’s data source could not be resolved',
5093
+ },
5094
+ listView: {
5095
+ sourceUnresolved: 'This list view’s data source could not be resolved',
5096
+ },
5097
+ map: {
5098
+ sourceUnresolved: 'This map’s data source could not be resolved',
5099
+ },
5100
+ timeline: {
5101
+ sourceUnresolved: 'This timeline’s data source could not be resolved',
5102
+ },
5103
+ component: {
5104
+ sourceUnresolved: 'This component’s data source could not be resolved',
5105
+ // The panel drawn when a block resolved no adapter at all.
5106
+ noDataSource: 'No data source resolved',
5107
+ // objectui#12197 — the two titles of a page element's API binding
5108
+ // (`dataSource: { provider: 'api', … }`): refused by the spec's schema,
5109
+ // or read and failed.
5110
+ apiRefused: 'This component’s API data source is refused',
5111
+ apiUnreadable: 'This component’s API data source could not be read',
5112
+ },
5113
+ // objectui#12910 — `element:metadata_viewer` (the ```metadata doc fence).
5114
+ // A `{{name}}`-style slot that the renderer draws as a `<code>` chip is
5115
+ // filled with a private-use mark and split on, so each language places
5116
+ // the chip. Counts read as "Label: N" so no key needs a plural family.
5117
+ metadataViewer: {
5118
+ missingKey: 'Missing {{key}} for this view.',
5119
+ loading: 'Loading {{name}}…',
5120
+ loadFailed: 'Failed to load {{name}}: {{message}}',
5121
+ notFound: '{{name}} not found.',
5122
+ stateMachine: 'State machine',
5123
+ flow: 'Flow',
5124
+ permission: 'Permission',
5125
+ noStateMachine: 'No state machine on {{object}}.',
5126
+ noStateMachineNamed: 'No state machine named “{{name}}” on {{object}}.',
5127
+ lifecycle: 'Lifecycle of {{object}}',
5128
+ field: 'field {{field}}',
5129
+ initial: 'initial',
5130
+ final: 'final',
5131
+ steps: 'Steps: {{count}}',
5132
+ noSteps: 'No steps to show.',
5133
+ untitledStep: '(step)',
5134
+ hiddenSteps: 'Technical steps hidden: {{count}} · set {{setting}} to show all',
5135
+ objectAccess: 'Object access: {{count}}',
5136
+ noPermissions: 'No object permissions declared.',
5137
+ object: 'Object',
5138
+ unknownType: 'Unknown metadata view type. Expected {{a}}, {{b}} or {{c}}.',
5139
+ unknownTypeNamed: 'Unknown metadata view type “{{type}}”. Expected {{a}}, {{b}} or {{c}}.',
5140
+ },
5141
+ },
3723
5142
  };
3724
5143
  export default en;