Internationalisation
Every string the grid shows lives in one object, DataGridLabels, and every number and date goes
through Intl with a locale you choose. Pass a partial translation and anything you leave out
stays English.
| Yuki Iyer | Actif | 36,00 € | 35,5 % | 2 déc. 2024 | 9 mars 2026, 01:59 | |
| Sofia Kim | Actif | 319,00 € | 2 % | 21 juin 2025 | 26 juil. 2026, 15:31 | |
| Aarav Bose | Actif | 0,00 € | 52 % | 29 mars 2024 | 17 oct. 2025, 05:15 | |
| Meera Silva | Actif | 1 624,00 € | 51 % | 4 févr. 2024 | 26 déc. 2024, 13:52 | |
| Nia Reddy | Actif | 0,00 € | 96,9 % | 17 févr. 2023 | 27 août 2025, 19:54 | |
| Olivia Müller | Actif | 48,00 € | 4,8 % | 31 mars 2024 | 12 janv. 2025, 20:06 | |
| Noah Das | En pause | 12,00 € | 44,6 % | 6 juil. 2025 | 15 sept. 2026, 02:59 | |
| Ananya Khan | En pause | 0,00 € | 5,1 % | 10 déc. 2025 | 11 janv. 2026, 07:58 |
import { DataGrid, createDataGridColumns, useDataGrid, type DataGridLabels,} from "@/components/ui/data-grid";
import { makePeople, type Person } from "@/lib/data-grid-demo-data";
const col = createDataGridColumns<Person>();
const columns = col.columns([ col.text("name", { header: "Nom" }), col.enum("status", { header: "Statut", options: [ { value: "active", label: "Actif" }, { value: "invited", label: "Invité" }, { value: "paused", label: "En pause" }, { value: "churned", label: "Résilié" }, ], }), col.currency("mrr", { header: "MRR", currency: "EUR" }), col.percent("usage", { header: "Utilisation" }), col.boolean("active", { header: "Actif" }), col.date("joinedAt", { header: "Inscrit le" }), col.datetime("lastSeenAt", { header: "Vu le" }),]);
/** A partial translation: anything left out falls back to English. */const fr: Partial<DataGridLabels> = { search: "Rechercher…", clearFilters: "Effacer", filters: (n) => (n === 1 ? "1 filtre" : `${n} filtres`), view: "Affichage", viewColumns: "Colonnes", searchColumns: "Rechercher une colonne…", hidden: (n) => (n === 1 ? "1 masquée" : `${n} masquées`), showAll: "Tout afficher", resetView: "Réinitialiser", density: "Densité", densityCompact: "Compacte", densityComfortable: "Confortable", densitySpacious: "Aérée", export: "Exporter", exportCsv: "Télécharger en CSV", exportJson: "Télécharger en JSON", exportCopy: "Copier", exportSelectedOnly: "Lignes sélectionnées uniquement", group: "Grouper", groupBy: (c) => `Grouper par ${c}`, ungroup: "Dégrouper", groupedBy: "Groupé par", clearGrouping: "Supprimer le regroupement", fullscreen: "Plein écran", exitFullscreen: "Quitter le plein écran", sortAsc: "Tri croissant", sortDesc: "Tri décroissant", clearSort: "Annuler le tri", pinStart: "Épingler au début", pinEnd: "Épingler à la fin", unpin: "Désépingler", hideColumn: "Masquer la colonne", autosize: "Ajuster au contenu", filter: "Filtrer", filterPlaceholder: "Rechercher une valeur…", filterTrue: "Oui", filterFalse: "Non", rowsPerPage: "Lignes par page", pageOf: (page, pages) => `Page ${page} sur ${pages}`, previousPage: "Page précédente", nextPage: "Page suivante", rowsCount: (n) => (n === 1 ? "1 ligne" : `${n.toLocaleString("fr-FR")} lignes`), selected: (n) => (n === 1 ? "1 sélectionnée" : `${n} sélectionnées`), copied: (n) => (n === 1 ? "1 cellule copiée" : `${n} cellules copiées`), empty: "Aucun enregistrement.", emptyFiltered: "Aucune ligne ne correspond aux filtres.", yes: "Oui", no: "Non",};
const data = makePeople(40, 47);
/** French labels and `fr-FR` number and date formatting. */export default function DataGridI18n() { const grid = useDataGrid<Person>({ key: "i18n", data, columns, getRowId: (r) => r.id, labels: fr, locale: "fr-FR", selection: false, pagination: { pageSize: 8 }, }); return <DataGrid grid={grid} />;}Use it
Section titled “Use it”import { DataGrid, type DataGridLabels } from "@/components/ui/data-grid"
const fr: Partial<DataGridLabels> = { search: "Rechercher…", view: "Affichage", export: "Exporter", pageOf: (page, pages) => `Page ${page} sur ${pages}`, selected: (n) => (n === 1 ? "1 sélectionnée" : `${n} sélectionnées`),}
<DataGrid data={rows} columns={columns} labels={fr} locale="fr-FR" />Labels that carry a number are functions, so plural rules are yours. Column titles are not labels:
set header (or label) on each column in the language you want.
Locale
Section titled “Locale”locale is a BCP 47 tag for Intl.NumberFormat and Intl.DateTimeFormat. It defaults to the
browser’s locale, which is usually right. It affects:
| Kind | Formatting |
|---|---|
number |
grouping and decimal separators; maximumFractionDigits defaults to 2 |
currency |
style: "currency" with the column’s currency (default USD) |
percent |
style: "percent"; maximumFractionDigits defaults to 1 |
date |
dateStyle (default "medium") |
datetime |
dateStyle + timeStyle (defaults "medium" and "short") |
enum, tags |
not formatted by locale; the label comes from options |
The same strings appear in cells, facet filter lists, aggregated group rows, the clipboard and
exports, so they always agree. Per column, locale overrides the grid’s (a Japanese yen column in
an otherwise German grid), and format: (value, row) => string replaces the formatting entirely.
col.currency("total", { currency: "EUR" }) // the grid's localecol.currency("totalJpy", { currency: "JPY", locale: "ja-JP" }) // this column's owncol.date("due", { dateStyle: "full" })col.number("score", { format: (v) => `${v.toFixed(1)} pts` })Formatters are cached per locale and options, so changing locale at runtime is cheap.
Dates in editors
Section titled “Dates in editors”Date and datetime editors are the browser’s <input type="date"> and <input type="datetime-local">,
which display and parse in the browser’s locale, not the grid’s. The value they commit is a
Date either way.
Right-to-left
Section titled “Right-to-left”Set dir="rtl" on the document (or the grid’s ancestor) as usual. Pinning uses TanStack’s logical
start / end regions and CSS inset-inline-start / inset-inline-end, tree indentation uses
margin-inline-start, and alignment uses text-start / text-end, so a column pinned to the
start sits on the right in RTL. The “Pin to start” and “Pin to end” menu items read correctly in
both directions.
Every label
Section titled “Every label”The DataGridLabels keys with their English defaults. Functions receive the numbers they format.
Toolbar
Section titled “Toolbar”| Key | Default |
|---|---|
search |
Search… |
clearFilters |
Clear |
filters |
(n) => "1 filter" | "n filters" |
view |
View |
viewColumns |
Columns |
searchColumns |
Search columns… |
noColumns |
No columns found. |
hidden |
(n) => "n hidden" |
showAll |
Show all |
resetView |
Reset view |
density |
Density |
densityCompact / densityComfortable / densitySpacious |
Compact / Comfortable / Spacious |
export |
Export |
exportCsv / exportJson / exportCopy |
Download CSV / Download JSON / Copy to clipboard |
exportSelectedOnly |
Selected rows only |
group |
Group |
groupBy |
(column) => "Group by <column>" |
ungroup |
Ungroup |
groupedBy |
Grouped by |
clearGrouping |
Clear grouping |
savedViews |
Views |
saveView |
Save current view… |
saveViewPrompt |
Name this view |
deleteView |
Delete view |
defaultView |
Default view |
fullscreen / exitFullscreen |
Fullscreen / Exit fullscreen |
Column menu and filters
Section titled “Column menu and filters”| Key | Default |
|---|---|
sortAsc / sortDesc / clearSort |
Sort ascending / Sort descending / Clear sort |
pinStart / pinEnd / unpin |
Pin to start / Pin to end / Unpin |
hideColumn |
Hide column |
autosize / autosizeAll |
Fit to content / Fit all columns |
filter |
Filter |
filterPlaceholder |
Search values… |
filterNoResults |
No values found. |
filterApplied |
(n) => "n applied" |
filterContains / filterNotContains / filterStartsWith / filterEndsWith / filterEquals |
Contains / Does not contain / Starts with / Ends with / Equals |
filterIsEmpty / filterIsNotEmpty |
Is empty / Is not empty |
filterMin / filterMax |
Min / Max |
filterFrom / filterTo |
From / To |
filterAny / filterTrue / filterFalse |
Any / Yes / No |
applied |
applied |
dragColumn |
Drag to reorder column |
resizeColumn |
Resize column |
Pagination and selection
Section titled “Pagination and selection”| Key | Default |
|---|---|
rowsPerPage |
Rows per page |
pageOf |
(page, pages) => "Page 1 of 10" |
goToPage |
Go to page |
firstPage / previousPage / nextPage / lastPage |
Go to first page / Go to previous page / Go to next page / Go to last page |
selectedOf |
(selected, total) => "2 of 50 row(s) selected." |
rowsCount |
(total) => "1 row" | "1,250 rows" |
selectAll / selectRow |
Select all / Select row |
clearSelection |
Clear selection |
selected |
(n) => "1 selected" | "n selected" |
Rows, states and editing
Section titled “Rows, states and editing”| Key | Default |
|---|---|
expand / collapse / expandAll / collapseAll |
Expand / Collapse / Expand all / Collapse all |
pinRowTop / pinRowBottom / unpinRow |
Pin row to top / Pin row to bottom / Unpin row |
rowActions |
Row actions |
columnMenu |
Column menu |
moveLeft / moveRight |
Move left / Move right |
values |
(n) => "1 value" | "n values" (an aggregated unique cell with more than three) |
outOfRange |
(min, max) => "Must be between 1 and 100", or “at least” / “at most” with one bound |
loading |
Loading… |
empty |
No records found. |
emptyFiltered |
No rows match the current filters. |
error |
Something went wrong. |
retry |
Retry |
copied |
(n) => "Copied 1 cell" | "Copied n cells" |
edit |
Edit |
save / cancel |
Save / Cancel |
invalid |
Invalid value |
keyboardHint |
Use the arrow keys to move, Enter to edit, Space to select. |
yes / no |
Yes / No |
blank |
— |
defaultLabels and resolveLabels(partial) are exported, and the merged set is on grid.labels
for your own parts.
Options
Section titled “Options”| Option | Type | Default | What it does |
|---|---|---|---|
labels |
Partial<DataGridLabels> |
English | Merged over the defaults. |
locale |
string |
the browser’s | BCP 47 tag for number and date formatting. |
locale (column) |
string |
the grid’s | Overrides for one column. |
format (column) |
(value, row) => string |
by kind | Replaces the formatting for one column. |
currency (column) |
string |
"USD" |
ISO 4217 code for currency columns. |
dateStyle, timeStyle (column) |
Intl.DateTimeFormatOptions[...] |
"medium", "short" |
For date and datetime columns. |
minimumFractionDigits, maximumFractionDigits (column) |
number |
by kind | For number-like columns. |
See also
Section titled “See also”- Columns: every formatting option.
- Accessibility: which labels screen readers hear.
- Export: formatted and raw values.