Customization

Localization

Twelve languages ship with the package. Hand the grid the ones your app offers and it picks between them; the same tag drives Intl, so numbers and dates follow the language rather than drifting from it.

A language is chosen per grid, through locales and locale on createDataGrid. Nothing is set globally, so two grids on one page may speak different languages and neither has to be put back afterwards.

Switching Language

Change the language below and watch three things move at once: the toolbar and footer strings, the currency format, and the date format. The sort, the filter and the page stay where they were, because the language is state on the grid rather than a remount.

Hoang Kowalski
Design
€127,691.00
Mar 6, 2024
Bruno Nguyen
Growth
€105,146.00
Mar 6, 2024
Bruno Dubois
Design
€86,538.00
Feb 5, 2021
Farid Haddad
Growth
€76,443.00
May 4, 2022
Jonas Yilmaz
Core
€129,812.00
Nov 13, 2024
24 rows
1-5 of 24
// locale forces a tag rather than following the page
createDataGrid<Person>({ columns, data, getRowId, locales, locale: 'vi-VN' });

// Assigning it switches in place, keeping the sort, filter and selection
grid.locale = 'ja-JP';

// The same tag drives Intl, so number, currency and date columns that name
// no locale of their own reformat with it.
{ id: 'salary', type: 'currency', typeOptions: { currency: 'EUR' } }

// A tag nobody answers for falls back to English, and 'vi' is answered by
// the 'vi-VN' pack.

Loading Packs

There is no "all languages" export on purpose: a pack nobody imports is a pack nobody ships. The grid below ships one pack, viVN, and the three tags show the whole rule. The exact tag matches. A bare vi is answered by the vi-VN pack. A tag no pack answers for falls back to English, which is built in and needs no import.

Hoang Kowalski
Design
€127,691.00
Mar 6, 2024
Bruno Nguyen
Growth
€105,146.00
Mar 6, 2024
Bruno Dubois
Design
€86,538.00
Feb 5, 2021
Farid Haddad
Growth
€76,443.00
May 4, 2022
Jonas Yilmaz
Core
€129,812.00
Nov 13, 2024
5 rows
1-5 of 5
import { createDataGrid } from '@sv5ui/datagrid';
import { enUS, jaJP, viVN } from '@sv5ui/datagrid/locales';

// Hand the grid the languages it may use. It picks one from the page's own
// language, so nothing else needs configuring. A pack nobody imports is
// never bundled, which is why there is no "all languages" export.
const grid = createDataGrid<Person>({
  columns,
  data,
  getRowId,
  locales: [enUS, viVN, jaJP]
});

The Twelve Packs

From @sv5ui/datagrid/locales, named after their tag: enUS, viVN, zhCN, jaJP, koKR, frFR, deDE, esES, ptBR, ruRU, idID, thTH.

TagLanguage
en-USEnglish
vi-VNVietnamese
zh-CNChinese, simplified
ja-JPJapanese
ko-KRKorean
fr-FRFrench
de-DEGerman
es-ESSpanish
pt-BRPortuguese, Brazil
ru-RURussian
id-IDIndonesian
th-THThai

Overriding Single Strings

labels replaces individual strings on top of the chosen pack. The grid below is English with four of them changed: the search placeholder, the empty text, the selection count and the page-size label.

Hoang Kowalski
Design
€127,691.00
Bruno Nguyen
Growth
€105,146.00
Bruno Dubois
Design
€86,538.00
5 rows
1-3 of 5
// labels replaces single strings on top of the chosen pack, any subset
createDataGrid<Person>({
  columns,
  data,
  getRowId,
  locales: [enUS],
  labels: {
    search: 'Find a member',
    noData: 'Nobody here yet',
    // Functions where a value is interpolated, so a language can put the
    // number where it needs it.
    selectedRows: (count) => `${count} chosen`,
    pageRange: (from, to, total) => `${from} to ${to} of ${total}`,
    // Operator maps override one entry at a time
    textOps: { contains: 'has' }
  },
  // The same for what the live region speaks
  announcer: {
    sorted: (column, direction) => `${column}, ${direction}ending`
  }
});

