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:
| Config | Covers | Shape |
|---|---|---|
a11y.messages | Live-region announcements read by screen readers | Functions of runtime values |
locale | Visible plugin UI — buttons, labels, placeholders | Flat 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.
Screen reader announcements
Section titled “Screen reader announcements”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.
Built-in UI strings (locale)
Section titled “Built-in UI strings (locale)”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).
Key reference
Section titled “Key reference”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.
| Key | English default | Plugin |
|---|---|---|
filter.search | Search... | Filtering |
filter.selectAll | Select All | Filtering |
filter.noMatches | No matching values | Filtering |
filter.apply | Apply | Filtering |
filter.clear | Clear Filter | Filtering |
filter.clearAll | Clear All Filters | Filtering |
filter.min | Min | Filtering (number) |
filter.max | Max | Filtering (number) |
filter.from | From | Filtering (date) |
columns.panelTitle | Columns | Column Visibility |
columns.hideColumn | Hide column | Column Visibility |
columns.showAll | Show all | Column Visibility |
columns.dragHandle | Drag to reorder | Column Visibility |
columns.dragGroupHandle | Drag to reorder group | Column Visibility |
pinnedColumns.pinLeft | Pin Left | Pinned Columns |
pinnedColumns.pinRight | Pin Right | Pinned Columns |
pinnedColumns.unpin | Unpin | Pinned Columns |
print.buttonTitle | Print grid | |
pivot.panelTitle | Pivot | Pivot |
pivot.options | Options | Pivot |
pivot.enable | Enable pivot | Pivot |
pivot.rowGroups | Row Groups | Pivot |
pivot.columnGroups | Column Groups | Pivot |
pivot.values | Values | Pivot |
pivot.availableFields | Available Fields | Pivot |
pivot.filterFields | Filter fields... | Pivot |
pivot.allFieldsUsed | All fields in use | Pivot |
pivot.dropFields | Drop fields here | Pivot |
pivot.dropNumericFields | Drop numeric fields here | Pivot |
pivot.removeField | Remove field | Pivot |
pivot.removeValueField | Remove value field | Pivot |
pivot.aggFunction | Aggregation function | Pivot |
pivot.customAgg | Custom | Pivot |
pivot.showRowTotals | Show row totals | Pivot |
pivot.showGrandTotal | Show grand total | Pivot |
pivot.grandTotal | Grand Total | Pivot |
In custom renderers and plugins
Section titled “In custom renderers and plugins”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).
Other localizable surfaces
Section titled “Other localizable surfaces”| Surface | How to localize |
|---|---|
| Column headers | columns[].header — pass an already-translated string |
| Empty / loading state | A custom emptyRenderer / loadingRenderer returning translated markup |
| Cell values (dates, numbers, currency) | columns[].format with Intl.NumberFormat / Intl.DateTimeFormat |
| Filter panel labels | locale keys (filter.*), or a custom filterPanelRenderer |
| Context menu items | Context Menu plugin items[].label |
| Tool panel titles | title on <tbw-grid-tool-panel> / the shell toolPanel config |
| Export filenames and sheet names | Export plugin config |
Formatting values
Section titled “Formatting values”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),}Right-to-left
Section titled “Right-to-left”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.