@object-ui/i18n 17.2.0 → 17.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1817 -1
- package/README.md +26 -13
- package/dist/index.d.ts +3 -1
- package/dist/index.js +14 -1
- package/dist/locales/ar.d.ts +550 -107
- package/dist/locales/ar.js +646 -156
- package/dist/locales/de.d.ts +550 -107
- package/dist/locales/de.js +661 -178
- package/dist/locales/en.d.ts +586 -143
- package/dist/locales/en.js +851 -143
- package/dist/locales/es.d.ts +550 -107
- package/dist/locales/es.js +647 -160
- package/dist/locales/fr.d.ts +550 -107
- package/dist/locales/fr.js +640 -155
- package/dist/locales/index.d.ts +5587 -1157
- package/dist/locales/ja.d.ts +550 -107
- package/dist/locales/ja.js +632 -149
- package/dist/locales/ko.d.ts +550 -107
- package/dist/locales/ko.js +638 -156
- package/dist/locales/pt.d.ts +550 -107
- package/dist/locales/pt.js +639 -157
- package/dist/locales/ru.d.ts +550 -107
- package/dist/locales/ru.js +650 -156
- package/dist/locales/zh.d.ts +588 -145
- package/dist/locales/zh.js +629 -145
- package/dist/pickLocalized.d.ts +21 -0
- package/dist/pickLocalized.js +40 -7
- package/dist/provider.d.ts +125 -1
- package/dist/provider.js +380 -4
- package/dist/useDisplayLocale.d.ts +49 -0
- package/dist/useDisplayLocale.js +55 -0
- package/dist/useObjectLabel.d.ts +8 -2
- package/dist/useObjectLabel.js +28 -8
- package/dist/useSafeTranslation.js +67 -3
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/number-display.d.ts +32 -0
- package/dist/utils/number-display.js +32 -0
- package/dist/utils/spec-formatters.d.ts +39 -13
- package/dist/utils/spec-formatters.js +3 -3
- package/package.json +5 -4
package/dist/locales/en.js
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
* English (en) - Default language pack for Object UI
|
|
3
3
|
*/
|
|
4
4
|
const en = {
|
|
5
|
+
// objectui#4467 — the impersonation banner (app-shell ImpersonationBanner).
|
|
6
|
+
// Raised by `session.impersonatedBy`, so it survives SPA reboots; it names
|
|
7
|
+
// BOTH parties because the audit trail attributes the work to the impersonated
|
|
8
|
+
// user, and the exit states its own failure rather than appearing to succeed.
|
|
9
|
+
impersonation: {
|
|
10
|
+
banner: {
|
|
11
|
+
message: 'You are impersonating {{user}} — every action is recorded as them.',
|
|
12
|
+
startedBy: 'Started by administrator {{admin}}.',
|
|
13
|
+
stop: 'Stop impersonating',
|
|
14
|
+
stopping: 'Stopping…',
|
|
15
|
+
stopFailed: 'Could not stop impersonating: {{reason}}',
|
|
16
|
+
notRestored: 'The server accepted the request but did not restore your administrator session — you are still impersonating {{user}}. Sign out and sign in again to end it.',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
5
19
|
// objectui#2600 B5 — capability picker scope group headers (labels come from
|
|
6
20
|
// the sys_capability registry; only these group titles are UI strings).
|
|
7
21
|
capability: {
|
|
@@ -22,6 +36,34 @@ const en = {
|
|
|
22
36
|
studio_access: 'Studio Access',
|
|
23
37
|
},
|
|
24
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',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
25
67
|
lookup: {
|
|
26
68
|
recentlyUsed: 'Recently used',
|
|
27
69
|
allResults: 'All results',
|
|
@@ -50,13 +92,12 @@ const en = {
|
|
|
50
92
|
retry: 'Retry',
|
|
51
93
|
},
|
|
52
94
|
common: {
|
|
53
|
-
loading: 'Loading
|
|
95
|
+
loading: 'Loading…',
|
|
54
96
|
save: 'Save',
|
|
55
97
|
cancel: 'Cancel',
|
|
56
98
|
delete: 'Delete',
|
|
57
99
|
edit: 'Edit',
|
|
58
100
|
create: 'Create',
|
|
59
|
-
search: 'Search',
|
|
60
101
|
filter: 'Filter',
|
|
61
102
|
reset: 'Reset',
|
|
62
103
|
confirm: 'Confirm',
|
|
@@ -82,11 +123,22 @@ const en = {
|
|
|
82
123
|
required: 'Required',
|
|
83
124
|
optional: 'Optional',
|
|
84
125
|
selectOption: 'Select an option',
|
|
85
|
-
select: 'Select
|
|
126
|
+
select: 'Select…',
|
|
86
127
|
openChat: 'Open chat',
|
|
87
128
|
closeChat: 'Close chat',
|
|
129
|
+
closePanel: 'Close panel',
|
|
130
|
+
resizeDrawer: 'Resize drawer',
|
|
131
|
+
// Count badge on a tab / section header. Two keys, NOT an i18next
|
|
132
|
+
// `_one`/`_other` pair — see the `reactionCount` note under `detail`.
|
|
133
|
+
itemCount: '{{count}} items',
|
|
134
|
+
itemCountOne: '{{count}} item',
|
|
88
135
|
toggleSidebar: 'Toggle sidebar',
|
|
89
136
|
package: 'Package',
|
|
137
|
+
done: 'Done',
|
|
138
|
+
editInStudio: 'Edit in studio',
|
|
139
|
+
record: 'Record',
|
|
140
|
+
retry: 'Retry',
|
|
141
|
+
printDialogHint: 'Opens your browser’s print dialog (not a PDF export)',
|
|
90
142
|
},
|
|
91
143
|
actions: {
|
|
92
144
|
decisionOutput: {
|
|
@@ -109,6 +161,12 @@ const en = {
|
|
|
109
161
|
url: 'Please enter a valid URL',
|
|
110
162
|
pattern: '{{field}} format is invalid',
|
|
111
163
|
formInvalid: 'Please check the highlighted fields: {{fields}}',
|
|
164
|
+
// Separator between the field names interpolated into `formInvalid`.
|
|
165
|
+
// Per-locale because list punctuation is a locale property, not a code
|
|
166
|
+
// constant: CJK enumerates with U+3001, Latin scripts with a comma+space,
|
|
167
|
+
// Arabic with U+060C. Hardcoding one of them in the renderer put the CJK
|
|
168
|
+
// comma into the English toast (objectstack#5407).
|
|
169
|
+
formInvalidJoiner: ', ',
|
|
112
170
|
unique: '{{field}} must be unique',
|
|
113
171
|
type: '{{field}} must be a valid {{type}}',
|
|
114
172
|
},
|
|
@@ -124,6 +182,12 @@ const en = {
|
|
|
124
182
|
unsavedChanges: 'You have unsaved changes. Are you sure you want to leave?',
|
|
125
183
|
discardTitle: 'Discard changes?',
|
|
126
184
|
discardMessage: 'You have unsaved changes. If you close this form now, your edits will be lost.',
|
|
185
|
+
// The create/edit dialog's `sr-only` accessible description, used when the
|
|
186
|
+
// form declares no `description` of its own (objectui#4024). Not visible
|
|
187
|
+
// copy: it is what assistive tech announces for the dialog. An app could
|
|
188
|
+
// only displace it by authoring a `description`, which makes a VISIBLE
|
|
189
|
+
// subtitle appear on every form — so the fallback has to come from here.
|
|
190
|
+
dialogDescriptionFallback: 'Complete the form fields, then submit or cancel.',
|
|
127
191
|
keepEditing: 'Keep editing',
|
|
128
192
|
discard: 'Discard',
|
|
129
193
|
conflictTitle: 'Save conflict',
|
|
@@ -133,12 +197,21 @@ const en = {
|
|
|
133
197
|
stepOf: 'Step {{current}} of {{total}}',
|
|
134
198
|
createTitle: 'Create {{object}}',
|
|
135
199
|
editTitle: 'Edit {{object}}',
|
|
200
|
+
viewTitle: 'View {{object}}',
|
|
201
|
+
createTargetOrg: 'Creates in {{org}}',
|
|
136
202
|
saveRecord: 'Save',
|
|
137
203
|
create: 'Create',
|
|
138
204
|
update: 'Update',
|
|
139
205
|
createSuccess: '{{object}} created successfully',
|
|
140
206
|
updateSuccess: '{{object}} updated successfully',
|
|
141
207
|
deleteSuccess: '{{object}} deleted successfully',
|
|
208
|
+
fullscreen: {
|
|
209
|
+
title: 'Edit text',
|
|
210
|
+
description: 'Edit the full text value, then save or cancel your changes.',
|
|
211
|
+
done: 'Done',
|
|
212
|
+
toggle: 'Edit {{label}} fullscreen',
|
|
213
|
+
textFallback: 'text',
|
|
214
|
+
},
|
|
142
215
|
},
|
|
143
216
|
fields: {
|
|
144
217
|
relativeDate: {
|
|
@@ -176,7 +249,15 @@ const en = {
|
|
|
176
249
|
richText: {
|
|
177
250
|
format: 'Format: {{format}}',
|
|
178
251
|
basicEditorHint: 'Rich text editor (basic)',
|
|
179
|
-
placeholder: 'Enter text
|
|
252
|
+
placeholder: 'Enter text…',
|
|
253
|
+
},
|
|
254
|
+
// objectui#3231 — the "this option list cannot be filled" copy shared by
|
|
255
|
+
// the fixed-option widgets (select / multiselect / radio / checkboxes) AND
|
|
256
|
+
// by the form renderer's gate hint. One key, one sentence: the renderer
|
|
257
|
+
// interpolates field LABELS, a standalone widget its raw field names.
|
|
258
|
+
options: {
|
|
259
|
+
empty: 'No options available',
|
|
260
|
+
selectFirst: 'Select {{fields}} first',
|
|
180
261
|
},
|
|
181
262
|
objectRef: {
|
|
182
263
|
loading: 'Loading objects…',
|
|
@@ -205,6 +286,32 @@ const en = {
|
|
|
205
286
|
editAsJson: 'Edit as JSON',
|
|
206
287
|
useVisualBuilder: 'Use visual builder',
|
|
207
288
|
},
|
|
289
|
+
// objectui#3342 — the tags widget's input hint, shown while the tag list
|
|
290
|
+
// is empty. The author-declared `field.placeholder` always wins over this.
|
|
291
|
+
tags: {
|
|
292
|
+
placeholder: 'Type and press Enter to add…',
|
|
293
|
+
},
|
|
294
|
+
// objectui#3406 — the accessible sentence of the textarea character
|
|
295
|
+
// counter, rendered only when the field declares `maxLength`. The visible
|
|
296
|
+
// text is `{count}/{max}` digits and needs no locale. ONE interpolated key
|
|
297
|
+
// rather than assembled parts: ja and ko put the cap BEFORE the count
|
|
298
|
+
// ("of {{max}} characters, {{count}}"), an order no code-side
|
|
299
|
+
// concatenation can produce.
|
|
300
|
+
//
|
|
301
|
+
// objectui#3408 moved it off the `aria-live` region and onto the
|
|
302
|
+
// textarea's `aria-describedby` — read once when focus lands, instead of
|
|
303
|
+
// re-announced on every keystroke — and added `charactersRemaining` as the
|
|
304
|
+
// one thing that IS still spoken while typing: after a pause, and only
|
|
305
|
+
// inside the last 10% (or last 20 characters) of the cap.
|
|
306
|
+
//
|
|
307
|
+
// `charactersRemaining` is deliberately colon-form. `count` sends i18next
|
|
308
|
+
// to plural lookup before the base key, and these packs declare the base
|
|
309
|
+
// key only; a sentence whose grammar does not bend on the number is
|
|
310
|
+
// correct at 1 without ten plural entries per pack.
|
|
311
|
+
textarea: {
|
|
312
|
+
characterCount: 'Character count: {{count}} of {{max}}',
|
|
313
|
+
charactersRemaining: 'Characters remaining: {{count}}',
|
|
314
|
+
},
|
|
208
315
|
},
|
|
209
316
|
table: {
|
|
210
317
|
rowsPerPage: 'Rows per page',
|
|
@@ -231,7 +338,7 @@ const en = {
|
|
|
231
338
|
saveAll: 'Save All ({{count}})',
|
|
232
339
|
addRecord: 'Add record',
|
|
233
340
|
open: 'Open',
|
|
234
|
-
search: 'Search
|
|
341
|
+
search: 'Search…',
|
|
235
342
|
modified: '{{count}} row modified',
|
|
236
343
|
saveFailed: 'Save failed',
|
|
237
344
|
selected: '{{count}} selected',
|
|
@@ -244,7 +351,7 @@ const en = {
|
|
|
244
351
|
delete: 'Delete',
|
|
245
352
|
export: 'Export',
|
|
246
353
|
exportAs: 'Export as {{format}}',
|
|
247
|
-
loading: 'Loading grid
|
|
354
|
+
loading: 'Loading grid…',
|
|
248
355
|
errorLoading: 'Error loading grid',
|
|
249
356
|
pullToRefresh: 'Pull to refresh',
|
|
250
357
|
refreshing: 'Refreshing…',
|
|
@@ -261,6 +368,32 @@ const en = {
|
|
|
261
368
|
yes: 'Yes',
|
|
262
369
|
no: 'No',
|
|
263
370
|
systemFields: 'System',
|
|
371
|
+
// Column-footer aggregate prefixes, keyed by the spec's `ColumnSummary`
|
|
372
|
+
// vocabulary (objectui#4024). The footer already formatted its NUMBER
|
|
373
|
+
// through the locale-aware formatter and then put a hardcoded English
|
|
374
|
+
// `Avg: ` / `Sum: ` in front of it.
|
|
375
|
+
//
|
|
376
|
+
// `pattern` is a key rather than a `': '` baked into the renderer: the
|
|
377
|
+
// separator is translatable content — ja/zh set a fullwidth colon, ar runs
|
|
378
|
+
// right-to-left — so a pack owns the whole shape. Same reasoning as
|
|
379
|
+
// `collaboration.resolvedSuffix` below.
|
|
380
|
+
//
|
|
381
|
+
// `countEmpty`/`percentEmpty` (and the filled pair) deliberately share a
|
|
382
|
+
// word: the trailing `%` is what tells the two families apart on screen,
|
|
383
|
+
// exactly as the renderer's own comment says.
|
|
384
|
+
summary: {
|
|
385
|
+
pattern: '{{label}}: {{value}}',
|
|
386
|
+
count: 'Count',
|
|
387
|
+
countEmpty: 'Empty',
|
|
388
|
+
countFilled: 'Filled',
|
|
389
|
+
countUnique: 'Unique',
|
|
390
|
+
percentEmpty: 'Empty',
|
|
391
|
+
percentFilled: 'Filled',
|
|
392
|
+
sum: 'Sum',
|
|
393
|
+
avg: 'Avg',
|
|
394
|
+
min: 'Min',
|
|
395
|
+
max: 'Max',
|
|
396
|
+
},
|
|
264
397
|
toolbar: {
|
|
265
398
|
densityMode: 'Density',
|
|
266
399
|
densityCompact: 'Compact',
|
|
@@ -464,7 +597,8 @@ const en = {
|
|
|
464
597
|
sort: 'Sort',
|
|
465
598
|
sortRecords: 'Sort Records',
|
|
466
599
|
sortByIdSuffix: '(by ID)',
|
|
467
|
-
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,
|
|
600
|
+
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.',
|
|
601
|
+
resetSortToDefault: 'Reset to view default',
|
|
468
602
|
group: 'Group',
|
|
469
603
|
groupBy: 'Group By',
|
|
470
604
|
export: 'Export',
|
|
@@ -489,6 +623,8 @@ const en = {
|
|
|
489
623
|
loadErrorUnauthorizedMessage: 'Your session has expired or you are signed out. Sign in again to view these records.',
|
|
490
624
|
loadErrorRejectedTitle: 'This view’s query was rejected',
|
|
491
625
|
loadErrorRejectedMessage: 'The server could not process this view’s filter or query options. Clearing the filters usually fixes it; if the view is saved this way, an administrator needs to correct it.',
|
|
626
|
+
loadErrorApiDisabledTitle: 'This object isn’t available through the API',
|
|
627
|
+
loadErrorApiDisabledMessage: 'This page can’t load its records because the object is not exposed through the API. That is a setting on the object itself, not a permission — an administrator has to enable API access for it before this page can work.',
|
|
492
628
|
retry: 'Retry',
|
|
493
629
|
managedBy: {
|
|
494
630
|
system: {
|
|
@@ -563,8 +699,11 @@ const en = {
|
|
|
563
699
|
deleteCard: 'Delete card',
|
|
564
700
|
deleteColumn: 'Delete column',
|
|
565
701
|
noCards: 'No cards',
|
|
566
|
-
cardTitlePlaceholder: 'Enter card title
|
|
702
|
+
cardTitlePlaceholder: 'Enter card title…',
|
|
567
703
|
uncategorized: 'Uncategorized',
|
|
704
|
+
columns: 'columns',
|
|
705
|
+
requiredFieldsTitle: 'Complete required fields',
|
|
706
|
+
requiredFieldsDescription: 'This move makes the fields below required. Fill them in to continue.',
|
|
568
707
|
},
|
|
569
708
|
timeline: {
|
|
570
709
|
bucket: {
|
|
@@ -577,6 +716,13 @@ const en = {
|
|
|
577
716
|
noDate: 'No date',
|
|
578
717
|
unassigned: 'Unassigned',
|
|
579
718
|
},
|
|
719
|
+
scale: {
|
|
720
|
+
week: 'Week {{n}}',
|
|
721
|
+
quarter: 'Q{{quarter}} {{year}}',
|
|
722
|
+
},
|
|
723
|
+
gantt: {
|
|
724
|
+
rowLabel: 'Items',
|
|
725
|
+
},
|
|
580
726
|
},
|
|
581
727
|
gantt: {
|
|
582
728
|
column: {
|
|
@@ -603,6 +749,7 @@ const en = {
|
|
|
603
749
|
exportPng: 'Export as PNG',
|
|
604
750
|
exportPdf: 'Export PDF',
|
|
605
751
|
saveLayout: 'Save layout',
|
|
752
|
+
refresh: 'Refresh',
|
|
606
753
|
undo: 'Undo',
|
|
607
754
|
redo: 'Redo',
|
|
608
755
|
},
|
|
@@ -650,6 +797,18 @@ const en = {
|
|
|
650
797
|
ff: 'Finish → Finish',
|
|
651
798
|
sf: 'Start → Finish',
|
|
652
799
|
},
|
|
800
|
+
linkEnd: {
|
|
801
|
+
start: 'start',
|
|
802
|
+
end: 'end',
|
|
803
|
+
},
|
|
804
|
+
link: {
|
|
805
|
+
rejected: {
|
|
806
|
+
self: 'A task cannot depend on itself.',
|
|
807
|
+
locked: 'This row is locked and cannot take a new dependency.',
|
|
808
|
+
group: 'A summary row cannot take a dependency — link one of its tasks instead.',
|
|
809
|
+
cycle: 'That link would create a circular dependency.',
|
|
810
|
+
},
|
|
811
|
+
},
|
|
653
812
|
conflict: {
|
|
654
813
|
title: 'Schedule conflict',
|
|
655
814
|
body: 'This move conflicts with dependency constraints. Auto-reschedule {{count}} affected task(s)?',
|
|
@@ -658,8 +817,8 @@ const en = {
|
|
|
658
817
|
},
|
|
659
818
|
autoScheduleDlg: {
|
|
660
819
|
title: 'Auto-schedule',
|
|
661
|
-
body: 'Shift {count} task(s) later to satisfy dependency links?',
|
|
662
|
-
skipped: '{count} locked task(s) also violate links and were skipped.',
|
|
820
|
+
body: 'Shift {{count}} task(s) later to satisfy dependency links?',
|
|
821
|
+
skipped: '{{count}} locked task(s) also violate links and were skipped.',
|
|
663
822
|
confirm: 'Apply',
|
|
664
823
|
cancel: 'Cancel',
|
|
665
824
|
none: 'All dependencies satisfied — nothing to reschedule.',
|
|
@@ -670,6 +829,18 @@ const en = {
|
|
|
670
829
|
over: 'overloaded',
|
|
671
830
|
empty: 'No tasks to allocate.',
|
|
672
831
|
},
|
|
832
|
+
quickFilter: {
|
|
833
|
+
all: 'All',
|
|
834
|
+
clear: 'Clear filters',
|
|
835
|
+
empty: 'No options',
|
|
836
|
+
// SINGLE braces on purpose: the ObjectGantt call site resolves these
|
|
837
|
+
// with a literal `.replace('{shown}', …)`, not i18next interpolation.
|
|
838
|
+
// The last key in the gantt namespace on that idiom — `conflict.body`
|
|
839
|
+
// and the two `autoScheduleDlg` counts moved to `{{count}}` + i18next
|
|
840
|
+
// interpolation in objectui#4157, where the single-brace call site met
|
|
841
|
+
// a `{{count}}` pack and rendered a literal `{2}`.
|
|
842
|
+
resultSummary: 'Showing {shown} / {total} tasks',
|
|
843
|
+
},
|
|
673
844
|
readOnly: 'Read-only',
|
|
674
845
|
readOnlyHint: 'Editing is disabled for this view.',
|
|
675
846
|
lockedHint: 'No edit permission',
|
|
@@ -716,14 +887,15 @@ const en = {
|
|
|
716
887
|
editInlineHint: 'Double-click to edit',
|
|
717
888
|
lockedByApproval: 'Locked for approval',
|
|
718
889
|
lockedTooltip: 'This record has a pending approval request; editing is locked',
|
|
719
|
-
writeStrippedTitle: '
|
|
720
|
-
writeStrippedReadonly: 'Read-only, so it
|
|
721
|
-
writeStrippedByState: "Not editable in this record's current state, so it
|
|
890
|
+
writeStrippedTitle: 'Saved — but some fields did not take effect',
|
|
891
|
+
writeStrippedReadonly: 'Read-only, so it did not take effect: {{fields}}',
|
|
892
|
+
writeStrippedByState: "Not editable in this record's current state, so it did not take effect: {{fields}}",
|
|
722
893
|
approvalPendingEditable: 'In approval · editable',
|
|
723
894
|
approvalPendingTooltip: 'This record has a pending approval request; this step still allows editing',
|
|
724
895
|
approvalProgress: 'Approvals — {{got}} of {{need}}',
|
|
725
896
|
approvalProgressGroups: 'Sign-off — {{got}} of {{need}} groups',
|
|
726
897
|
approvalProgressLabel: 'Approval progress',
|
|
898
|
+
approvalsPanelTitle: 'Approvals',
|
|
727
899
|
cancelApproval: 'Recall approval',
|
|
728
900
|
cancelApprovalInFlight: 'Recalling…',
|
|
729
901
|
cancelApprovalTooltip: 'Recall the pending approval request to unlock this record',
|
|
@@ -745,6 +917,7 @@ const en = {
|
|
|
745
917
|
concurrentUpdateReload: 'Reload latest',
|
|
746
918
|
concurrentUpdateOverwrite: 'Overwrite anyway',
|
|
747
919
|
concurrentUpdateCancel: 'Cancel',
|
|
920
|
+
concurrentUpdateRecordLabel: 'this record',
|
|
748
921
|
openInNewTab: 'Open in new tab',
|
|
749
922
|
share: 'Share',
|
|
750
923
|
duplicate: 'Duplicate',
|
|
@@ -752,6 +925,28 @@ const en = {
|
|
|
752
925
|
viewHistory: 'View history',
|
|
753
926
|
delete: 'Delete',
|
|
754
927
|
moreActions: 'More actions',
|
|
928
|
+
addReaction: 'Add reaction',
|
|
929
|
+
pageHeaderActions: 'Page header actions',
|
|
930
|
+
emojiPicker: 'Emoji picker',
|
|
931
|
+
reactionCount: '{{emoji}} {{count}} reactions',
|
|
932
|
+
reactionCountOne: '{{emoji}} {{count}} reaction',
|
|
933
|
+
// Record-overlay chrome (objectstack#5506). `recordDetail` is the VISIBLE
|
|
934
|
+
// heading the overlay falls back to when the host passes no title;
|
|
935
|
+
// `openAsFullPage` is the icon-only expand button's default accessible
|
|
936
|
+
// name; `recordDetailOverlay` is the sr-only description on the
|
|
937
|
+
// Sheet/Dialog when the host supplies none.
|
|
938
|
+
recordDetail: 'Record Detail',
|
|
939
|
+
// Same heading, but for the hosts that DO know which object they are
|
|
940
|
+
// showing (objectui#3426): `ListView` / `ObjectGrid` used to string-build
|
|
941
|
+
// `` `${label} Detail` `` in TypeScript and hand it to the overlay's
|
|
942
|
+
// `title` prop, so `recordDetail` above never got a chance to apply and a
|
|
943
|
+
// non-English session read one English heading. Interpolating keeps the
|
|
944
|
+
// object label in the heading without freezing English word order — a pack
|
|
945
|
+
// whose qualifier trails the noun (de: a hyphenated compound) or that needs
|
|
946
|
+
// a possessive particle (ja/zh) writes its own arrangement here.
|
|
947
|
+
recordDetailWithLabel: '{{label}} Detail',
|
|
948
|
+
openAsFullPage: 'Open as full page',
|
|
949
|
+
recordDetailOverlay: 'Record detail overlay for {{title}}.',
|
|
755
950
|
addToFavorites: 'Add to favorites',
|
|
756
951
|
removeFromFavorites: 'Remove from favorites',
|
|
757
952
|
previousRecord: 'Previous record',
|
|
@@ -765,16 +960,19 @@ const en = {
|
|
|
765
960
|
relatedRecords: '{{count}} records',
|
|
766
961
|
relatedRecordOne: '{{count}} record',
|
|
767
962
|
noRelatedRecords: 'No related records found',
|
|
768
|
-
loading: 'Loading
|
|
963
|
+
loading: 'Loading…',
|
|
769
964
|
copyToClipboard: 'Copy to clipboard',
|
|
770
965
|
copied: 'Copied!',
|
|
771
966
|
deleteConfirmation: 'Are you sure you want to delete this record?',
|
|
967
|
+
deleted: 'Record deleted',
|
|
772
968
|
editRecord: 'Edit record',
|
|
773
969
|
viewAll: 'View All',
|
|
774
970
|
new: 'New',
|
|
971
|
+
add: 'Add',
|
|
775
972
|
emptyValue: '—',
|
|
776
973
|
activity: 'Activity',
|
|
777
974
|
history: 'History',
|
|
975
|
+
historyEmpty: 'No history yet',
|
|
778
976
|
editRow: 'Edit',
|
|
779
977
|
deleteRow: 'Delete',
|
|
780
978
|
deleteRowConfirmation: 'Are you sure you want to delete this record?',
|
|
@@ -784,7 +982,7 @@ const en = {
|
|
|
784
982
|
nextPage: 'Next',
|
|
785
983
|
pageOf: 'Page {{current}} of {{total}}',
|
|
786
984
|
sortBy: 'Sort by',
|
|
787
|
-
filterPlaceholder: 'Filter
|
|
985
|
+
filterPlaceholder: 'Filter…',
|
|
788
986
|
highlightFields: 'Key Fields',
|
|
789
987
|
// Comments
|
|
790
988
|
comments: 'Comments',
|
|
@@ -855,6 +1053,13 @@ const en = {
|
|
|
855
1053
|
nextRecordKey: 'Next record (→)',
|
|
856
1054
|
lastRecord: 'Last record (End)',
|
|
857
1055
|
noRecords: 'No records',
|
|
1056
|
+
// objectui#3863 — the BASE key is the slot every plural category a pack did not
|
|
1057
|
+
// enumerate resolves to, keeping that pack in its own language instead of falling
|
|
1058
|
+
// through `fallbackLng` to English. `en` itself can never reach it (its categories
|
|
1059
|
+
// are exactly `one`/`other`), so this value is parity ballast — and it must stay
|
|
1060
|
+
// byte-identical to the call site's inline `defaultValue` in
|
|
1061
|
+
// `record-reference-rail.tsx`, which `check:i18n-keys` now judges as class 3.
|
|
1062
|
+
showEmptyRelated: '+ {{count}} empty',
|
|
858
1063
|
showEmptyRelated_one: '+ {{count}} empty',
|
|
859
1064
|
showEmptyRelated_other: '+ {{count}} empty',
|
|
860
1065
|
searchWhileNavigating: 'Search while navigating',
|
|
@@ -869,6 +1074,7 @@ const en = {
|
|
|
869
1074
|
loadMore: 'Load more',
|
|
870
1075
|
edited: '(edited)',
|
|
871
1076
|
via: 'via {{source}}',
|
|
1077
|
+
viewSource: 'View source',
|
|
872
1078
|
// Replies
|
|
873
1079
|
replyCount: '{{count}} reply',
|
|
874
1080
|
replyCountPlural: '{{count}} replies',
|
|
@@ -889,7 +1095,8 @@ const en = {
|
|
|
889
1095
|
},
|
|
890
1096
|
chart: {
|
|
891
1097
|
noData: 'No chart data available',
|
|
892
|
-
loading: 'Loading chart
|
|
1098
|
+
loading: 'Loading chart…',
|
|
1099
|
+
nullCategory: '(None)',
|
|
893
1100
|
},
|
|
894
1101
|
report: {
|
|
895
1102
|
total: 'Total',
|
|
@@ -914,111 +1121,21 @@ const en = {
|
|
|
914
1121
|
first: 'First',
|
|
915
1122
|
},
|
|
916
1123
|
editor: {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
objectNameHelp: 'Which object should this report query?',
|
|
933
|
-
limit: 'Row limit',
|
|
934
|
-
limitPlaceholder: 'e.g. 100',
|
|
935
|
-
columns: 'Columns',
|
|
936
|
-
columnsHint: 'Pick which fields appear as columns. Add an aggregate (Sum, Count…) on Summary or Matrix reports.',
|
|
937
|
-
filters: 'Filters',
|
|
938
|
-
filtersHint: 'Limit the rows that go into this report.',
|
|
939
|
-
filtersComplex: 'This report uses an advanced filter that cannot be edited here. Save will keep it unchanged.',
|
|
940
|
-
groupBy: 'Group by',
|
|
941
|
-
groupByHint: 'Group the data and compute subtotals.',
|
|
942
|
-
rows: 'Rows',
|
|
943
|
-
rowsHint: 'Group rows by these fields.',
|
|
944
|
-
columnsAxis: 'Columns',
|
|
945
|
-
columnsAxisHint: 'Pivot these fields across the top.',
|
|
946
|
-
values: 'Values',
|
|
947
|
-
valuesHint: 'Numbers shown in each cell. Pick one or more fields and add an aggregate (Sum, Count…).',
|
|
948
|
-
grouping: 'Grouping',
|
|
949
|
-
addGrouping: 'Add grouping',
|
|
950
|
-
dateGranularity: 'Date bucket',
|
|
951
|
-
dateGranularityNone: '(use raw value)',
|
|
952
|
-
day: 'Day',
|
|
953
|
-
week: 'Week',
|
|
954
|
-
month: 'Month',
|
|
955
|
-
quarter: 'Quarter',
|
|
956
|
-
year: 'Year',
|
|
957
|
-
sortAsc: 'Ascending',
|
|
958
|
-
sortDesc: 'Descending',
|
|
959
|
-
chart: 'Chart',
|
|
960
|
-
chartHint: 'Optional visualization rendered alongside the table.',
|
|
961
|
-
chartType: 'Chart type',
|
|
962
|
-
chartTitle: 'Chart title',
|
|
963
|
-
chartTitlePlaceholder: 'Defaults to the report title',
|
|
964
|
-
chartXAxis: 'X-axis (category)',
|
|
965
|
-
chartYAxis: 'Y-axis (value)',
|
|
966
|
-
chartShowLegend: 'Show legend',
|
|
967
|
-
chartShowDataLabels: 'Show data labels',
|
|
968
|
-
chartNone: '(no chart)',
|
|
969
|
-
chartBar: 'Bar',
|
|
970
|
-
chartLine: 'Line',
|
|
971
|
-
chartArea: 'Area',
|
|
972
|
-
chartPie: 'Pie',
|
|
973
|
-
chartDonut: 'Donut',
|
|
974
|
-
chartFunnel: 'Funnel',
|
|
975
|
-
validationNeedsObject: 'Pick a data source before adding columns.',
|
|
976
|
-
validationMatrixNeedsRowsCols: 'Matrix reports need at least one Row and one Column.',
|
|
977
|
-
validationSummaryNeedsRows: 'Summary reports need at least one grouping field.',
|
|
978
|
-
blocks: 'Blocks',
|
|
979
|
-
blocksHint: 'Each block is its own table or chart. Block-level filters are ANDed with the report filter.',
|
|
980
|
-
addBlock: 'Add block',
|
|
981
|
-
removeBlock: 'Remove block',
|
|
982
|
-
blockName: 'Block name',
|
|
983
|
-
blockNamePlaceholder: 'unique_block_name',
|
|
984
|
-
blockLabel: 'Display label',
|
|
985
|
-
blockLabelPlaceholder: 'Shown above the block',
|
|
986
|
-
blockDescription: 'Description',
|
|
987
|
-
blockDescriptionPlaceholder: 'Optional context for this block',
|
|
988
|
-
validationJoinedNeedsBlocks: 'Joined reports need at least one block.',
|
|
989
|
-
validationBlockNameRequired: 'Every block needs a non-empty name.',
|
|
990
|
-
validationBlockNameDuplicate: 'Block names must be unique within the report.',
|
|
991
|
-
validationBlockNeedsColumns: 'Every block needs at least one column.',
|
|
992
|
-
noneOption: '(none)',
|
|
993
|
-
addCondition: 'Add condition',
|
|
994
|
-
combineLogic: 'Combine with',
|
|
995
|
-
opContains: 'contains',
|
|
996
|
-
opIsEmpty: 'is empty',
|
|
997
|
-
opIsNotEmpty: 'is not empty',
|
|
998
|
-
formatAuto: 'Auto',
|
|
999
|
-
formatCurrency: 'Currency',
|
|
1000
|
-
formatPercent: 'Percent',
|
|
1001
|
-
formatInteger: 'Integer',
|
|
1002
|
-
formatDate: 'Date',
|
|
1003
|
-
formatDatetime: 'Date & time',
|
|
1004
|
-
columnLabelPlaceholder: 'Label override',
|
|
1005
|
-
aggregateColumn: 'Aggregate',
|
|
1006
|
-
formatColumn: 'Format',
|
|
1007
|
-
addColumns: 'Add fields',
|
|
1008
|
-
searchFields: 'Search fields…',
|
|
1009
|
-
noMatchingFields: 'No fields match your search.',
|
|
1010
|
-
noFieldsAvailable: 'No fields available.',
|
|
1011
|
-
columnsCount: '{n} column(s) selected',
|
|
1012
|
-
columnsEmpty: 'No columns selected yet.',
|
|
1013
|
-
fieldPickerTitle: 'Select fields',
|
|
1014
|
-
fieldPickerDescription: 'Pick one or more fields to add. Use the search box to narrow the list.',
|
|
1015
|
-
fieldPickerChangeTitle: 'Change field',
|
|
1016
|
-
fieldPickerAddGroupingTitle: 'Add grouping',
|
|
1017
|
-
fieldPickerEmpty: '(pick field)',
|
|
1018
|
-
fieldPickerSelected: '{n} selected',
|
|
1019
|
-
fieldPickerClear: 'Clear selection',
|
|
1020
|
-
fieldPickerAdd: 'Add',
|
|
1021
|
-
fieldPickerAddN: 'Add {n}',
|
|
1124
|
+
// ONE key by design, not by accident (objectui#4145). This namespace held
|
|
1125
|
+
// 106 keys labelling the hand-rolled report editor form; that form is gone
|
|
1126
|
+
// — `ReportConfigPanel`'s body is `ReportDefaultInspector`, a spec-driven
|
|
1127
|
+
// inspector whose labels come from the report spec's own metadata. The
|
|
1128
|
+
// other 105 keys had no reader in any package, in any pack, in any dynamic
|
|
1129
|
+
// `t()` form, so they were retired here rather than left for the next
|
|
1130
|
+
// author to infer a live UI from.
|
|
1131
|
+
//
|
|
1132
|
+
// `panelTitle` names the PANEL (its heading and its `role="complementary"`
|
|
1133
|
+
// landmark). It is minted by objectui#4137 and is the namespace's only
|
|
1134
|
+
// consumer. Do not grow this namespace back: the spec-driven inspector is
|
|
1135
|
+
// the settled direction, and
|
|
1136
|
+
// `packages/i18n/src/__tests__/report-editor-retired-4145.test.ts` fails on
|
|
1137
|
+
// any retired key that returns.
|
|
1138
|
+
panelTitle: 'Edit report',
|
|
1022
1139
|
},
|
|
1023
1140
|
},
|
|
1024
1141
|
map: {
|
|
@@ -1041,8 +1158,8 @@ const en = {
|
|
|
1041
1158
|
properties: 'Properties',
|
|
1042
1159
|
connections: 'Connections',
|
|
1043
1160
|
quickConnect: 'Quick Connect',
|
|
1044
|
-
fromPlaceholder: 'From
|
|
1045
|
-
toPlaceholder: 'To
|
|
1161
|
+
fromPlaceholder: 'From…',
|
|
1162
|
+
toPlaceholder: 'To…',
|
|
1046
1163
|
connect: 'Connect',
|
|
1047
1164
|
label: 'Label',
|
|
1048
1165
|
type: 'Type',
|
|
@@ -1102,6 +1219,7 @@ const en = {
|
|
|
1102
1219
|
noDataAvailable: 'No data available',
|
|
1103
1220
|
noDataSourceFor: 'No data source available for',
|
|
1104
1221
|
noRows: 'No rows',
|
|
1222
|
+
loading: 'Loading…',
|
|
1105
1223
|
pickMeasures: 'Pick measures (values) for this dataset widget.',
|
|
1106
1224
|
datasetUnsupported: 'This data source does not support dataset queries.',
|
|
1107
1225
|
details: 'Details',
|
|
@@ -1351,8 +1469,9 @@ const en = {
|
|
|
1351
1469
|
},
|
|
1352
1470
|
},
|
|
1353
1471
|
console: {
|
|
1472
|
+
saveAdvisoryTitle: 'Saved — the authoring check raised {{count}} advisory finding(s)',
|
|
1354
1473
|
title: 'ObjectOS',
|
|
1355
|
-
initializing: 'Initializing application
|
|
1474
|
+
initializing: 'Initializing application…',
|
|
1356
1475
|
search: 'Search…',
|
|
1357
1476
|
breadcrumb: {
|
|
1358
1477
|
dashboards: 'Dashboards',
|
|
@@ -1384,6 +1503,58 @@ const en = {
|
|
|
1384
1503
|
Other: 'Other',
|
|
1385
1504
|
},
|
|
1386
1505
|
},
|
|
1506
|
+
// The single-namespace settings screen (objectui#4024). Placed beside
|
|
1507
|
+
// `settingsHub` deliberately: `SettingsView.tsx` and `SettingsHub.tsx` are
|
|
1508
|
+
// the same feature in the same directory, and the view was the one file
|
|
1509
|
+
// written against a different convention — every string below was a
|
|
1510
|
+
// hardcoded English literal while its sibling resolved through the bundle.
|
|
1511
|
+
//
|
|
1512
|
+
// `useSettingsLabel` already translates the manifest-authored CONTENT
|
|
1513
|
+
// (title, description, field labels), so a plugin author could translate
|
|
1514
|
+
// what is INSIDE a settings namespace but had no key reaching the chrome
|
|
1515
|
+
// around it — a zh-CN admin read translated field labels inside an English
|
|
1516
|
+
// save bar.
|
|
1517
|
+
settingsView: {
|
|
1518
|
+
backToHub: 'All settings',
|
|
1519
|
+
back: 'Back',
|
|
1520
|
+
noNamespace: 'No namespace selected.',
|
|
1521
|
+
loadError: 'Failed to load settings',
|
|
1522
|
+
saved: 'Settings saved',
|
|
1523
|
+
saveFailed: 'Save failed',
|
|
1524
|
+
// Parameterized, never concatenated: `{{key}}` is spliced into the
|
|
1525
|
+
// sentence so a pack can put the key where its own grammar wants it.
|
|
1526
|
+
lockedByEnv: 'Locked by environment: {{key}}',
|
|
1527
|
+
// The server named no key — the same refusal without a subject.
|
|
1528
|
+
lockedByEnvNoKey: 'Locked by environment',
|
|
1529
|
+
actionSucceeded: 'Action succeeded',
|
|
1530
|
+
actionFailed: 'Action failed',
|
|
1531
|
+
discard: 'Discard',
|
|
1532
|
+
saveChanges: 'Save changes',
|
|
1533
|
+
// Save-bar counter. A REAL i18next plural family, not an English-only
|
|
1534
|
+
// `change(s)` and not the two-sibling-key `xxxCountOne` shape used
|
|
1535
|
+
// elsewhere in this file.
|
|
1536
|
+
//
|
|
1537
|
+
// The BASE key is load-bearing (objectui#3863): i18next asks
|
|
1538
|
+
// `Intl.PluralRules` for the one suffix a language needs and, finding no
|
|
1539
|
+
// such slot, walks `fallbackLng` to `en`. `ru` has four categories and
|
|
1540
|
+
// `ar` six; no pack here enumerates `_few`/`_many`/`_two`/`_zero`, so
|
|
1541
|
+
// without this base key `ru` would render ENGLISH at counts 2-20.
|
|
1542
|
+
// `all-locales-key-parity.test.ts` owns that rule.
|
|
1543
|
+
unsavedCount: '{{count}} unsaved changes',
|
|
1544
|
+
unsavedCount_one: '{{count}} unsaved change',
|
|
1545
|
+
unsavedCount_other: '{{count}} unsaved changes',
|
|
1546
|
+
// The fail-closed crypto refusal (objectstack#8396). objectui#4579 added
|
|
1547
|
+
// these as English literals on purpose — routing one string through i18n
|
|
1548
|
+
// would have left a single translated string among a dozen hardcoded
|
|
1549
|
+
// ones — and deferred them to this card, which converts the screen whole.
|
|
1550
|
+
cryptoRefusalTitle: 'This deployment cannot encrypt secrets',
|
|
1551
|
+
// `{{subject}}` is rendered as a `< code >` element by the view, so the
|
|
1552
|
+
// sentence is split around it rather than interpolated.
|
|
1553
|
+
cryptoRefusalSubjectSuffix: 'is declared encrypted, so nothing was written.',
|
|
1554
|
+
cryptoRefusalNoSubject: 'The declared-encrypted value was refused, so nothing was written.',
|
|
1555
|
+
cryptoRefusalToast: 'Cannot encrypt secrets: {{subject}}',
|
|
1556
|
+
cryptoRefusalToastNoSubject: 'Cannot encrypt secrets',
|
|
1557
|
+
},
|
|
1387
1558
|
loadingSteps: {
|
|
1388
1559
|
connecting: 'Connecting to data source',
|
|
1389
1560
|
loadingConfig: 'Loading configuration',
|
|
@@ -1400,6 +1571,15 @@ const en = {
|
|
|
1400
1571
|
retry: 'Retry',
|
|
1401
1572
|
retrying: 'Retrying…',
|
|
1402
1573
|
},
|
|
1574
|
+
// Copy owned by the console server-action wrapper (`consoleServerAction.ts`,
|
|
1575
|
+
// objectui#3321): the pre-opened SSO spinner tab and the popup-blocked toast.
|
|
1576
|
+
serverAction: {
|
|
1577
|
+
openingTitle: 'Opening…',
|
|
1578
|
+
openingBody: 'Opening… this may take a moment.',
|
|
1579
|
+
popupBlockedTitle: 'Popup blocked',
|
|
1580
|
+
popupBlockedDescription: 'Your browser blocked the new tab from opening.',
|
|
1581
|
+
popupBlockedAction: 'Open in new tab',
|
|
1582
|
+
},
|
|
1403
1583
|
shortcuts: {
|
|
1404
1584
|
title: 'Keyboard Shortcuts',
|
|
1405
1585
|
description: 'Quick reference for all available keyboard shortcuts.',
|
|
@@ -1407,6 +1587,7 @@ const en = {
|
|
|
1407
1587
|
general: 'General',
|
|
1408
1588
|
navigation: 'Navigation',
|
|
1409
1589
|
dataViews: 'Data Views',
|
|
1590
|
+
aiChat: 'AI assistant',
|
|
1410
1591
|
preferences: 'Preferences',
|
|
1411
1592
|
},
|
|
1412
1593
|
openCommandPalette: 'Open command palette',
|
|
@@ -1417,11 +1598,13 @@ const en = {
|
|
|
1417
1598
|
createRecord: 'Create new record',
|
|
1418
1599
|
refreshData: 'Refresh data',
|
|
1419
1600
|
editRecord: 'Edit selected record',
|
|
1601
|
+
newChat: 'New chat',
|
|
1602
|
+
toggleChatsList: 'Toggle conversations list',
|
|
1420
1603
|
toggleDarkMode: 'Toggle dark mode',
|
|
1421
1604
|
},
|
|
1422
1605
|
commandPalette: {
|
|
1423
1606
|
title: 'Command palette',
|
|
1424
|
-
placeholder: 'Type a command or search
|
|
1607
|
+
placeholder: 'Type a command or search…',
|
|
1425
1608
|
noResults: 'No results found.',
|
|
1426
1609
|
searching: 'Searching…',
|
|
1427
1610
|
records: 'Records',
|
|
@@ -1463,7 +1646,7 @@ const en = {
|
|
|
1463
1646
|
shareTitle: 'Share this conversation',
|
|
1464
1647
|
shareDisabledTitle: 'Start chatting to enable sharing',
|
|
1465
1648
|
newChat: 'New',
|
|
1466
|
-
searchChats: 'Search chats
|
|
1649
|
+
searchChats: 'Search chats…',
|
|
1467
1650
|
noChatsYet: 'No chats yet',
|
|
1468
1651
|
noChatsDescription: 'Start a new conversation to see it here.',
|
|
1469
1652
|
noMatchingChats: 'No matching chats.',
|
|
@@ -1472,18 +1655,18 @@ const en = {
|
|
|
1472
1655
|
deleteConversation: 'Delete conversation',
|
|
1473
1656
|
saveRename: 'Save rename',
|
|
1474
1657
|
cancelRename: 'Cancel rename',
|
|
1475
|
-
loadingHistory: 'Loading conversation history
|
|
1658
|
+
loadingHistory: 'Loading conversation history…',
|
|
1476
1659
|
conversationReady: 'Conversation ready',
|
|
1477
1660
|
preparingConversation: 'Preparing a new conversation',
|
|
1478
1661
|
offlineDemoMode: 'Offline demo mode — agent list unavailable',
|
|
1479
1662
|
sendFailedRateLimited: "You're sending messages too quickly. Your message is kept below — wait a moment and try again.",
|
|
1480
1663
|
sendFailedGeneric: "Couldn't send your message. It's kept below — please try again.",
|
|
1481
|
-
askAgent: 'Ask {{agent}}
|
|
1664
|
+
askAgent: 'Ask {{agent}}…',
|
|
1482
1665
|
assistant: 'Assistant',
|
|
1483
1666
|
liveCanvas: 'Live preview — {{app}} (draft)',
|
|
1484
1667
|
liveCanvasUnlisted: 'Live app — {{app}} (unlisted until published)',
|
|
1485
|
-
loadingAgents: 'Loading agents
|
|
1486
|
-
askAnything: 'Ask anything
|
|
1668
|
+
loadingAgents: 'Loading agents…',
|
|
1669
|
+
askAnything: 'Ask anything…',
|
|
1487
1670
|
emptyTitle: 'Start a conversation',
|
|
1488
1671
|
emptyDescription: 'Ask anything — the assistant has access to your current app context.',
|
|
1489
1672
|
switchAssistant: 'Switch assistant',
|
|
@@ -1598,6 +1781,57 @@ const en = {
|
|
|
1598
1781
|
addAutomation: 'Add an automation — an approval, a status flow, or a notification.',
|
|
1599
1782
|
},
|
|
1600
1783
|
},
|
|
1784
|
+
// objectui#3546 slice four — the AI console surfaces: the /ai chat page's app switcher,
|
|
1785
|
+
// connection banner and plan/publish cards, the ChatDock chrome, and the
|
|
1786
|
+
// conversation-list date buckets. `group.*` is the template-key family
|
|
1787
|
+
// `console.ai.group.${key}` (ConversationsSidebar), enumerated from the
|
|
1788
|
+
// ConversationGroupKey union — all five members, no wildcard.
|
|
1789
|
+
collapseToDock: 'Collapse to side panel',
|
|
1790
|
+
switchApp: 'Switch app',
|
|
1791
|
+
switchAppLabel: 'Build conversations by app',
|
|
1792
|
+
newApp: 'New app',
|
|
1793
|
+
connectionWaiting: 'Waiting for server…',
|
|
1794
|
+
connectionStalled: 'Still working…',
|
|
1795
|
+
connectionOffline: 'Connection lost — reconnecting…',
|
|
1796
|
+
designingPlan: 'Designing your app…',
|
|
1797
|
+
designingPlanHint: {
|
|
1798
|
+
data: 'Mapping out the data you’ll track…',
|
|
1799
|
+
objects: 'Shaping objects and their fields…',
|
|
1800
|
+
relations: 'Connecting related records…',
|
|
1801
|
+
lookups: 'Setting up relationships and lookups…',
|
|
1802
|
+
views: 'Planning the screens and views…',
|
|
1803
|
+
forms: 'Laying out forms and lists…',
|
|
1804
|
+
defaults: 'Adding sensible defaults and validations…',
|
|
1805
|
+
dashboard: 'Sketching a dashboard to track it…',
|
|
1806
|
+
review: 'Double-checking the structure hangs together…',
|
|
1807
|
+
finalize: 'Pulling the plan together…',
|
|
1808
|
+
},
|
|
1809
|
+
planBuilding: 'Building…',
|
|
1810
|
+
planBuilt: 'Built',
|
|
1811
|
+
planDeferred: 'Not yet built',
|
|
1812
|
+
planReady: 'The plan is ready. Build it now, or tell me what to adjust.',
|
|
1813
|
+
published: 'Published',
|
|
1814
|
+
publishFailed: 'Publish failed',
|
|
1815
|
+
unavailableTitle: 'AI assistant unavailable',
|
|
1816
|
+
unavailableDescription: "This deployment doesn't have an AI assistant enabled. Everything else works as usual.",
|
|
1817
|
+
unavailableError: "Couldn't reach the AI service. It may be temporarily offline — try again, or head back home.",
|
|
1818
|
+
unavailableRetry: 'Try again',
|
|
1819
|
+
unavailableHome: 'Back to home',
|
|
1820
|
+
dock: {
|
|
1821
|
+
title: 'Assistant',
|
|
1822
|
+
description: 'AI assistant chat',
|
|
1823
|
+
resize: 'Resize chat',
|
|
1824
|
+
collapse: 'Collapse chat',
|
|
1825
|
+
maximize: 'Open full page',
|
|
1826
|
+
open: 'Open assistant',
|
|
1827
|
+
},
|
|
1828
|
+
group: {
|
|
1829
|
+
today: 'Today',
|
|
1830
|
+
yesterday: 'Yesterday',
|
|
1831
|
+
previous7Days: 'Previous 7 days',
|
|
1832
|
+
previous30Days: 'Previous 30 days',
|
|
1833
|
+
older: 'Older',
|
|
1834
|
+
},
|
|
1601
1835
|
},
|
|
1602
1836
|
errors: {
|
|
1603
1837
|
somethingWentWrong: 'Something went wrong',
|
|
@@ -1606,6 +1840,12 @@ const en = {
|
|
|
1606
1840
|
goHome: 'Go Home',
|
|
1607
1841
|
errorDetails: 'Error Details (dev only)',
|
|
1608
1842
|
},
|
|
1843
|
+
// objectui#3546 slice four — the console's catch-all route (AppContent `RouteNotFound`).
|
|
1844
|
+
notFound: {
|
|
1845
|
+
title: 'Page not found',
|
|
1846
|
+
description: 'The URL you followed does not match any view in this app.',
|
|
1847
|
+
back: 'Go back',
|
|
1848
|
+
},
|
|
1609
1849
|
theme: {
|
|
1610
1850
|
toggle: 'Toggle theme',
|
|
1611
1851
|
light: 'Light',
|
|
@@ -1643,7 +1883,14 @@ const en = {
|
|
|
1643
1883
|
objectNotFound: 'Object Not Found',
|
|
1644
1884
|
objectNotFoundDescription: 'The object "{{objectName}}" does not exist in the current configuration.',
|
|
1645
1885
|
objectNotFoundHint: 'Check your app navigation settings or select a different object from the sidebar.',
|
|
1886
|
+
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.',
|
|
1887
|
+
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.',
|
|
1646
1888
|
systemViewReadonly: 'System view defined in code — read-only.',
|
|
1889
|
+
// ObjectView.tsx references ONE edit-denied key from four call sites
|
|
1890
|
+
// (rename / pin / set-as-default / configure), so the copy has to cover
|
|
1891
|
+
// any change to a built-in view rather than name one operation.
|
|
1892
|
+
cannotEditMetaView: 'This view is built in and defined in code — it cannot be changed.',
|
|
1893
|
+
cannotDeleteMetaView: 'This view is built in and defined in code — it cannot be deleted.',
|
|
1647
1894
|
expandToPage: 'Open as full page',
|
|
1648
1895
|
allRecords: 'All Records',
|
|
1649
1896
|
exitDesignMode: 'Exit Design Mode',
|
|
@@ -1677,7 +1924,7 @@ const en = {
|
|
|
1677
1924
|
exportPrintHint: 'Export formats, printing, and record count display',
|
|
1678
1925
|
showAllFields: 'Show All',
|
|
1679
1926
|
hideAllFields: 'Hide All',
|
|
1680
|
-
searchFields: 'Search fields
|
|
1927
|
+
searchFields: 'Search fields…',
|
|
1681
1928
|
title: 'Title',
|
|
1682
1929
|
description: 'Description',
|
|
1683
1930
|
data: 'Data',
|
|
@@ -1756,8 +2003,8 @@ const en = {
|
|
|
1756
2003
|
imageField: 'Image field',
|
|
1757
2004
|
imageFieldHelp: 'The gallery cover comes from this image or attachment field.',
|
|
1758
2005
|
dateField: 'Date field',
|
|
1759
|
-
selectField: 'Select field
|
|
1760
|
-
selectOption: 'Select
|
|
2006
|
+
selectField: 'Select field…',
|
|
2007
|
+
selectOption: 'Select…',
|
|
1761
2008
|
noEligibleFieldForType: 'No eligible field of this type. Add one to the object first.',
|
|
1762
2009
|
viewTypeUnavailable: 'This object has no field that can serve as "{{field}}".',
|
|
1763
2010
|
viewTypeUnavailableShort: 'Not supported by this object.',
|
|
@@ -1831,7 +2078,7 @@ const en = {
|
|
|
1831
2078
|
ufDropdown: 'Dropdown',
|
|
1832
2079
|
ufTabs: 'Tabs',
|
|
1833
2080
|
ufToggle: 'Toggle',
|
|
1834
|
-
ufAddField: '+ Add filter field
|
|
2081
|
+
ufAddField: '+ Add filter field…',
|
|
1835
2082
|
ufNoFields: 'No fields available',
|
|
1836
2083
|
ufAddTab: '+ Add tab',
|
|
1837
2084
|
ufTabLabel: 'Label',
|
|
@@ -1863,11 +2110,25 @@ const en = {
|
|
|
1863
2110
|
passwordPlaceholder: 'Enter your password',
|
|
1864
2111
|
forgotPasswordText: 'Forgot password?',
|
|
1865
2112
|
submitButton: 'Sign In',
|
|
1866
|
-
submittingButton: 'Signing in
|
|
2113
|
+
submittingButton: 'Signing in…',
|
|
1867
2114
|
noAccountText: "Don't have an account?",
|
|
1868
2115
|
signUpText: 'Sign up',
|
|
1869
2116
|
signingIn: 'Signing you in…',
|
|
1870
2117
|
ssoHandoff: 'Continue to {{target}}',
|
|
2118
|
+
// Phone/OTP sign-in labels. `LoginForm` interpolates `{seconds}` with a
|
|
2119
|
+
// literal `.replace()` of its own (packages/auth/src/LoginForm.tsx:429),
|
|
2120
|
+
// so those SINGLE braces must survive translation — i18next never sees
|
|
2121
|
+
// them.
|
|
2122
|
+
emailOrPhoneLabel: 'Email or phone number',
|
|
2123
|
+
emailOrPhonePlaceholder: 'name@example.com or +1 555 000 0000',
|
|
2124
|
+
phoneLabel: 'Phone number',
|
|
2125
|
+
phonePlaceholder: '+1 555 000 0000',
|
|
2126
|
+
otpCodeLabel: 'Verification code',
|
|
2127
|
+
otpCodePlaceholder: '6-digit code',
|
|
2128
|
+
sendOtpButton: 'Get code',
|
|
2129
|
+
resendOtpCountdownText: 'Resend in {seconds}s',
|
|
2130
|
+
usePhoneOtpText: 'Sign in with verification code',
|
|
2131
|
+
usePasswordSignInText: 'Sign in with password instead',
|
|
1871
2132
|
devAdminHint: {
|
|
1872
2133
|
title: 'Development instance',
|
|
1873
2134
|
body: 'Sign in with the seeded dev admin:',
|
|
@@ -1893,7 +2154,7 @@ const en = {
|
|
|
1893
2154
|
passwordMismatchError: 'Passwords do not match',
|
|
1894
2155
|
passwordTooShortError: 'Password must be at least 8 characters',
|
|
1895
2156
|
submitButton: 'Create Account',
|
|
1896
|
-
submittingButton: 'Creating account
|
|
2157
|
+
submittingButton: 'Creating account…',
|
|
1897
2158
|
hasAccountText: 'Already have an account?',
|
|
1898
2159
|
signInText: 'Sign in',
|
|
1899
2160
|
errors: {
|
|
@@ -1914,12 +2175,28 @@ const en = {
|
|
|
1914
2175
|
emailLabel: 'Email',
|
|
1915
2176
|
emailPlaceholder: 'name@example.com',
|
|
1916
2177
|
submitButton: 'Send Reset Link',
|
|
1917
|
-
submittingButton: 'Sending
|
|
2178
|
+
submittingButton: 'Sending…',
|
|
1918
2179
|
successTitle: 'Check your email',
|
|
1919
2180
|
successDescription: "We've sent a password reset link to {{email}}. Please check your inbox.",
|
|
1920
2181
|
backToSignInText: 'Back to sign in',
|
|
1921
2182
|
rememberPasswordText: 'Remember your password?',
|
|
1922
2183
|
signInText: 'Sign in',
|
|
2184
|
+
// The SMS branch of the same form: request an OTP, then set the new
|
|
2185
|
+
// password inline instead of following an emailed link. `{seconds}` is
|
|
2186
|
+
// ForgotPasswordForm's own hole (packages/auth/src/ForgotPasswordForm.tsx:367).
|
|
2187
|
+
phoneLabel: 'Phone number',
|
|
2188
|
+
phonePlaceholder: '+1 555 000 0000',
|
|
2189
|
+
otpCodeLabel: 'Verification code',
|
|
2190
|
+
otpCodePlaceholder: '6-digit code',
|
|
2191
|
+
sendOtpButton: 'Get code',
|
|
2192
|
+
resendOtpCountdownText: 'Resend in {seconds}s',
|
|
2193
|
+
newPasswordLabel: 'New password',
|
|
2194
|
+
newPasswordPlaceholder: 'Enter a new password',
|
|
2195
|
+
resetButton: 'Reset Password',
|
|
2196
|
+
usePhoneResetText: 'Reset via SMS code',
|
|
2197
|
+
useEmailResetText: 'Reset via email instead',
|
|
2198
|
+
phoneSuccessTitle: 'Password reset',
|
|
2199
|
+
phoneSuccessDescription: 'Your password has been reset. You can now sign in with your new password.',
|
|
1923
2200
|
},
|
|
1924
2201
|
resetPassword: {
|
|
1925
2202
|
title: 'Set a new password',
|
|
@@ -1953,6 +2230,7 @@ const en = {
|
|
|
1953
2230
|
resentSuccess: 'Verification email sent!',
|
|
1954
2231
|
resentDescription: 'Please check your inbox and click the verification link.',
|
|
1955
2232
|
resendFailed: 'Cannot resend verification email',
|
|
2233
|
+
resendUnavailable: 'Email delivery may not be configured for this environment. Contact support if this persists.',
|
|
1956
2234
|
signInLink: 'Go to sign in',
|
|
1957
2235
|
backToSignIn: 'Back to sign in',
|
|
1958
2236
|
backToLogin: 'Back to login',
|
|
@@ -1994,6 +2272,10 @@ const en = {
|
|
|
1994
2272
|
denyFailed: 'Failed to deny request',
|
|
1995
2273
|
invalidTitle: 'Invalid device link',
|
|
1996
2274
|
invalidDescription: 'No device code was provided in the URL.',
|
|
2275
|
+
// Shown when the deployment has the device-authorization plugin off, so
|
|
2276
|
+
// the endpoints 404 (framework#2874 / objectui#2513).
|
|
2277
|
+
disabledTitle: 'Device authorization not enabled',
|
|
2278
|
+
disabledDescription: 'This deployment does not have device authorization enabled, so this device cannot be approved here.',
|
|
1997
2279
|
loading: 'Loading…',
|
|
1998
2280
|
cancel: 'Cancel',
|
|
1999
2281
|
},
|
|
@@ -2054,6 +2336,38 @@ const en = {
|
|
|
2054
2336
|
},
|
|
2055
2337
|
},
|
|
2056
2338
|
},
|
|
2339
|
+
// The OAuth authorization-code consent screen (`/oauth/consent`). A third
|
|
2340
|
+
// party asks for scopes and the signed-in user grants or denies them, so
|
|
2341
|
+
// every string here is a security decision the user must read in their own
|
|
2342
|
+
// language — objectui#3546 slice three.
|
|
2343
|
+
oauth: {
|
|
2344
|
+
consent: {
|
|
2345
|
+
// `{{appName}}` is the client's registered name, or `unknownApp` when the
|
|
2346
|
+
// client metadata carries none. `{{suffix}}` is the parenthesised account
|
|
2347
|
+
// hint the page passes (empty when no user is loaded), so it must stay
|
|
2348
|
+
// attached to the sentence's last word.
|
|
2349
|
+
title: '{{appName}} wants to access your account',
|
|
2350
|
+
request: '{{appName}} is requesting permission{{suffix}}.',
|
|
2351
|
+
unknownApp: 'an application',
|
|
2352
|
+
willAllow: 'This app will be able to:',
|
|
2353
|
+
// Only the four scopes the page maps by name; anything else renders the
|
|
2354
|
+
// raw scope string, which is why there is no generic entry here.
|
|
2355
|
+
scope: {
|
|
2356
|
+
openid: 'Confirm your identity',
|
|
2357
|
+
profile: 'Read your basic profile (name, picture)',
|
|
2358
|
+
email: 'Read your email address',
|
|
2359
|
+
offlineAccess: 'Stay signed in (refresh access)',
|
|
2360
|
+
},
|
|
2361
|
+
deny: 'Deny',
|
|
2362
|
+
authorize: 'Authorize',
|
|
2363
|
+
submitting: 'Authorizing…',
|
|
2364
|
+
granted: 'Access granted',
|
|
2365
|
+
denied: 'Access denied',
|
|
2366
|
+
noRedirect: 'No redirect URL returned by the server.',
|
|
2367
|
+
failed: 'Consent failed',
|
|
2368
|
+
footer: 'You can revoke access at any time from your account settings.',
|
|
2369
|
+
},
|
|
2370
|
+
},
|
|
2057
2371
|
profile: {
|
|
2058
2372
|
title: 'Profile',
|
|
2059
2373
|
subtitle: 'Manage your account settings',
|
|
@@ -2115,6 +2429,8 @@ const en = {
|
|
|
2115
2429
|
invite: 'Invite member',
|
|
2116
2430
|
members: 'Members',
|
|
2117
2431
|
settings: 'Workspace settings',
|
|
2432
|
+
multiOrgDisabled: 'Creating new organizations is disabled on this instance.',
|
|
2433
|
+
createFailed: 'Failed to create workspace',
|
|
2118
2434
|
},
|
|
2119
2435
|
help: {
|
|
2120
2436
|
onThisPage: 'On this page',
|
|
@@ -2135,7 +2451,7 @@ const en = {
|
|
|
2135
2451
|
area: 'Area',
|
|
2136
2452
|
scope: 'Scope',
|
|
2137
2453
|
packageManagement: 'Package management',
|
|
2138
|
-
searchNavigation: 'Search navigation
|
|
2454
|
+
searchNavigation: 'Search navigation…',
|
|
2139
2455
|
recent: 'Recent',
|
|
2140
2456
|
favorites: 'Favorites',
|
|
2141
2457
|
starred: 'Starred',
|
|
@@ -2151,8 +2467,8 @@ const en = {
|
|
|
2151
2467
|
switchObject: 'Switch Object',
|
|
2152
2468
|
connection: {
|
|
2153
2469
|
connected: 'Connected',
|
|
2154
|
-
connecting: 'Connecting
|
|
2155
|
-
reconnecting: 'Reconnecting
|
|
2470
|
+
connecting: 'Connecting…',
|
|
2471
|
+
reconnecting: 'Reconnecting…',
|
|
2156
2472
|
disconnected: 'Disconnected',
|
|
2157
2473
|
error: 'Connection Error',
|
|
2158
2474
|
},
|
|
@@ -2195,7 +2511,7 @@ const en = {
|
|
|
2195
2511
|
starred: 'Starred',
|
|
2196
2512
|
recent: 'Recent items',
|
|
2197
2513
|
},
|
|
2198
|
-
loading: 'Loading workspace
|
|
2514
|
+
loading: 'Loading workspace…',
|
|
2199
2515
|
recent: 'Recent',
|
|
2200
2516
|
starred: 'Starred',
|
|
2201
2517
|
welcome: 'Build your business system with AI',
|
|
@@ -2218,6 +2534,15 @@ const en = {
|
|
|
2218
2534
|
publishing: 'Publishing…',
|
|
2219
2535
|
published: 'Published! Your changes are live.',
|
|
2220
2536
|
publishFailed: 'Publish failed',
|
|
2537
|
+
// objectui#3546 slice six — the rest of `usePublishAllDrafts`'s toasts
|
|
2538
|
+
// (the ADR-0038 L3 probe / seed health report and the ADR-0066 ⑨
|
|
2539
|
+
// capability-reference lint). Same banner, same publish button, so they
|
|
2540
|
+
// live beside the five keys above rather than in a namespace of their own.
|
|
2541
|
+
nothing: 'Nothing to publish.',
|
|
2542
|
+
probeWarn: 'Published, but verification found problems.',
|
|
2543
|
+
seedWarn: 'Published, but some sample data failed to load.',
|
|
2544
|
+
publishedVerified: 'Published & verified — {{count}} sample row(s) live.',
|
|
2545
|
+
capabilityWarn: 'Authoring check: {{count}} capability reference(s) resolve nowhere.',
|
|
2221
2546
|
},
|
|
2222
2547
|
createFirstApp: 'Create app manually',
|
|
2223
2548
|
systemSettings: 'System Settings',
|
|
@@ -2275,6 +2600,9 @@ const en = {
|
|
|
2275
2600
|
roles: 'Roles',
|
|
2276
2601
|
configuration: 'Configuration',
|
|
2277
2602
|
createApp: 'Create App',
|
|
2603
|
+
administration: 'Administration',
|
|
2604
|
+
datasources: 'Datasources',
|
|
2605
|
+
documentation: 'Documentation',
|
|
2278
2606
|
},
|
|
2279
2607
|
activityFeed: {
|
|
2280
2608
|
title: 'Recent Activity',
|
|
@@ -2303,7 +2631,7 @@ const en = {
|
|
|
2303
2631
|
search: {
|
|
2304
2632
|
title: 'Search',
|
|
2305
2633
|
back: 'Back',
|
|
2306
|
-
placeholder: 'Search objects, dashboards, pages, reports
|
|
2634
|
+
placeholder: 'Search objects, dashboards, pages, reports…',
|
|
2307
2635
|
inputAriaLabel: 'Search objects, dashboards, pages, reports',
|
|
2308
2636
|
resultsCount: '{{count}} result for "{{query}}"',
|
|
2309
2637
|
resultsCountPlural: '{{count}} results for "{{query}}"',
|
|
@@ -2322,6 +2650,7 @@ const en = {
|
|
|
2322
2650
|
empty: {
|
|
2323
2651
|
objectNotFound: 'Object Not Found',
|
|
2324
2652
|
objectNotFoundDescription: 'Object "{{name}}" definition missing. Check your configuration or navigate back to select a valid object.',
|
|
2653
|
+
interfacePageSourceMissing: 'This interface page references "{{name}}", which is not available.',
|
|
2325
2654
|
recordNotFound: 'Record not found',
|
|
2326
2655
|
recordNotFoundDescription: 'The record you are looking for does not exist or may have been deleted.',
|
|
2327
2656
|
pageNotFound: 'Page Not Found',
|
|
@@ -2332,6 +2661,11 @@ const en = {
|
|
|
2332
2661
|
reportNotFoundDescription: 'The report "{{name}}" could not be found. It may have been removed or renamed.',
|
|
2333
2662
|
noAppsConfigured: 'No Apps Configured',
|
|
2334
2663
|
noAppsConfiguredDescription: 'No applications have been registered. Create your first app or visit System Settings to configure your environment.',
|
|
2664
|
+
appNotAvailable: 'App not available',
|
|
2665
|
+
appNotAvailableDescription: 'This app is not available yet — it may still be publishing. Try again in a moment.',
|
|
2666
|
+
appAccessDenied: "You don't have access to this app",
|
|
2667
|
+
appAccessDeniedDescription: 'This app exists, but your account is not authorized to open it. Ask an administrator to grant you access.',
|
|
2668
|
+
appAccessDeniedHome: 'Back to home',
|
|
2335
2669
|
createFirstApp: 'Create Your First App',
|
|
2336
2670
|
systemSettings: 'System Settings',
|
|
2337
2671
|
back: 'Back',
|
|
@@ -2374,6 +2708,39 @@ const en = {
|
|
|
2374
2708
|
confirmNote: 'Publishing releases all {{count}} pending drafts of this package atomically.',
|
|
2375
2709
|
publishConfirm: 'Publish all',
|
|
2376
2710
|
},
|
|
2711
|
+
// ADR-0045 — the materialized-but-unlisted app banner
|
|
2712
|
+
// (UnpublishedAppBar.tsx), sibling of draftBar above.
|
|
2713
|
+
unpublishedBar: {
|
|
2714
|
+
message: 'Unpublished app — fully functional, but only builders can see it. Publish to make it visible to your users.',
|
|
2715
|
+
publish: 'Publish',
|
|
2716
|
+
publishing: 'Publishing…',
|
|
2717
|
+
published: 'Published! The app is now visible to your users.',
|
|
2718
|
+
publishFailed: 'Publish failed',
|
|
2719
|
+
},
|
|
2720
|
+
// ADR-0067 — the append-only build/revert timeline (CommitTimeline.tsx);
|
|
2721
|
+
// `button` is the banner's entry point into it.
|
|
2722
|
+
history: {
|
|
2723
|
+
button: 'History',
|
|
2724
|
+
title: 'Build history',
|
|
2725
|
+
description: 'Every change to this app, newest first. Revert any step to undo it — no publish confirmation needed.',
|
|
2726
|
+
loadFailed: 'Could not load history:',
|
|
2727
|
+
// objectui#3529 — the retryable class gets its own sentence: a 503 means
|
|
2728
|
+
// the read never reached the commit store, which is a different operator
|
|
2729
|
+
// disposition from a 404/500 and must not read as a bare status code.
|
|
2730
|
+
loadFailedUnavailable: 'Commit store temporarily unreachable — this read did not happen, so no history is shown. Retry in a moment.',
|
|
2731
|
+
loading: 'Loading history…',
|
|
2732
|
+
empty: 'No history yet for this app.',
|
|
2733
|
+
revertLabel: 'Reverted a change',
|
|
2734
|
+
applyLabel: 'Build change',
|
|
2735
|
+
revert: 'revert',
|
|
2736
|
+
items: 'item(s)',
|
|
2737
|
+
revertAction: 'Revert',
|
|
2738
|
+
reverted: 'Reverted — the change has been undone.',
|
|
2739
|
+
revertFailed: 'Revert failed',
|
|
2740
|
+
// The WRITE half of the same fact. Not "try again": the revert may have
|
|
2741
|
+
// landed before the 503, and re-issuing appends a second revert commit.
|
|
2742
|
+
revertUnavailable: 'Commit store temporarily unreachable — the revert may not have been applied. Reopen this history to check before retrying.',
|
|
2743
|
+
},
|
|
2377
2744
|
},
|
|
2378
2745
|
renderer: {
|
|
2379
2746
|
noPageSchema: 'No page schema provided',
|
|
@@ -2441,7 +2808,7 @@ const en = {
|
|
|
2441
2808
|
deleteFailed: 'Failed to delete view',
|
|
2442
2809
|
},
|
|
2443
2810
|
dashboardActions: {
|
|
2444
|
-
|
|
2811
|
+
printDialogOpening: 'Opening your browser’s print dialog (not a PDF export)',
|
|
2445
2812
|
exportFailed: 'Export failed: {{message}}',
|
|
2446
2813
|
forecastSoon: 'Forecast view coming soon',
|
|
2447
2814
|
},
|
|
@@ -2476,6 +2843,149 @@ const en = {
|
|
|
2476
2843
|
emptyDescription: 'Create your first workspace to get started.',
|
|
2477
2844
|
noMatches: 'No workspaces match your search.',
|
|
2478
2845
|
},
|
|
2846
|
+
// Organization MANAGEMENT (packages/app-shell/src/console/organizations/**
|
|
2847
|
+
// plus the workspace switcher). Distinct from `organizations` above, which
|
|
2848
|
+
// is the org PICKER — same domain, different surface, and the singular /
|
|
2849
|
+
// plural spelling is the only thing telling them apart at a call site.
|
|
2850
|
+
organization: {
|
|
2851
|
+
roles: {
|
|
2852
|
+
owner: 'Owner',
|
|
2853
|
+
admin: 'Admin',
|
|
2854
|
+
delegatedAdmin: 'Delegated Admin',
|
|
2855
|
+
member: 'Member',
|
|
2856
|
+
},
|
|
2857
|
+
errors: {
|
|
2858
|
+
notAllowedToInvite: 'You are not allowed to invite users to this organization',
|
|
2859
|
+
notAllowedToInviteWithRole: 'You are not allowed to invite a user with this role',
|
|
2860
|
+
alreadyInvited: 'This user has already been invited to this organization',
|
|
2861
|
+
organizationExists: 'Organization already exists',
|
|
2862
|
+
slugTaken: 'That URL slug is already taken',
|
|
2863
|
+
notAllowedToCreate: 'You are not allowed to create a new organization',
|
|
2864
|
+
notTheRecipient: 'You are not the recipient of the invitation',
|
|
2865
|
+
invitationNotFound: 'This invitation no longer exists or has expired',
|
|
2866
|
+
unknown: 'Something went wrong. Please try again.',
|
|
2867
|
+
},
|
|
2868
|
+
backToList: 'Back to organizations',
|
|
2869
|
+
notFound: 'Organization not found',
|
|
2870
|
+
notFoundDescription: 'This organization does not exist or you do not have access.',
|
|
2871
|
+
tabs: {
|
|
2872
|
+
members: 'Members',
|
|
2873
|
+
invitations: 'Invitations',
|
|
2874
|
+
settings: 'Settings',
|
|
2875
|
+
},
|
|
2876
|
+
members: {
|
|
2877
|
+
title: 'Members',
|
|
2878
|
+
inviteMember: 'Invite member',
|
|
2879
|
+
inviteRestrictedNote: 'Only organization admins can invite members.',
|
|
2880
|
+
removeMember: 'Remove member',
|
|
2881
|
+
removeConfirmTitle: 'Remove member?',
|
|
2882
|
+
removeConfirmDescription: 'This will remove {{name}} from the organization. They will lose access immediately.',
|
|
2883
|
+
removeConfirmAction: 'Remove',
|
|
2884
|
+
memberRemoved: 'Member removed',
|
|
2885
|
+
removeFailed: 'Failed to remove member',
|
|
2886
|
+
roleUpdated: 'Role updated',
|
|
2887
|
+
roleUpdateFailed: 'Failed to update role',
|
|
2888
|
+
memberActions: 'Member actions',
|
|
2889
|
+
loadFailed: 'Failed to load members',
|
|
2890
|
+
},
|
|
2891
|
+
invitations: {
|
|
2892
|
+
title: 'Invitations',
|
|
2893
|
+
empty: 'No invitations found.',
|
|
2894
|
+
expiresAt: 'Expires',
|
|
2895
|
+
linkCopied: 'Invitation link copied',
|
|
2896
|
+
copyFailed: 'Failed to copy link',
|
|
2897
|
+
canceled: 'Invitation canceled',
|
|
2898
|
+
cancelFailed: 'Failed to cancel invitation',
|
|
2899
|
+
cancelTitle: 'Cancel invitation?',
|
|
2900
|
+
cancelDescription: 'The invitation for {{email}} will be revoked.',
|
|
2901
|
+
cancelAction: 'Cancel invitation',
|
|
2902
|
+
inviteTitle: 'Invite a member',
|
|
2903
|
+
inviteDescription: 'They will receive an invitation to join this organization.',
|
|
2904
|
+
emailLabel: 'Email',
|
|
2905
|
+
roleLabel: 'Role',
|
|
2906
|
+
// Placement = business unit + positions applied to the invitee on
|
|
2907
|
+
// accept. The lists are already filtered to what the inviter may
|
|
2908
|
+
// delegate.
|
|
2909
|
+
placementLabel: 'Placement (optional)',
|
|
2910
|
+
placementDescription: 'Applied when the invitation is accepted. Only units and positions you may delegate are listed.',
|
|
2911
|
+
businessUnitLabel: 'Business unit',
|
|
2912
|
+
businessUnitPlaceholder: 'No placement',
|
|
2913
|
+
positionsLabel: 'Positions',
|
|
2914
|
+
sendInvite: 'Send invite',
|
|
2915
|
+
sentTitle: 'Invitation created',
|
|
2916
|
+
sentDescription: 'Share the link below with the invitee. They will need to sign in to accept.',
|
|
2917
|
+
linkLabel: 'Accept link',
|
|
2918
|
+
invitedAs: '{{email}} invited as {{role}}',
|
|
2919
|
+
copyLinkLabel: 'Copy invitation link',
|
|
2920
|
+
loadFailed: 'Failed to load invitations',
|
|
2921
|
+
inviteFailed: 'Failed to invite member',
|
|
2922
|
+
status: {
|
|
2923
|
+
all: 'All',
|
|
2924
|
+
pending: 'Pending',
|
|
2925
|
+
accepted: 'Accepted',
|
|
2926
|
+
rejected: 'Rejected',
|
|
2927
|
+
canceled: 'Canceled',
|
|
2928
|
+
},
|
|
2929
|
+
},
|
|
2930
|
+
settings: {
|
|
2931
|
+
generalTitle: 'General',
|
|
2932
|
+
generalDescription: 'Update your organization information.',
|
|
2933
|
+
readOnlyNote: 'Only owners can change settings.',
|
|
2934
|
+
nameLabel: 'Organization name',
|
|
2935
|
+
// The org's URL segment. `deleteConfirmSlugLabel` asks the user to
|
|
2936
|
+
// retype it, so the two must keep naming the same thing.
|
|
2937
|
+
slugLabel: 'Slug',
|
|
2938
|
+
logoLabel: 'Logo',
|
|
2939
|
+
logoUpload: 'Upload',
|
|
2940
|
+
logoReplace: 'Replace',
|
|
2941
|
+
logoClear: 'Remove',
|
|
2942
|
+
logoUploaded: 'Logo uploaded — save to apply',
|
|
2943
|
+
logoUploadFailed: 'Failed to upload logo',
|
|
2944
|
+
save: 'Save changes',
|
|
2945
|
+
saved: 'Settings saved',
|
|
2946
|
+
saveFailed: 'Failed to save settings',
|
|
2947
|
+
leaveTitle: 'Leave organization',
|
|
2948
|
+
leaveDescription: 'You will lose access to this organization.',
|
|
2949
|
+
leaveAction: 'Leave',
|
|
2950
|
+
leaveConfirmTitle: 'Leave organization?',
|
|
2951
|
+
leaveConfirmDescription: 'Are you sure you want to leave {{name}}? You will lose access immediately.',
|
|
2952
|
+
leaveConfirmAction: 'Leave',
|
|
2953
|
+
leftOrg: 'You have left the organization',
|
|
2954
|
+
leaveFailed: 'Failed to leave organization',
|
|
2955
|
+
dangerZone: 'Danger zone',
|
|
2956
|
+
deleteTitle: 'Delete organization',
|
|
2957
|
+
deleteDescription: 'Permanently delete this organization and all its data.',
|
|
2958
|
+
deleteAction: 'Delete',
|
|
2959
|
+
deleteConfirmTitle: 'Delete organization?',
|
|
2960
|
+
deleteConfirmDescription: 'This action is irreversible. All data will be permanently deleted. Type the organization slug to confirm.',
|
|
2961
|
+
deleteConfirmSlugLabel: 'Type "{{slug}}" to confirm',
|
|
2962
|
+
deleteConfirmAction: 'Delete organization',
|
|
2963
|
+
deleted: 'Organization deleted',
|
|
2964
|
+
deleteFailed: 'Failed to delete organization',
|
|
2965
|
+
},
|
|
2966
|
+
accept: {
|
|
2967
|
+
title: 'You have been invited',
|
|
2968
|
+
description: 'You have been invited to join {{orgName}} as {{role}}.',
|
|
2969
|
+
errorTitle: 'Invitation unavailable',
|
|
2970
|
+
goToOrgs: 'Go to organizations',
|
|
2971
|
+
loading: 'Loading invitation…',
|
|
2972
|
+
organization: 'Organization',
|
|
2973
|
+
role: 'Role',
|
|
2974
|
+
expiresAt: 'Expires',
|
|
2975
|
+
accept: 'Accept invitation',
|
|
2976
|
+
accepted: 'Invitation accepted',
|
|
2977
|
+
acceptFailed: 'Failed to accept invitation',
|
|
2978
|
+
decline: 'Decline',
|
|
2979
|
+
declined: 'Invitation declined',
|
|
2980
|
+
declineFailed: 'Failed to decline invitation',
|
|
2981
|
+
},
|
|
2982
|
+
switcher: {
|
|
2983
|
+
label: 'Switch organization',
|
|
2984
|
+
groupLabel: 'Working organization',
|
|
2985
|
+
groupHint: 'New records are created here. Views show data from all your organizations.',
|
|
2986
|
+
manageMembers: 'Manage members',
|
|
2987
|
+
},
|
|
2988
|
+
},
|
|
2479
2989
|
notifications: {
|
|
2480
2990
|
regionLabel: 'Notifications',
|
|
2481
2991
|
empty: 'No notifications',
|
|
@@ -2492,6 +3002,12 @@ const en = {
|
|
|
2492
3002
|
viewApprovals: 'View approvals',
|
|
2493
3003
|
noPendingApprovals: 'No pending approvals',
|
|
2494
3004
|
openApprovalsInbox: 'Open Approvals Inbox',
|
|
3005
|
+
// Bell-badge breakdown (#7233): the badge sums unread notification topics
|
|
3006
|
+
// and pending approvals, then clamps at "9+". These three spell the sum
|
|
3007
|
+
// out inside the popover so the number is explainable.
|
|
3008
|
+
badgeTotal: '{{total}} total',
|
|
3009
|
+
badgeNotifications: '{{unread}} notifications',
|
|
3010
|
+
badgeApprovals: '{{approvals}} pending approvals',
|
|
2495
3011
|
},
|
|
2496
3012
|
publicForm: {
|
|
2497
3013
|
submit: 'Submit',
|
|
@@ -2767,6 +3283,7 @@ const en = {
|
|
|
2767
3283
|
dismiss: 'Dismiss',
|
|
2768
3284
|
openOnCloud: 'Open on cloud',
|
|
2769
3285
|
backHome: 'Back to home',
|
|
3286
|
+
updateTo: 'Update',
|
|
2770
3287
|
},
|
|
2771
3288
|
install: {
|
|
2772
3289
|
dialogTitle: 'Install {{name}}',
|
|
@@ -2783,6 +3300,37 @@ const en = {
|
|
|
2783
3300
|
localManifestConflict: '{{message}}\nTip: a local app already owns this manifest_id. Remove it from objectstack.config.ts first.',
|
|
2784
3301
|
localUnauthorized: 'Sign in to this runtime first, then try again.',
|
|
2785
3302
|
localMarketplaceUnavailable: 'This runtime has no OS_CLOUD_URL configured, so the marketplace catalog is unreachable.',
|
|
3303
|
+
updateTo: 'Update → v{{version}}',
|
|
3304
|
+
installedVersion: 'Installed v{{version}}',
|
|
3305
|
+
},
|
|
3306
|
+
// objectui#3546 — the MarketplacePage "Your organization" strip.
|
|
3307
|
+
org: {
|
|
3308
|
+
heading: 'Your organization',
|
|
3309
|
+
install: 'Install',
|
|
3310
|
+
installed: 'Installed {{name}}',
|
|
3311
|
+
installedBadge: 'Installed',
|
|
3312
|
+
installing: 'Installing…',
|
|
3313
|
+
},
|
|
3314
|
+
// ADR-0025 PD4 §3.5/§3.11 — the pre-install consent panel
|
|
3315
|
+
// (PluginDisclosure.tsx). `runtime` is a CLOSED enum: spec
|
|
3316
|
+
// PluginRuntimeSchema = z.enum(['node', 'sandbox', 'worker']).
|
|
3317
|
+
disclosure: {
|
|
3318
|
+
containsCode: 'This package contains code',
|
|
3319
|
+
reviewed: 'Reviewed & approved',
|
|
3320
|
+
unreviewed: 'Not yet reviewed',
|
|
3321
|
+
signed: 'Signed',
|
|
3322
|
+
grantsIntro: 'On install, this package will be granted:',
|
|
3323
|
+
services: 'Platform services',
|
|
3324
|
+
hooks: 'Lifecycle hooks',
|
|
3325
|
+
network: 'Network access',
|
|
3326
|
+
fs: 'Filesystem access',
|
|
3327
|
+
noPermissions: 'Requests no special permissions.',
|
|
3328
|
+
acknowledge: 'I understand this package runs code and grants the permissions above.',
|
|
3329
|
+
runtime: {
|
|
3330
|
+
node: 'In-process · full trust',
|
|
3331
|
+
sandbox: 'Sandboxed',
|
|
3332
|
+
worker: 'Out-of-process',
|
|
3333
|
+
},
|
|
2786
3334
|
},
|
|
2787
3335
|
// ADR-0090 D5/D9 — a package's isDefault permission set is an
|
|
2788
3336
|
// install-time suggestion to bind it to the everyone/guest position;
|
|
@@ -3052,5 +3600,165 @@ const en = {
|
|
|
3052
3600
|
addSort: 'Add sort',
|
|
3053
3601
|
removeSort: 'Remove sort',
|
|
3054
3602
|
},
|
|
3603
|
+
// `@object-ui/collaboration` — the comment thread's copy (objectstack#5506,
|
|
3604
|
+
// objectui#3424). The package used to carry every one of these as an English
|
|
3605
|
+
// literal, so a zh session read a Chinese console with an English comment
|
|
3606
|
+
// thread inside it. `COLLAB_DEFAULT_TRANSLATIONS` in that package mirrors
|
|
3607
|
+
// this namespace verbatim as its no-provider fallback; keep the two in step.
|
|
3608
|
+
//
|
|
3609
|
+
// The generic action words (Save / Cancel / Edit / Delete) are NOT repeated
|
|
3610
|
+
// here — the thread reads them from `common`.
|
|
3611
|
+
collaboration: {
|
|
3612
|
+
// Thread header. `commentCount`/`commentCountOne` are two keys, NOT an
|
|
3613
|
+
// i18next `_one`/`_other` pair — see the `reactionCount` note under
|
|
3614
|
+
// `detail`: zh/ja/ko have no separate singular form, so those packs would
|
|
3615
|
+
// legitimately omit the `_one` half and `all-locales-key-parity` reads a
|
|
3616
|
+
// legitimately-absent half as a lost key.
|
|
3617
|
+
commentCount: '{{count}} comments',
|
|
3618
|
+
commentCountOne: '{{count}} comment',
|
|
3619
|
+
// Appended to the count, separator included, so a translator owns the
|
|
3620
|
+
// whole phrase rather than inheriting an English-shaped ` · ` glue.
|
|
3621
|
+
resolvedSuffix: ' · Resolved',
|
|
3622
|
+
sortComments: 'Sort comments',
|
|
3623
|
+
sortOldest: 'Oldest',
|
|
3624
|
+
sortNewest: 'Newest',
|
|
3625
|
+
resolve: 'Resolve',
|
|
3626
|
+
reopen: 'Reopen',
|
|
3627
|
+
// Relative comment age. Word-level entries only — the component keeps its
|
|
3628
|
+
// existing minute/hour/day buckets and no date library was introduced.
|
|
3629
|
+
justNow: 'just now',
|
|
3630
|
+
minutesAgo: '{{count}}m ago',
|
|
3631
|
+
hoursAgo: '{{count}}h ago',
|
|
3632
|
+
daysAgo: '{{count}}d ago',
|
|
3633
|
+
edited: '(edited)',
|
|
3634
|
+
// Reaction-chip tooltip. A DEDICATED pair rather than `detail.reactionCount`:
|
|
3635
|
+
// that one interpolates `{{emoji}}`, and here the emoji is the chip's
|
|
3636
|
+
// visible label with nothing to hand the placeholder.
|
|
3637
|
+
reactionCount: '{{count}} reactions',
|
|
3638
|
+
reactionCountOne: '{{count}} reaction',
|
|
3639
|
+
addThumbsUp: 'Add thumbs up',
|
|
3640
|
+
reply: 'Reply',
|
|
3641
|
+
// Accessible names for the three emoji-only controls (objectui#3441).
|
|
3642
|
+
// A `button`'s accessible name is computed from its CONTENT before `title`
|
|
3643
|
+
// is ever consulted, so '👍' / '❤️' / '✕' named themselves — these keys are
|
|
3644
|
+
// wired as `aria-label`, which overrides content.
|
|
3645
|
+
//
|
|
3646
|
+
// `reactThumbsUp` stays distinct from `addThumbsUp` above even though both
|
|
3647
|
+
// dispatch the same reaction today: that one names the reaction-bar `+`
|
|
3648
|
+
// picker, and the two render side by side on a comment that already has
|
|
3649
|
+
// reactions.
|
|
3650
|
+
reactThumbsUp: 'React with thumbs up',
|
|
3651
|
+
reactHeart: 'React with heart',
|
|
3652
|
+
// Not `common.cancel`: an accessible name has to say what is cancelled.
|
|
3653
|
+
// Only the reply TARGET is dropped — the composer keeps whatever was typed.
|
|
3654
|
+
cancelReply: 'Cancel reply',
|
|
3655
|
+
replyingTo: 'Replying to {{name}}…',
|
|
3656
|
+
// The no-author-found half of the reply banner, as a whole sentence:
|
|
3657
|
+
// languages that inflect around the addressee cannot build it by
|
|
3658
|
+
// substituting a noun into the `{{name}}` form.
|
|
3659
|
+
replyingToComment: 'Replying to comment…',
|
|
3660
|
+
commentPlaceholder: 'Add a comment… (use @ to mention)',
|
|
3661
|
+
send: 'Send',
|
|
3662
|
+
// Presence avatar stack (objectui#3440). `presentUserCount*` is the avatar
|
|
3663
|
+
// group's `aria-label` — with only images and initials inside, that label
|
|
3664
|
+
// IS the control for a screen reader. Two keys, same reason as
|
|
3665
|
+
// `commentCount` above.
|
|
3666
|
+
presentUserCount: '{{count}} users present',
|
|
3667
|
+
presentUserCountOne: '{{count}} user present',
|
|
3668
|
+
moreUserCount: '{{count}} more users',
|
|
3669
|
+
moreUserCountOne: '{{count}} more user',
|
|
3670
|
+
// Avatar tooltip. The parentheses are part of the translation, spacing
|
|
3671
|
+
// included, so the CJK packs can drop the space English puts before `(`.
|
|
3672
|
+
userStatusTitle: '{{name}} ({{status}})',
|
|
3673
|
+
// Display copy for the `PresenceUser['status']` enum. The enum value
|
|
3674
|
+
// itself stays raw data — it is translated at the render exit only, and a
|
|
3675
|
+
// status outside the union falls back to the raw string.
|
|
3676
|
+
statusActive: 'active',
|
|
3677
|
+
statusIdle: 'idle',
|
|
3678
|
+
statusAway: 'away',
|
|
3679
|
+
},
|
|
3680
|
+
// Multi-step form (plugin-form's WizardForm). `{{fields}}` is a
|
|
3681
|
+
// comma-joined, already-truncated label list built at the call site.
|
|
3682
|
+
wizard: {
|
|
3683
|
+
missingRequired: 'Please complete the required fields: {{fields}}',
|
|
3684
|
+
},
|
|
3685
|
+
// Console › System › Applications (objectui#4307). The page's OWN chrome —
|
|
3686
|
+
// headings, controls, badges and the frames of its toasts.
|
|
3687
|
+
//
|
|
3688
|
+
// What is deliberately NOT here: the server's refusal text. Each failure
|
|
3689
|
+
// toast is a template with a `{{reason}}` hole, and what fills that hole is
|
|
3690
|
+
// whatever `PUT/DELETE /api/v1/meta/app/:name` answered (`forbidden:
|
|
3691
|
+
// manage_metadata required`, a 5xx body, an offline message), passed through
|
|
3692
|
+
// untranslated. The server does not promise a fixed catalogue of sentences,
|
|
3693
|
+
// so there is nothing here for those to key against — see PR #4300 and the
|
|
3694
|
+
// page's own header.
|
|
3695
|
+
appManagement: {
|
|
3696
|
+
title: 'Applications',
|
|
3697
|
+
subtitle: 'Manage all configured applications',
|
|
3698
|
+
newApp: 'New App',
|
|
3699
|
+
// `searchLabel` is the input's sr-only `<label>`; `searchPlaceholder` is
|
|
3700
|
+
// what a sighted user reads. Both, because a placeholder is not a label.
|
|
3701
|
+
searchLabel: 'Search apps',
|
|
3702
|
+
searchPlaceholder: 'Search apps…',
|
|
3703
|
+
selectedCount: '{{n}} selected',
|
|
3704
|
+
// The two bulk buttons. Bare verbs — the selection badge beside them is
|
|
3705
|
+
// what says how many rows they act on.
|
|
3706
|
+
bulkEnable: 'Enable',
|
|
3707
|
+
bulkDisable: 'Disable',
|
|
3708
|
+
selectAll: 'Select all ({{n}})',
|
|
3709
|
+
empty: 'No apps found.',
|
|
3710
|
+
defaultBadge: 'Default',
|
|
3711
|
+
active: 'Active',
|
|
3712
|
+
inactive: 'Inactive',
|
|
3713
|
+
// Row controls. Each is a pair: the `title` tooltip names the ACTION, the
|
|
3714
|
+
// `aria-label` names the action AND its target, because a screen reader
|
|
3715
|
+
// reaching an icon button has no row context to borrow from.
|
|
3716
|
+
selectApp: 'Select {{name}}',
|
|
3717
|
+
openApp: 'Open app',
|
|
3718
|
+
openAppNamed: 'Open {{name}}',
|
|
3719
|
+
editApp: 'Edit app',
|
|
3720
|
+
editAppNamed: 'Edit {{name}}',
|
|
3721
|
+
editWithAi: 'Edit with AI',
|
|
3722
|
+
editWithAiNamed: 'Edit {{name}} with AI',
|
|
3723
|
+
enableApp: 'Enable app',
|
|
3724
|
+
disableApp: 'Disable app',
|
|
3725
|
+
enableAppNamed: 'Enable {{name}}',
|
|
3726
|
+
disableAppNamed: 'Disable {{name}}',
|
|
3727
|
+
setDefault: 'Set as default',
|
|
3728
|
+
setDefaultNamed: 'Set {{name}} as default',
|
|
3729
|
+
deleteApp: 'Delete app',
|
|
3730
|
+
deleteAppNamed: 'Delete {{name}}',
|
|
3731
|
+
// Delete is a two-click control: the first click arms it and the label
|
|
3732
|
+
// becomes the confirmation prompt.
|
|
3733
|
+
confirmDelete: 'Click again to confirm delete',
|
|
3734
|
+
confirmDeleteNamed: 'Confirm delete {{name}}',
|
|
3735
|
+
toast: {
|
|
3736
|
+
noClient: 'Cannot reach the metadata service',
|
|
3737
|
+
// What `reason()` says when the failure carried no message at all. The
|
|
3738
|
+
// ONE localized part of an otherwise verbatim server string.
|
|
3739
|
+
unknownError: 'unknown error',
|
|
3740
|
+
appEnabled: '{{name}} enabled',
|
|
3741
|
+
appDisabled: '{{name}} disabled',
|
|
3742
|
+
toggleFailed: 'Failed to toggle app status: {{reason}}',
|
|
3743
|
+
setDefaultDone: '{{name}} set as default',
|
|
3744
|
+
setDefaultFailed: 'Failed to set default app: {{reason}}',
|
|
3745
|
+
appDeleted: '{{name}} deleted',
|
|
3746
|
+
deleteFailed: 'Failed to delete app: {{reason}}',
|
|
3747
|
+
// `{{n}}` is the count that actually LANDED, not the selection size — a
|
|
3748
|
+
// bulk toggle is N independent writes with no transaction behind them.
|
|
3749
|
+
bulkEnabled: '{{n}} apps enabled',
|
|
3750
|
+
bulkDisabled: '{{n}} apps disabled',
|
|
3751
|
+
bulkFailed: 'Failed for {{n}}: {{details}}',
|
|
3752
|
+
// One entry of `{{details}}`. The bracket pair and the space before it
|
|
3753
|
+
// are part of the translation, so the CJK packs can set their own — the
|
|
3754
|
+
// same reason `detail.userStatusTitle` is keyed rather than composed.
|
|
3755
|
+
bulkFailureEntry: '{{name}} ({{reason}})',
|
|
3756
|
+
// Separator between those entries. Per-locale because list punctuation
|
|
3757
|
+
// is a locale property, not a code constant — `validation.
|
|
3758
|
+
// formInvalidJoiner` is the same key for the same past defect.
|
|
3759
|
+
bulkFailureJoiner: '; ',
|
|
3760
|
+
bulkOperationFailed: 'Bulk operation failed: {{reason}}',
|
|
3761
|
+
},
|
|
3762
|
+
},
|
|
3055
3763
|
};
|
|
3056
3764
|
export default en;
|