Skip to content

Internationalization

The grid ships English defaults and gives you an override point for every string it produces. There is no locale bundle to load and no i18n runtime — you supply strings from whatever i18n system your app already uses.

There are two override surfaces, split by what the strings are:

ConfigCoversShape
a11y.messagesLive-region announcements read by screen readersFunctions of runtime values
localeVisible plugin UI — buttons, labels, placeholdersFlat key → string map

Everything else (column headers, menu labels, cell values) is text you already supply, so you translate it before it reaches the grid — see Other localizable surfaces.

Live-region announcements — sort, filter, selection, grouping, editing — are the grid’s largest body of user-facing text. Override any subset via a11y.messages; anything you omit falls back to the English default.

grid.gridConfig = {
a11y: {
messages: {
sortApplied: (column, direction) => `Trié par ${column}, ${direction}`,
sortCleared: () => 'Tri effacé',
filterApplied: (column) => `Filtre appliqué sur ${column}`,
filterCleared: (column) => `Filtre effacé de ${column}`,
allFiltersCleared: () => 'Tous les filtres effacés',
groupExpanded: (name, count) => `Groupe ${name} développé, ${count} lignes`,
groupCollapsed: (name) => `Groupe ${name} réduit`,
selectionChanged: (count) => `${count} ligne(s) sélectionnée(s)`,
editingStarted: (rowIndex) => `Édition de la ligne ${rowIndex + 1}`,
editingCommitted: (rowIndex) => `Ligne ${rowIndex + 1} enregistrée`,
dataLoaded: (count) => `${count} lignes chargées`,
},
},
};

Every message is a function, so you can interpolate, pluralize, or delegate to Intl.PluralRules however your locale requires.

The buttons, labels and placeholders that plugins render into their own UI — the filter panel, the columns tool panel, the pivot builder — are localized through a single flat key/value map on gridConfig.locale.

grid.gridConfig = {
locale: {
'filter.search': 'Rechercher...',
'filter.apply': 'Appliquer',
'filter.clear': 'Effacer le filtre',
'columns.panelTitle': 'Colonnes',
'pivot.grandTotal': 'Total général',
},
};

locale is separate from a11y.messages on purpose: these are static labels, while announcements are functions of runtime values (row counts, column names, sort direction).

Keys are namespaced by the plugin that owns them. Only load-bearing UI text is keyed; anything you already supply (column headers, menu item labels, export filenames) stays under your control.

KeyEnglish defaultPlugin
filter.searchSearch...Filtering
filter.selectAllSelect AllFiltering
filter.noMatchesNo matching valuesFiltering
filter.applyApplyFiltering
filter.clearClear FilterFiltering
filter.clearAllClear All FiltersFiltering
filter.minMinFiltering (number)
filter.maxMaxFiltering (number)
filter.fromFromFiltering (date)
columns.panelTitleColumnsColumn Visibility
columns.hideColumnHide columnColumn Visibility
columns.showAllShow allColumn Visibility
columns.dragHandleDrag to reorderColumn Visibility
columns.dragGroupHandleDrag to reorder groupColumn Visibility
pinnedColumns.pinLeftPin LeftPinned Columns
pinnedColumns.pinRightPin RightPinned Columns
pinnedColumns.unpinUnpinPinned Columns
print.buttonTitlePrint gridPrint
pivot.panelTitlePivotPivot
pivot.optionsOptionsPivot
pivot.enableEnable pivotPivot
pivot.rowGroupsRow GroupsPivot
pivot.columnGroupsColumn GroupsPivot
pivot.valuesValuesPivot
pivot.availableFieldsAvailable FieldsPivot
pivot.filterFieldsFilter fields...Pivot
pivot.allFieldsUsedAll fields in usePivot
pivot.dropFieldsDrop fields herePivot
pivot.dropNumericFieldsDrop numeric fields herePivot
pivot.removeFieldRemove fieldPivot
pivot.removeValueFieldRemove value fieldPivot
pivot.aggFunctionAggregation functionPivot
pivot.customAggCustomPivot
pivot.showRowTotalsShow row totalsPivot
pivot.showGrandTotalShow grand totalPivot
pivot.grandTotalGrand TotalPivot

A custom filterPanelRenderer receives the same lookup on its params, so your own panel localizes through the app’s single map:

filterPanelRenderer: (container, params) => {
const apply = document.createElement('button');
apply.textContent = params.t('filter.apply', 'Apply');
container.appendChild(apply);
},

Inside a custom plugin, BaseGridPlugin exposes the same helper as this.t(key, fallback) (and this.translate when you need to hand the function to a render module).

SurfaceHow to localize
Column headerscolumns[].header — pass an already-translated string
Empty / loading stateA custom emptyRenderer / loadingRenderer returning translated markup
Cell values (dates, numbers, currency)columns[].format with Intl.NumberFormat / Intl.DateTimeFormat
Filter panel labelslocale keys (filter.*), or a custom filterPanelRenderer
Context menu itemsContext Menu plugin items[].label
Tool panel titlestitle on <tbw-grid-tool-panel> / the shell toolPanel config
Export filenames and sheet namesExport plugin config

Use Intl in a column format function rather than pre-formatting your data — this keeps sorting and filtering operating on the underlying value:

{
field: 'salary',
type: 'number',
format: (value) => new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
}).format(value as number),
}

Rows and headers are laid out with CSS Grid (a single grid-template-columns track list shared by every row), so column order follows the inline axis and mirrors automatically when the direction flips. Together with logical CSS properties, that means the grid inherits direction from its ancestors — set dir="rtl" on a container (or <html>) and it mirrors:

<div dir="rtl">
<tbw-grid id="grid"></tbw-grid>
</div>

Behaviour that CSS alone cannot mirror resolves the direction at runtime: arrow-key navigation swaps left/right, and pinned columns accept the logical 'start' / 'end' values so the same config pins correctly in both directions.

Custom renderers and custom themes are your responsibility — prefer logical properties (margin-inline-start, padding-inline, inset-inline-end) over physical ones so your additions mirror too.

AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://toolboxjs.com/llms-full.txt