You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(i18n): make en the complete source of truth for grid import and set-password (#2872 b/c) (#2877)
`en` and `zh` had drifted in both directions, silently: `fallbackLng: 'en'`
degrades a missing key into English rather than an error, and the missing-key
handler only fires in dev.
- 74 keys existed only in `zh` (`grid.import.*`, `auth.setPassword.*`). They
were never in `en` — the English came from call-site `defaultValue:` args and
a private map inside `ImportWizard` — so no other locale could translate them.
- 4 `en` keys were missing from `zh`, so Chinese users saw English.
`grid.import` had three disagreeing sources (en 62, zh 130, wizard map 133,
union 134, no two the same set); all three now agree on 134. `en` and `zh` are
at exact parity, 2448 keys each.
`IMPORT_DEFAULT_TRANSLATIONS` is kept, not deleted — it is what lets the wizard
render with no `I18nProvider` mounted — and is instead pinned to the `en` pack
by a test. `SetPasswordPage` drops its now-redundant inline `defaultValue:`
args; rendered text is byte-identical.
Two mutation-verified guards: en↔zh full key parity (both directions), and
wizard-map ↔ `en.grid.import` same keys and same text. The other eight packs
are ~357 keys behind and are deliberately not asserted — that backfill needs a
translation-strategy decision (#2872 part a).
unsupportedFile: 'Unsupported file type. Use CSV, TSV, or Excel (.xlsx).',
252
254
parseFailed: 'Could not read this file. Please check the format and try again.',
253
255
fileNeedsHeader: 'File must contain a header row and at least one data row.',
@@ -268,6 +270,13 @@ const en = {
268
270
csvColumn: 'Column',
269
271
mapsTo: 'Maps To',
270
272
typeMismatch: 'Looks like {{type}}',
273
+
autoMatched: 'Auto-matched',
274
+
autoMatchedSummary: 'Auto-matched {{count}} column(s) — review and adjust below.',
275
+
confidence: {
276
+
high: 'High confidence',
277
+
medium: 'Medium confidence',
278
+
low: 'Low confidence',
279
+
},
271
280
type: {
272
281
number: 'Number',
273
282
boolean: 'Boolean',
@@ -285,20 +294,89 @@ const en = {
285
294
clickToFix: '— click a highlighted cell to fix it inline.',
286
295
showingRows: 'Showing {{shown}} of {{total}} rows',
287
296
importing: 'Importing… {{progress}}%',
297
+
asyncQueued: 'Queued — preparing to import…',
298
+
asyncProcessing: 'Importing {{processed}} of {{total}} rows… {{progress}}%',
299
+
asyncLargeHint: 'This file is large, so it will be imported in the background.',
300
+
largeSampleNotice: 'Previewing the first {{shown}} of {{total}} rows.',
301
+
cancelImport: 'Cancel import',
302
+
importCancelled: 'Import cancelled',
303
+
resultsTruncated: 'Showing the first {{count}} row results (of {{total}}).',
288
304
importComplete: 'Import Complete',
289
305
imported: '{{count}} imported',
306
+
createdCount: '{{count}} created',
307
+
updatedCount: '{{count}} updated',
290
308
skippedCount: '{{count}} skipped',
291
309
moreErrors: '…and {{count}} more errors',
310
+
downloadFailed: 'Download failed rows',
311
+
options: 'Import options',
312
+
writeMode: 'When a row matches an existing record',
313
+
writeModeOpt: {
314
+
insert: 'Always create new',
315
+
update: 'Update existing (skip if no match)',
316
+
upsert: 'Update if matched, else create',
317
+
},
318
+
matchFields: 'Match on',
319
+
matchFieldsPlaceholder: 'Choose match field(s)…',
320
+
matchFieldsHint: 'Rows are matched to existing records by these field(s).',
321
+
needMatchFields: 'Select at least one field to match on.',
322
+
optCreateOptions: 'Keep unknown option values',
323
+
optRunAutomations: 'Run automations & triggers',
324
+
optTreatHistorical: 'Import as historical data',
325
+
optTreatHistoricalHint: '(import completed records as-is — skip state-machine checks and keep their original timestamps & author instead of stamping now)',
326
+
optSkipBlankKey: 'Skip rows with a blank match value',
327
+
optBackground: 'Import in the background',
328
+
optBackgroundHint: '(runs as an undoable job)',
329
+
validate: 'Validate data',
330
+
validating: 'Validating…',
331
+
validateHint: 'Check every row against the server before importing.',
332
+
validatePassed: 'All {{ok}} rows are valid.',
333
+
validateFailed: '{{ok}} valid, {{errors}} with errors.',
334
+
errorRowPrefix: 'Row {{row}}: ',
335
+
referenceNotFound: 'No matching record for "{{value}}"',
336
+
referenceAmbiguous: '"{{value}}" matches more than one record — use a unique value or the record id',
337
+
invalidBoolean: '"{{value}}" is not a valid true/false value',
338
+
invalidNumber: '"{{value}}" is not a valid number',
339
+
invalidDate: '"{{value}}" is not a valid date',
340
+
invalidOption: '"{{value}}" is not one of the allowed options',
341
+
requiredValue: 'This field is required',
342
+
matchAmbiguous: 'Matches more than one existing record — use a unique value or the record id',
343
+
history: 'History',
344
+
historyBack: 'Back to import',
345
+
historyDescription: 'Recent imports for this object.',
historyUnsupported: 'Import history isn’t available for this data source.',
351
+
historyColStatus: 'Status',
352
+
historyColRows: 'Rows',
353
+
historyColResult: 'Result',
354
+
historyColTime: 'When',
355
+
errorCount: '{{count}} errors',
356
+
undoImport: 'Undo import',
357
+
undoing: 'Undoing…',
358
+
undoConfirm: 'Undo this import? Records it created will be deleted and records it updated will be restored to their previous values.',
359
+
reverted: 'Undone',
360
+
jobStatus: {
361
+
pending: 'Pending',
362
+
running: 'Running',
363
+
succeeded: 'Succeeded',
364
+
failed: 'Failed',
365
+
cancelled: 'Cancelled',
366
+
},
292
367
cancel: 'Cancel',
293
368
back: 'Back',
294
369
next: 'Next',
295
370
close: 'Close',
296
371
importNRows: 'Import {{count}} Rows',
297
372
importingProgress: 'Importing…',
298
-
requiredMark: '*',
299
373
required: 'Required',
300
374
invalidType: 'Invalid {{type}}',
301
-
legacyReferenceBlocked: 'Import blocked: {{fields}} are relation fields that need the server import route to resolve names into record IDs, and this connection doesn\u2019t support it. Importing them as plain text would corrupt the data. Upgrade the backend/client, or unmap these columns and import them separately.',
375
+
legacyReferenceBlocked: 'Import blocked: {{fields}} are relation fields that need the server import route to resolve names into record IDs, and this connection doesn’t support it. Importing them as plain text would corrupt the data. Upgrade the backend/client, or unmap these columns and import them separately.',
376
+
missingRequiredHint: 'Can’t continue — required field(s) not mapped: {{fields}}. Add a matching column to your file, or go back and upload one that includes it.',
377
+
legacyFallbackNotice: 'Imported via a compatibility fallback: this connection doesn’t support the server import route, so values were saved as text without server-side type coercion. Upgrade the backend/client for full import support (type coercion and relation lookups).',
378
+
notAllowed: 'This object is not open for import.',
379
+
requiredMark: '*',
302
380
},
303
381
bulk: {
304
382
confirmDefault: 'This will apply to {{count}} record(s).',
@@ -1847,6 +1925,24 @@ const en = {
1847
1925
shell: {
1848
1926
tenantHostHint: 'You are signing in to this workspace',
1849
1927
},
1928
+
// These were previously supplied only as inline `defaultValue:` args at the
1929
+
// SetPasswordPage call sites, so `zh` was the only pack that could carry a
1930
+
// translation. Owning them here makes the page translatable everywhere.
1931
+
setPassword: {
1932
+
title: 'Set a recovery password',
1933
+
description:
1934
+
'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.',
1935
+
email: 'Email',
1936
+
newPassword: 'New password',
1937
+
confirmPassword: 'Confirm password',
1938
+
submit: 'Set password',
1939
+
submitting: 'Saving…',
1940
+
success: 'Local password set',
1941
+
failed: 'Could not set password',
1942
+
passwordsMismatch: 'Passwords do not match',
1943
+
noSession: 'Your session has expired.',
1944
+
backToSignIn: 'Sign in again',
1945
+
},
1850
1946
layout: {
1851
1947
headline: 'Build powerful business applications, faster.',
1852
1948
subhead: 'The universal platform for enterprise data management, workflows, and analytics.',
0 commit comments