What the Labels Cover

All 255 strings the grid renders, grouped by where they appear. A label is a plain string, or a function where a value is interpolated. A feature you do not register never asks for its labels, so the count is what the table can hold, not what any one grid uses.

AreaKeys
Toolbarsearch, activeFilters, removeFilter, clearAllFilters, chooseColumns, rowDensity, densityCompact, densityStandard, densityComfortable
Header and column menucolumnMenu, resizeColumn, resizeGroup, sortAscending, sortDescending, clearSort, pinLeft, pinRight, unpin, openFilter, autosize, hideColumn, collapseGroup, expandGroup
Filter panel and filter rowfilterColumn, filterOperator, filterValue, filterRowValue, filterUpperBound, valuePlaceholder, upperBoundPlaceholder, searchValues, blankValue, anyValue, combineConditions, addCondition, removeCondition, matchCase, apply, clear, and, or, yes, no, and the three operator maps textOps, numberOps and dateOps
RowsselectRow, selectAllRows, rowActions, dragRow, expandRow, collapseRow
Footer and statusrowsPerPage, pageSizeOption, pageRange, totalRows, filteredRows, selectedRows, noData, retry. Under a pivot the count reads crossedRows, and crossedFilteredRows while a filter is on: both are functions, taking the number of gathered rows and the number of rows they were measured from
Menuscopy, copyWithHeaders, exportCsv, exportAllRows, exportLoadedRows, exportSelectedRows, clearSelection
GroupingThe group panel, the column menu entries that group and ungroup, and the rows grouping draws: groupBy, groupPanelEmpty, addGroup, clearGroups, moveGroupEarlier, moveGroupLater, removeGroup, groupByColumn, ungroupColumn, groupFooter, grandTotal, and the three a server row model uses while a count is still partial, groupLoaded, groupFooterLoaded and grandTotalLoaded
PivotThe three column menu entries that start and stop a crossing: pivotByRow, pivotByColumn, removeFromPivot. The four zones of Grid.PivotPanel and their hints: pivotRows, pivotColumns, pivotValues, pivotSettings, pivotRowsHint, pivotColumnsHint, pivotValuesHint, pivotSettingsHint, pivotPanelEmpty. The chips and their controls: addPivotRow, addPivotColumn, addPivotValue, clearPivotAll, pivotAggregation, pivotShare, pivotShareNone, and movePivotEarlier, movePivotLater and removePivotChip, which are functions taking the column header. The settings: pivotTotalColumn, pivotTotalRow, pivotSubtotal, pivotFoldable, pivotSortable, pivotValuesOn, pivotValuesDown, pivotExpandAll, pivotCollapseAll, pivotKeep, pivotKeepAll, pivotKeepOthers, and pivotKeepTop and pivotKeepBottom, which are functions taking the count. pivotSubtotalOf is a function taking a group header, and it also names the subtotal columns a saved view brings back. pivotTotalColumn, pivotTotalRow and pivotKeepOthers double as the headers the panel gives a total or a gathered row it switches on. Two are maps rather than strings, overridden one entry at a time the way textOps is: pivotAggs names the nine reductions a measure chip offers, and pivotShares the seven readings
Tool panelIts title, its three tabs and everything inside them: toolPanelTitle, toolPanelColumns, toolPanelGroup, toolPanelValues, toolPanelSearch, toolPanelShowAll, toolPanelHideAll, toolPanelEmpty, toolPanelMoveUp, toolPanelMoveDown, toolPanelNoAggregation, toolPanelActions, toolPanelCollapse, toolPanelExpand
Cell rangesWhat the range status bar reads out: rangeCells, rangeShape, rangeSum, rangeAvg, rangeMin, rangeMax
Filter builderThe nested condition tree: filterTitle, filterEmpty, filterAddCondition, filterAddGroup, filterRemove, filterJoin, filterNot, filterBuilderColumn, filterBuilderOperator, filterBuilderValue, filterValueTo, filterTrue, filterFalse, filterOp, and filterServerHandled for a condition the server answers
Find and replaceThe bar, its controls and everything it reports: findTitle, findPlaceholder, replacePlaceholder, findCaseSensitive, findWholeCell, findPrevious, findNext, findClose, findReplaceOne, findReplaceAll, findCount, findNoMatch, findReplaced, findNoWritable for matches that cannot be written, and findCountLoaded, findNoMatchLoaded and findServerReadOnly under a server row model
Saved viewsThe views bar and sharing: viewsLabel, viewsEmpty, viewsPick, viewNamePlaceholder, viewSave, viewUpdate, viewRevert, viewRemove, viewShare, viewShareCopied, viewModified, and viewShareTooLong for a state that will not fit in a link
Conditional formattingThe rules bar and the five rule kinds: formatLabel, formatEmpty, formatAdd, formatClear, formatColumn, formatKind, formatKindColorScale, formatKindDataBar, formatKindDuplicates, formatKindTopN, formatKindExpression, formatTopCount, formatExpressionPlaceholder, formatWholeRow, formatRuleName, formatRemoveRule, formatBadExpression
Data importThe wizard, from the drop zone through mapping to review: importTitle, importDropHere, importChooseFile, importPaste, importEmpty, importUnknownFormat, importHeaderRow, importSheet, importColumnFrom, importSkipColumn, importMatchTitle, importReviewTitle, importBack, importNext, importCancel, importAddRows, importAddValid, importIssues, importReady, importMatched, importNothingMatched, importRowCount, importTooBig, importServerRefusal, and the six that word a bad cell: importNotNumber, importNotDate, importNotBoolean, importInvalid, importDuplicate, importExisting
Command paletteThe palette and every command the built-in features register: commandPaletteTitle, commandPalettePlaceholder, commandPaletteEmpty, commandGroupDefault, commandShowColumn, commandHideColumn, commandUnpinColumn, commandPinColumnLeft, commandPinColumnRight, commandGroupByColumn, commandUngroupColumn, commandExpandAllGroups, commandCollapseAllGroups, commandClearGrouping, commandCopyRange, commandFillDown, commandFillRight, commandFind, commandSaveView, and the three group headings commandGroupColumns, commandGroupGrouping and commandGroupClipboard

