Spreadsheet-UX op een echte relationele engine
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
import { formatCount, type Locale } from '@wellformed/shared';
import type { MessageCatalog } from './entry';
/**
* The dialogs and side panels: column and table editing, import, the tighten
* dry run, the audit trail and the empty workspace.
*/
export const dialogs = {
/* Shared between the column and table dialogs. */
'dialogs.saveChanges': { en: 'Save changes', nl: 'Wijzigingen opslaan' },
/* ColumnDialog */
'column.editTitle': {
en: (name: string) => `Edit ${name}`,
nl: (name: string) => `${name} bewerken`,
},
'column.addTitle': {
en: (table: string) => `Add column to ${table}`,
nl: (table: string) => `Kolom toevoegen aan ${table}`,
},
'column.addColumn': { en: 'Add column', nl: 'Kolom toevoegen' },
'column.namePlaceholder': { en: 'e.g. Unit price', nl: 'bijv. Prijs per stuk' },
'column.type': { en: 'Type', nl: 'Type' },
'column.typeViaTighten': {
en: 'Fixed here. The type and its rules are changed by tightening — under the column header’s right-click menu.',
nl: 'Ligt hier vast. Het type en de regels veranderen via aanscherpen — onder het rechtsklikmenu van de kolomkop.',
},
'column.computedTypeHint': {
en: "A computed column's type is its expression. Change the expression, or add a stored column beside it.",
nl: 'Het type van een berekende kolom is zijn expressie. Pas de expressie aan, of zet er een opgeslagen kolom naast.',
},
'column.options': { en: 'Options', nl: 'Opties' },
'column.commaSeparated': { en: 'comma-separated', nl: 'kommagescheiden' },
'column.optionsPlaceholder': { en: 'Low, Medium, High', nl: 'Laag, Gemiddeld, Hoog' },
'column.linksTo': { en: 'Links to', nl: 'Koppelt aan' },
'column.linkHint': {
en: "Stored as a real foreign key. Deleting the target row clears this cell. Text already in the column is matched against the other table's row labels.",
nl: 'Opgeslagen als een echte foreign key. Verwijder je de doelrij, dan wordt deze cel leeggemaakt. Tekst die al in de kolom staat wordt gematcht met de rijlabels van de andere tabel.',
},
'column.expression': { en: 'Expression', nl: 'Expressie' },
'column.compiles': {
en: 'Compiles. This becomes part of the SELECT.',
nl: 'Compileert. Dit wordt onderdeel van de SELECT.',
},
'column.exprHint': {
en: 'Formulas speak database names — the ones under each column name here. Follow links with dots; aggregate across a linked table with SUM(orders.total) or COUNT(orders).',
nl: 'Formules spreken databasenamen — die onder elke kolomnaam hier staan. Volg koppelingen met punten; aggregeer over een gekoppelde tabel met SUM(orders.totaal) of COUNT(orders).',
},
'column.exprPlaceholder': { en: 'qty * product.price', nl: 'aantal * product.prijs' },
'column.displayFormat': { en: 'Display format', nl: 'Weergaveformaat' },
'column.formatDefaultOption': {
en: (phrase: string) => `Default for the type — ${phrase}`,
nl: (phrase: string) => `Standaard voor het type — ${phrase}`,
},
'column.numbering': { en: 'Numbering', nl: 'Nummering' },
'column.optional': { en: 'optional', nl: 'optioneel' },
'column.sequencePreview': {
en: (example: string) =>
`A new row arriving with this cell empty gets the next number — the first one today would be ${example}. A date in the pattern starts the count over when it changes; a typed value always stands.`,
nl: (example: string) =>
`Een nieuwe rij die met deze cel leeg binnenkomt krijgt het volgende nummer — de eerste van vandaag zou ${example} zijn. Een datum in het patroon laat de telling opnieuw beginnen zodra hij verandert; een getypte waarde blijft altijd staan.`,
},
'column.sequenceHint': {
en: '{n} counts, {nnn} pads to three digits; {yyyy} {yy} {mm} {q} are today. Put the year or quarter in the pattern and the count restarts with it.',
nl: '{n} telt, {nnn} vult aan tot drie cijfers; {yyyy} {yy} {mm} {q} zijn vandaag. Zet het jaar of kwartaal in het patroon en de telling begint daarmee opnieuw.',
},
'column.required': {
en: 'Required — reject rows that leave it blank',
nl: 'Verplicht — rijen die dit leeg laten worden geweigerd',
},
'column.unique': {
en: 'Unique — a real index, so the database refuses the second one',
nl: 'Uniek — een echte index, dus de database weigert de tweede',
},
'column.tightened': {
en: (name: string, cleared: number, locale: Locale) =>
`Tightened ${name}; ${formatCount(cleared, locale)} value${cleared === 1 ? '' : 's'} cleared.`,
nl: (name: string, cleared: number, locale: Locale) =>
`${name} aangescherpt; ${formatCount(cleared, locale)} ${cleared === 1 ? 'waarde' : 'waarden'} leeggemaakt.`,
},
'column.updated': {
en: (name: string) => `Updated ${name}.`,
nl: (name: string) => `${name} is bijgewerkt.`,
},
'column.added': {
en: (name: string) => `Added ${name}.`,
nl: (name: string) => `${name} is toegevoegd.`,
},
/* The column types, named for the picker. The identifiers stay identifiers. */
'column.typeText': { en: 'Text', nl: 'Tekst' },
'column.typeNumber': { en: 'Number', nl: 'Getal' },
'column.typeCurrency': { en: 'Currency', nl: 'Valuta' },
'column.typeDate': { en: 'Date', nl: 'Datum' },
'column.typeCheckbox': { en: 'Checkbox', nl: 'Selectievakje' },
'column.typeSelect': { en: 'Select (one of a list)', nl: 'Keuzelijst (één uit een lijst)' },
'column.typeLink': { en: 'Link to table (foreign key)', nl: 'Koppeling naar tabel (foreign key)' },
'column.typeFormula': { en: 'Formula', nl: 'Formule' },
'column.typeRollup': { en: 'Rollup', nl: 'Rollup' },
/* The display formats, named by what each does to a value. */
'column.formatCurrency': {
en: 'Money — in the workspace currency',
nl: 'Geld — in de valuta van de werkruimte',
},
'column.formatNumber': {
en: 'Number — with thousands separators',
nl: 'Getal — met duizendtalscheiding',
},
'column.formatPercent': {
en: 'Percent — 0.15 reads as 15.0%',
nl: 'Procent — 0.15 wordt getoond als 15,0%',
},
'column.formatDate': { en: 'Date — as stored', nl: 'Datum — zoals opgeslagen' },
'column.formatText': { en: 'Plain — exactly as stored', nl: 'Kaal — precies zoals opgeslagen' },
/* The same formats as a phrase, for saying what the default already is. */
'column.defaultCurrency': { en: 'money', nl: 'geld' },
'column.defaultNumber': { en: 'a grouped number', nl: 'een gegroepeerd getal' },
'column.defaultPercent': { en: 'a percentage', nl: 'een percentage' },
'column.defaultDate': { en: 'as stored', nl: 'zoals opgeslagen' },
'column.defaultText': { en: 'plain text', nl: 'platte tekst' },
/* TableDialog */
'table.editTitle': {
en: (name: string) => `Edit ${name}`,
nl: (name: string) => `${name} bewerken`,
},
'table.newTitle': { en: 'New table', nl: 'Nieuwe tabel' },
'table.createTable': { en: 'Create table', nl: 'Tabel aanmaken' },
'table.saved': {
en: (name: string) => `Saved ${name}.`,
nl: (name: string) => `${name} is opgeslagen.`,
},
'table.created': {
en: (name: string) => `Created table ${name}.`,
nl: (name: string) => `Tabel ${name} is aangemaakt.`,
},
'table.label': { en: 'Name', nl: 'Naam' },
'table.labelHint': { en: 'in your own words', nl: 'in je eigen woorden' },
'table.labelPlaceholder': { en: 'Purchase orders', nl: 'Inkooporders' },
/* The identifier is derived, never asked for; this line shows the
derivation so the SQL bar never surprises anyone. */
'table.inDatabase': {
en: (name: string) => `In the database: ${name}`,
nl: (name: string) => `In de database: ${name}`,
},
'table.sqlNameFixed': {
en: '— fixed; formulas address it by name.',
nl: '— ligt vast; formules verwijzen ernaar op naam.',
},
'table.rowLabelColumn': { en: 'Row label column', nl: 'Rijlabelkolom' },
'table.rowLabelDefault': { en: 'Name', nl: 'Naam' },
'table.rowLabelPlaceholder': { en: 'Name', nl: 'Naam' },
'table.rowLabelEditHint': {
en: 'What link chips and the audit trail show for a row.',
nl: 'Wat koppelingschips en de audittrail voor een rij tonen.',
},
'table.rowLabelCreateHint': {
en: 'Created as a text column. It is what link chips and the audit trail show for a row.',
nl: 'Wordt aangemaakt als tekstkolom. Dit is wat koppelingschips en de audittrail voor een rij tonen.',
},
'table.group': { en: 'Group', nl: 'Groep' },
'table.groupHint': {
en: 'a heading in the sidebar, nothing more',
nl: 'een kopje in de zijbalk, meer niet',
},
'table.groupPlaceholder': { en: 'no group', nl: 'geen groep' },
'table.runsBefore': { en: 'Runs ', nl: 'Dit voert ' },
'table.runsAfter': {
en: ' and adds a default view. Add more columns once it exists.',
nl: ' uit en voegt een standaardweergave toe. Voeg meer kolommen toe zodra hij bestaat.',
},
/* ImportDialog */
'import.title': { en: 'Import a spreadsheet', nl: 'Een spreadsheet importeren' },
'import.import': { en: 'Import', nl: 'Importeren' },
'import.importRows': {
en: (n: number, locale: Locale) => `Import ${formatCount(n, locale)} row${n === 1 ? '' : 's'}`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'rij' : 'rijen'} importeren`,
},
'import.couldNotRead': {
en: 'Could not read that file.',
nl: 'Dat bestand kon niet worden gelezen.',
},
'import.couldNotImport': {
en: 'Could not import that file.',
nl: 'Dat bestand kon niet worden geïmporteerd.',
},
'import.chooseFile': { en: 'Choose a file', nl: 'Kies een bestand' },
'import.fileHint': {
en: 'Excel (.xlsx), OpenDocument (.ods), CSV or tab-separated. Wellformed works out the separator, picks the sheet with something in it, and skips the title lines, blank rows and empty columns an export leaves behind.',
nl: 'Excel (.xlsx), OpenDocument (.ods), CSV of tabgescheiden. Wellformed bepaalt zelf het scheidingsteken, kiest het werkblad waar iets in staat, en slaat de titelregels, lege rijen en lege kolommen over die een export achterlaat.',
},
'import.orPaste': { en: '…or paste it here', nl: '…of plak het hier' },
'import.pastePlaceholder': { en: 'Name,Amount\nFirst,10', nl: 'Naam,Bedrag\nEerste,10' },
'import.readingFile': {
en: (name: string) => `Reading ${name}.`,
nl: (name: string) => `${name} wordt gelezen.`,
},
'import.pasteInstead': { en: 'Paste text instead', nl: 'Toch tekst plakken' },
'import.into': { en: 'Into', nl: 'Naar' },
'import.newTable': { en: 'A new table', nl: 'Een nieuwe tabel' },
'import.matchHint': {
en: 'Headers must match the columns already there; nothing new is created.',
nl: 'Koppen moeten overeenkomen met de kolommen die er al staan; er wordt niets nieuws aangemaakt.',
},
'import.tableName': { en: 'Table name', nl: 'Naam van de tabel' },
'import.tableNamePlaceholder': { en: 'Orders 2026', nl: 'Orders 2026' },
'import.previewSummary': {
en: (rows: number, cols: number, source: string, locale: Locale) =>
`${formatCount(rows, locale)} row${rows === 1 ? '' : 's'}, ${formatCount(cols, locale)} column${cols === 1 ? '' : 's'}, ${source}.`,
nl: (rows: number, cols: number, source: string, locale: Locale) =>
`${formatCount(rows, locale)} ${rows === 1 ? 'rij' : 'rijen'}, ${formatCount(cols, locale)} ${cols === 1 ? 'kolom' : 'kolommen'}, ${source}.`,
},
'import.pastLimit': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} row${n === 1 ? '' : 's'} past the limit will be left out.`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'rij boven de limiet wordt' : 'rijen boven de limiet worden'} weggelaten.`,
},
'import.sheet': { en: 'Sheet', nl: 'Werkblad' },
'import.headerFrom': { en: 'Column names come from', nl: 'Kolomnamen komen uit' },
'import.rowOption': {
en: (row: number, text: string) => `Row ${row}: ${text}`,
nl: (row: number, text: string) => `Rij ${row}: ${text}`,
},
'import.blank': { en: '(blank)', nl: '(leeg)' },
'import.groupRowToggle': {
en: (row: number, text: string) =>
`Row ${row} (${text}) groups the columns — put its headings in front of the names`,
nl: (row: number, text: string) =>
`Rij ${row} (${text}) groepeert de kolommen — zet zijn kopjes vóór de namen`,
},
'import.groupN': {
en: (n: number) => `Group ${n}`,
nl: (n: number) => `Groep ${n}`,
},
'import.groupsHint': {
en: 'One word for a whole group: editing a heading renames every column under it, and emptied it drops the prefix.',
nl: 'Eén woord voor een hele groep: een kopje aanpassen hernoemt alle kolommen eronder, en leeggemaakt vervalt het voorvoegsel.',
},
'import.footerToggle': {
en: (n: number, text: string) =>
n === 1
? `The bottom row reads as totals (${text}) — leave it out`
: `The bottom ${n} rows read as totals (${text}) — leave them out`,
nl: (n: number, text: string) =>
n === 1
? `De onderste rij leest als totaalregel (${text}) — niet meenemen`
: `De onderste ${n} rijen lezen als totaalregels (${text}) — niet meenemen`,
},
'import.colColumn': { en: 'Column', nl: 'Kolom' },
'import.colType': { en: 'Type', nl: 'Type' },
'import.colFirstValue': { en: 'First value', nl: 'Eerste waarde' },
'import.nameRuleShort': {
en: 'give the column a name',
nl: 'geef de kolom een naam',
},
'import.labelStaysText': {
en: 'The first column labels the rows, so it stays text.',
nl: 'De eerste kolom benoemt de rijen en blijft dus tekst.',
},
'import.wontRead': {
en: (n: number, problem: string, locale: Locale) =>
`${formatCount(n, locale)} value${n === 1 ? '' : 's'} will not read this way — first: “${problem}”.`,
nl: (n: number, problem: string, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'waarde leest' : 'waarden lezen'} zo niet — eerste: “${problem}”.`,
},
'import.was': {
en: (header: string) => `was “${header}”`,
nl: (header: string) => `was “${header}”`,
},
/* The toast after an import, in parts the component composes. */
'import.landed': {
en: (n: number, table: string, locale: Locale) =>
`Imported ${formatCount(n, locale)} row${n === 1 ? '' : 's'} into ${table}.`,
nl: (n: number, table: string, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'rij' : 'rijen'} geïmporteerd naar ${table}.`,
},
'import.landedSkipped': {
en: (skipped: number, limit: number, locale: Locale) =>
`${formatCount(skipped, locale)} more were past the ${formatCount(limit, locale)}-row limit for one import.`,
nl: (skipped: number, limit: number, locale: Locale) =>
`Nog eens ${formatCount(skipped, locale)} ${skipped === 1 ? 'lag' : 'lagen'} boven de limiet van ${formatCount(limit, locale)} rijen per import.`,
},
'import.landedOverLimit': {
en: 'That takes the workspace past what its plan allows, so it is the last one until you upgrade.',
nl: 'Hiermee gaat de werkruimte over wat het abonnement toestaat, dus dit is de laatste import tot je upgradet.',
},
/* What the parser walked past, in parts the component lists. */
'import.skippedPreamble': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} line${n === 1 ? '' : 's'} above the header`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'regel' : 'regels'} boven de kop`,
},
'import.skippedBlank': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} blank row${n === 1 ? '' : 's'}`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} lege ${n === 1 ? 'rij' : 'rijen'}`,
},
'import.skippedEmptyCols': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} empty column${n === 1 ? '' : 's'}`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} lege ${n === 1 ? 'kolom' : 'kolommen'}`,
},
'import.skippedFooter': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} totals row${n === 1 ? '' : 's'} at the bottom`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} ${n === 1 ? 'totaalregel' : 'totaalregels'} onderaan`,
},
'import.and': { en: ' and ', nl: ' en ' },
'import.skippedSentence': {
en: (listed: string) => `Skipped ${listed}. Check the column names below.`,
nl: (listed: string) =>
`${listed.charAt(0).toUpperCase()}${listed.slice(1)} overgeslagen. Controleer de kolomnamen hieronder.`,
},
/* Where the grid came from, in the words of whichever format it was. */
'import.sourceXlsx': {
en: (sheet: string) => `read from “${sheet}” in an Excel workbook`,
nl: (sheet: string) => `gelezen uit “${sheet}” in een Excel-werkmap`,
},
'import.sourceOds': {
en: (sheet: string) => `read from “${sheet}” in an OpenDocument spreadsheet`,
nl: (sheet: string) => `gelezen uit “${sheet}” in een OpenDocument-spreadsheet`,
},
'import.sourceDelimited': {
en: (delimiter: string) => `separated by ${delimiter}`,
nl: (delimiter: string) => `gescheiden door ${delimiter}`,
},
'import.tidyOffer': {
en: (n: number, locale: Locale) =>
n === 1 ? 'One column could be tighter.' : `${formatCount(n, locale)} columns could be tighter.`,
nl: (n: number, locale: Locale) =>
n === 1 ? 'Eén kolom kan strakker.' : `${formatCount(n, locale)} kolommen kunnen strakker.`,
},
'import.tidyNow': { en: 'Have a look', nl: 'Nalopen' },
'import.delimTabs': { en: 'tabs', nl: 'tabs' },
'import.delimSemicolons': { en: 'semicolons', nl: 'puntkomma’s' },
'import.delimPipes': { en: 'pipes', nl: 'verticale strepen' },
'import.delimCommas': { en: 'commas', nl: 'komma’s' },
/* TightenPanel */
'tighten.dialogTitle': {
en: (column: string) => `Tighten ${column}`,
nl: (column: string) => `${column} aanscherpen`,
},
'tighten.applyBtn': { en: 'Tighten', nl: 'Aanscherpen' },
'tighten.checkFailed': {
en: 'Could not check the column.',
nl: 'De kolom kon niet worden gecontroleerd.',
},
'tighten.noOptionsYet': {
en: 'No options yet. Take them from what the column already says, below.',
nl: 'Nog geen opties. Neem ze hieronder over uit wat er al in de kolom staat.',
},
'tighten.nothingYet': {
en: 'Nothing in this column yet, so nothing can fail to fit.',
nl: 'Er staat nog niets in deze kolom, dus er kan ook niets niet passen.',
},
'tighten.allFit': {
en: (total: number, cleared: number, locale: Locale) =>
`All ${formatCount(total, locale)} rows fit${cleared > 0 ? `, once ${formatCount(cleared, locale)} ${cleared === 1 ? 'is' : 'are'} cleared` : ''}.`,
nl: (total: number, cleared: number, locale: Locale) =>
`Alle ${formatCount(total, locale)} rijen passen${cleared > 0 ? `, zodra ${formatCount(cleared, locale)} ${cleared === 1 ? 'is' : 'zijn'} leeggemaakt` : ''}.`,
},
'tighten.someFit': {
en: (fit: number, total: number, locale: Locale) =>
`${formatCount(fit, locale)} of ${formatCount(total, locale)} row${total === 1 ? '' : 's'} fit${fit === 1 ? 's' : ''}.`,
nl: (fit: number, total: number, locale: Locale) =>
`${formatCount(fit, locale)} van de ${formatCount(total, locale)} rijen ${fit === 1 ? 'past' : 'passen'}.`,
},
'tighten.checking': { en: 'Checking every row…', nl: 'Elke rij wordt gecontroleerd…' },
'tighten.willBeCleared': {
en: (_n: number) => ' — will be cleared',
nl: (n: number) => (n === 1 ? ' — wordt leeggemaakt' : ' — worden leeggemaakt'),
},
'tighten.moreCount': {
en: (n: number, locale: Locale) => `+${formatCount(n, locale)} more`,
nl: (n: number, locale: Locale) => `+${formatCount(n, locale)} meer`,
},
'tighten.emptyCells': {
en: (n: number, locale: Locale) =>
`Empty the ${formatCount(n, locale)} cell${n === 1 ? '' : 's'} that will not convert`,
nl: (n: number, locale: Locale) =>
n === 1
? 'Maak de cel leeg die niet converteert'
: `Maak de ${formatCount(n, locale)} cellen leeg die niet converteren`,
},
'tighten.decimalMark': { en: 'Decimals are marked with', nl: 'Decimalen zijn gemarkeerd met' },
'tighten.decimalPoint': { en: 'a point — 1,234.50', nl: 'een punt — 1,234.50' },
'tighten.decimalComma': { en: 'a comma — 1.234,50', nl: 'een komma — 1.234,50' },
'tighten.readDateAs': { en: 'Read 03/04 as', nl: 'Lees 03/04 als' },
'tighten.dayFirst': { en: '3 April — day first', nl: '3 april — dag eerst' },
'tighten.monthFirst': { en: '4 March — month first', nl: '4 maart — maand eerst' },
'tighten.ambiguousDates': {
en: 'Nothing in this column settles it — no value has a day past the twelfth.',
nl: 'Niets in deze kolom geeft uitsluitsel — geen enkele waarde heeft een dag na de twaalfde.',
},
'tighten.distinctValues': {
en: (n: number, locale: Locale) =>
`${formatCount(n, locale)} distinct value${n === 1 ? '' : 's'} in this column`,
nl: (n: number, locale: Locale) =>
`${formatCount(n, locale)} verschillende ${n === 1 ? 'waarde' : 'waarden'} in deze kolom`,
},
'tighten.makeSelect': { en: 'Make it a select of these', nl: 'Maak er een keuzelijst van' },
'tighten.hideSql': { en: 'Hide the SQL', nl: 'Verberg de SQL' },
'tighten.showSql': { en: 'Show the SQL this runs', nl: 'Toon de SQL die dit uitvoert' },
/* TidyDialog — the tighten walk */
'tidy.title': {
en: (table: string) => `Tighten ${table}`,
nl: (table: string) => `${table} aanscherpen`,
},
'tidy.scanning': { en: 'Reading every column…', nl: 'Elke kolom wordt gelezen…' },
'tidy.checkFailed': {
en: 'Could not read the table.',
nl: 'De tabel kon niet worden gelezen.',
},
'tidy.nothing': {
en: (n: number, locale: Locale) =>
n === 1
? 'Nothing to tighten — the one column already is what its values read as.'
: `Nothing to tighten — each of the ${formatCount(n, locale)} columns already is what its values read as.`,
nl: (n: number, locale: Locale) =>
n === 1
? 'Niets aan te scherpen — de ene kolom is al wat zijn waarden lezen.'
: `Niets aan te scherpen — alle ${formatCount(n, locale)} kolommen zijn al wat hun waarden lezen.`,
},
'tidy.readsAs': {
en: (type: string, present: number, total: number, locale: Locale) =>
`Every filled value here reads as ${type.toLowerCase()} — ${formatCount(present, locale)} of ${formatCount(total, locale)} rows hold one, and all of them fit.`,
nl: (type: string, present: number, total: number, locale: Locale) =>
`Elke gevulde waarde hier leest als ${type.toLowerCase()} — ${formatCount(present, locale)} van de ${formatCount(total, locale)} rijen hebben er een, en ze passen allemaal.`,
},
'tidy.tightenTo': {
en: (type: string) => `Make it a ${type.toLowerCase()} column`,
nl: (type: string) => `Maak er een ${type.toLowerCase()}kolom van`,
},
'tidy.summaryConverted': {
en: (n: number) =>
n === 1
? 'One column was tightened to its real type.'
: `${n} columns were tightened to their real types.`,
nl: (n: number) =>
n === 1
? 'Eén kolom is aangescherpt naar zijn echte type.'
: `${n} kolommen zijn aangescherpt naar hun echte type.`,
},
'tidy.intro': {
en: 'These spellings read as the same answer. Untick a group that is genuinely two answers; correct the surviving spelling where the guess is wrong.',
nl: 'Deze schrijfwijzen lezen als hetzelfde antwoord. Vink een groep uit die echt twee antwoorden is; verbeter de overblijvende schrijfwijze waar de gok fout is.',
},
'tidy.step': {
en: (i: number, n: number) => `column ${i} of ${n}`,
nl: (i: number, n: number) => `kolom ${i} van ${n}`,
},
'tidy.survivorFor': {
en: (value: string) => `The spelling that survives for "${value}"`,
nl: (value: string) => `De schrijfwijze die overblijft voor "${value}"`,
},
'tidy.optionsFollow': {
en: 'The option list follows: merged spellings leave it.',
nl: 'De optielijst volgt mee: samengevoegde schrijfwijzen verdwijnen eruit.',
},
'tidy.apply': { en: 'Tidy this column', nl: 'Ruim deze kolom op' },
'tidy.skip': { en: 'Skip this column', nl: 'Sla deze kolom over' },
'tidy.summary': {
en: (values: number, merges: number, locale: Locale) =>
`${formatCount(values, locale)} value${values === 1 ? '' : 's'} rewritten across ${formatCount(merges, locale)} merge${merges === 1 ? '' : 's'}.`,
nl: (values: number, merges: number, locale: Locale) =>
`${formatCount(values, locale)} ${values === 1 ? 'waarde' : 'waarden'} herschreven in ${formatCount(merges, locale)} ${merges === 1 ? 'samenvoeging' : 'samenvoegingen'}.`,
},
'tidy.applied': {
en: (n: number, locale: Locale) =>
`Tidied — ${formatCount(n, locale)} value${n === 1 ? '' : 's'} rewritten.`,
nl: (n: number, locale: Locale) =>
`Opgeruimd — ${formatCount(n, locale)} ${n === 1 ? 'waarde' : 'waarden'} herschreven.`,
},
'tidy.offerSelect': {
en: (n: number, locale: Locale) =>
`The column now holds ${formatCount(n, locale)} distinct values — few enough to become a real select list, so a typo can never split an answer again.`,
nl: (n: number, locale: Locale) =>
`De kolom heeft nu ${formatCount(n, locale)} verschillende waarden — weinig genoeg voor een echte keuzelijst, zodat een typefout nooit meer een antwoord kan splitsen.`,
},
'tidy.continue': { en: 'Keep it as text', nl: 'Tekst laten' },
'tidy.summaryTightened': {
en: (n: number) =>
n === 1 ? 'One column became a select list.' : `${n} columns became select lists.`,
nl: (n: number) =>
n === 1 ? 'Eén kolom is een keuzelijst geworden.' : `${n} kolommen zijn keuzelijsten geworden.`,
},
'tidy.summaryOptionsOnly': {
en: 'No cell needed rewriting — only the option list was tidied.',
nl: 'Geen cel hoefde te worden herschreven — alleen de optielijst is opgeruimd.',
},
'tidy.summaryNothing': { en: 'Nothing was changed.', nl: 'Er is niets veranderd.' },
/* AuditPanel */
'audit.title': { en: 'Audit trail', nl: 'Audittrail' },
'audit.close': { en: 'Close audit trail', nl: 'Audittrail sluiten' },
'audit.row': {
en: (id: string) => `row ${id}`,
nl: (id: string) => `rij ${id}`,
},
'audit.showAll': { en: 'show all', nl: 'toon alles' },
'audit.nothingYet': { en: 'Nothing recorded yet.', nl: 'Nog niets vastgelegd.' },
'audit.older': { en: 'Older entries', nl: 'Oudere regels' },
'audit.viaAgent': {
en: (key: string) => `Through the agent key "${key}"`,
nl: (key: string) => `Via de agentsleutel "${key}"`,
},
/* EmptyWorkspace */
'empty.nothingTitle': { en: 'Nothing here yet', nl: 'Nog niets hier' },
'empty.viewerHint': {
en: 'This workspace has no tables. An admin has to make the first one — you can add rows once there is somewhere to put them.',
nl: 'Deze werkruimte heeft geen tabellen. Een beheerder moet de eerste aanmaken — je kunt rijen toevoegen zodra er ergens een plek voor is.',
},
'empty.bringTitle': {
en: 'Bring the spreadsheet you already have',
nl: 'Breng de spreadsheet mee die je al hebt',
},
'empty.bringHint': {
en: 'Drop in the Excel file, the OpenDocument one or a CSV, and it becomes a real table — you get to correct the sheet, the column names and the types it guessed before anything is written. It is the fastest way to find out whether your data holds up.',
nl: 'Zet het Excel-bestand, het OpenDocument-bestand of een CSV erin en het wordt een echte tabel — je kunt het werkblad, de kolomnamen en de geraden typen corrigeren voordat er iets wordt weggeschreven. Het is de snelste manier om te ontdekken of je gegevens deugen.',
},
'empty.defineTable': { en: 'Define a table myself', nl: 'Zelf een tabel definiëren' },
} as const satisfies MessageCatalog;