# Localization & RTL

> The messages object, shipped en and tr, replacing every string, locale for formatting, and dir="rtl" mirroring.

Page: https://svelte-lean.vercel.app/table/localization-rtl · Markdown: https://svelte-lean.vercel.app/table/localization-rtl.md · Index: https://svelte-lean.vercel.app/llms.txt

Every visible or accessible string of the renderer comes from one messages object, two sets ship, and the locale drives collation and folding. Direction is read from the document unless a prop says otherwise.

- **Source**: messages.tsdata-table.svelte (isRtl)core.ts (fold, collator)
- **Tests**: data-table.dom.test.tstable.test.tstable.spec.ts (browser)
- **Fixture**: fixtures/table-basic
- **Benchmark**: table.bench.spec.ts

The renderer contains no literal strings. Labels of the search input, the row and header checkboxes, the pager buttons, the editor, the column controls and the row grips, the status lines and the empty text are all keys of `TableMessages`; three of them are functions because they take a count or a column name. `en` is the default and `tr` ships alongside; a partial `messages` object overrides any subset.

`locale` is passed to the `Intl.Collator` that sorts text and to the case folding used by search and filters. Folding also normalizes accents and the Turkish dotted and dotless i, so a search in one form finds the other. Number and date formatting are not part of the table: a column's `format` function does it with whatever formatter the application uses.

`a third message set`

```ts
import { en, type TableMessages } from '@svelte-lean/table';

// Every key is required in a full set; spread a shipped set to override some.
const de: TableMessages = {
	...en,
	search: 'Zeilen durchsuchen',
	empty: 'Keine passenden Zeilen',
	page: (page, pages, rows) => `Seite ${page} von ${pages} · ${rows} Zeilen`,
	selected: (count) => `${count} ausgewählt`
};
```

## Messages

Replace every built-in message and switch layout direction independently.

Messages.svelte

```svelte
<script lang="ts">
	import { DataTable, en, tr, type TableMessages } from '@svelte-lean/table';
	import '@svelte-lean/styles/radio-group.css';
	import { memberColumns, pick } from '$lib/demo/columns';
	import { people } from '$lib/demo/data';

	const data = people(12);
	const columns = pick(memberColumns(), ['name', 'team', 'region', 'revenue']);
	let language = $state<'en' | 'tr'>('tr');
	// A partial override on top of a shipped set: replace one string, keep the rest.
	const messages = $derived<Partial<TableMessages>>(
		language === 'tr' ? tr : { ...en, empty: 'Nobody matches this search' }
	);
</script>

<fieldset data-slean="radio-group">
	<legend>Messages</legend>
	<label><input type="radio" name="language" value="en" bind:group={language} /> en</label>
	<label><input type="radio" name="language" value="tr" bind:group={language} /> tr</label>
</fieldset>

<DataTable
	{data}
	{columns}
	getRowId={(row) => row.id}
	{messages}
	locale={language === 'tr' ? 'tr-TR' : 'en-US'}
	searchable
	selection="multiple"
	state={{ pageSize: 5 }}
	label="Members: localization"
/>
```

Switch between the shipped tr set with a Turkish locale and en with one key overridden: search for something that matches nothing to see the custom empty text. The pager, the selection labels and the status line follow the set; the row data is the same.

## Right-to-left

Mirror pinned columns, navigation and resize behavior with one direction prop.

Rtl.svelte

```svelte
<script lang="ts">
	import { DataTable } from '@svelte-lean/table';
	import { memberColumns } from '$lib/demo/columns';
	import { people } from '$lib/demo/data';

	const data = people(12);
	const columns = memberColumns().map((column) =>
		column.id === 'name' ? { ...column, pin: 'left' as const } : { ...column, width: 200 }
	);
</script>

<!-- Without a dir prop the table follows the nearest dir attribute; with one it overrides it. -->
<div dir="rtl">
	<DataTable {data} {columns} getRowId={(row) => row.id} height="16rem" pagination={false} label="Üyeler" />
</div>

<DataTable dir="rtl" {data} {columns} getRowId={(row) => row.id} label="Üyeler" />
```

This table has no dir prop: toggle the RTL checkbox and it follows the frame's dir attribute, read at interaction time. The pinned Member column moves to the trailing edge through logical insets, the arrow keys are mirrored, and the resize handle drags in the other direction. A dir prop on the table overrides the document.

- Without a prop the root carries no `dir` attribute at all; `dir="auto"` above it is resolved through `:dir(rtl)` where supported.
- Text alignment uses logical values (`start`, `end`), so numbers stay at the end in both directions.
- Pinned columns are placed with `inset-inline-start` and `inset-inline-end`.

## API

| Name | Of | Type | Default | Description |
| --- | --- | --- | --- | --- |
| [`messages`](https://svelte-lean.vercel.app/table/api.md#prop-messages) | Presentation | `Partial<TableMessages>` | `en` | Overrides of the visible and accessible strings; missing keys fall back to en. |
| [`locale`](https://svelte-lean.vercel.app/table/api.md#prop-locale) | Presentation | `string` | `undefined` | Locale of the collator used for sorting and of case folding in search and filters. |
| [`dir`](https://svelte-lean.vercel.app/table/api.md#prop-dir) | Presentation | `'ltr' \| 'rtl'` | `undefined` | Text direction of the table. Without it the root carries no dir attribute and keyboard, resize and pinning follow the nearest dir attribute in the document. |
| [`en`](https://svelte-lean.vercel.app/table/api.md#export-en) | Root entry | `TableMessages` | – | English messages, the default of the renderer. |
| [`tr`](https://svelte-lean.vercel.app/table/api.md#export-tr) | Root entry | `TableMessages` | – | Turkish messages. |
| [`filter`](https://svelte-lean.vercel.app/table/api.md#message-filter) | TableMessages | `(column: string) => string` | `` (column) => `Filter ${column}` `` | Label of a column filter input. |
| [`page`](https://svelte-lean.vercel.app/table/api.md#message-page) | TableMessages | `(page: number, pages: number, rows: number \| undefined) => string` | ``(page, pages, rows) => `Page ${page} of ${pages}` + (rows === undefined ? '' : ` · ${rows} rows`)`` | The pager status line; rows is undefined when a manual table has no rowCount. |
| [`selected`](https://svelte-lean.vercel.app/table/api.md#message-selected) | TableMessages | `(count: number) => string` | `` (count) => `${count} selected` `` | The live selection count in the toolbar. |
| [`fold`](https://svelte-lean.vercel.app/table/api.md#core-fold) | Core entry | `(value, locale?) => string` | – | Case and accent folding used by search and text filters (Turkish dotted and dotless i included). |

## Related

- [Messages reference](https://svelte-lean.vercel.app/table/api.md#messages)
- [Columns](https://svelte-lean.vercel.app/table/columns.md)
- [Accessibility](https://svelte-lean.vercel.app/table/accessibility.md)