Writing a Pack

A pack says what it has. English answers for the rest, so a pack of five strings works, and one written against this version keeps working when the next names a string it has never heard of: that string arrives in English rather than as a blank or as a build that will not run. Every key it does say is typed, so a translation cannot land on a name the grid does not have.

Which makes spreading enUS.labels into a pack unnecessary, and worse than unnecessary: it freezes today's English into the pack, so a string added later arrives translated to whatever English said when the pack was written.

The second option below is a pack written in this file, four labels deep. Switch to it and watch the search box, the footer's page-size label and the column chooser change while everything left untranslated stays English.

Hoang Kowalski
Design
€127,691.00
Mar 6, 2024
Bruno Nguyen
Growth
€105,146.00
Mar 6, 2024
Bruno Dubois
Design
€86,538.00
Feb 5, 2021
3 rows
1-3 of 3
import type { DataGridLocalePack } from '@sv5ui/datagrid';

// A pack is { tag, labels, announcer } and says what it has. English
// answers for the rest, so two labels is a working pack, and a string the
// grid adds next release arrives in English rather than as a blank.
export const svSE: DataGridLocalePack = {
  tag: 'sv-SE',
  labels: { search: 'Sok...', noData: 'Inga rader' },
  announcer: { sortCleared: () => 'Sortering rensad' }
};

// Every key it does say is typed, so a translation cannot land on a name
// the grid does not have, and a label function keeps its signature.

createDataGrid<Person>({ columns, data, getRowId, locales: [svSE] });

// Layered in this order, so the narrowest wins:
//   English -> the pack for grid.locale -> this app's own labels/announcer
createDataGrid<Person>({
  columns, data, getRowId,
  locales: [svSE],
  labels: { search: 'Sok medlemmar...' }   // over the pack, over English
});

Options

OptionDefault
locales[]
localethe page language
labels-
announcer-