Languages & RTL
Translate the texts the components bring with them, set the locale of the date components and run any skin right to left.
// you'll learn
- How
MessagesProvidertranslates built-in texts, and how to change a few of them - Every message key, in English and Portuguese
- Which locale the date components use, and how to set it
- How
DirectionProviderturns a page right to left in every skin
Built-in texts
Most text in a Camaleo interface is yours: labels, titles, button text. A few components also bring texts of their own, like a close button's name, an empty table's message or "Page 3" for a screen reader. Those are English by default.
Put one MessagesProvider near the root to change them. Pick a language below: only the component texts change.
import { MessagesProvider } from "@camaleo/react";
<MessagesProvider locale="pt-BR">{children}</MessagesProvider>
locale picks a bundled set: "en" (the default) or "pt-BR". A prop on the component always wins over the provider, so <Pagination previousLabel="Back" /> keeps "Back".
Change a few texts
messages takes any subset of the keys. Each group you pass is merged over the set below it, so you only write what changes:
<MessagesProvider
locale="pt-BR"
messages={{ drawer: { close: "Fechar painel" }, dataTable: { empty: "Nenhum pedido ainda." } }}
>
{children}
</MessagesProvider>
Providers nest. An inner provider without locale starts from its parent's texts, which is handy for one area with its own wording:
<MessagesProvider messages={{ dataTable: { empty: "No orders match these filters." } }}>
{children}
</MessagesProvider>
For a language Camaleo doesn't bundle, write a full set typed as Messages and pass it as messages. The type makes sure no key is missing.
Server or client
Some messages are functions, like pagination.page(3) → "Page 3". React can't pass functions from a Server Component to a client one, so:
- From a Server Component (a root layout), pass
localeand string-onlymessages. - To override a function, put the provider in a client component:
"use client";
import { MessagesProvider } from "@camaleo/react";
export function AppMessages({ children }: { children: React.ReactNode }) {
return (
<MessagesProvider
locale="pt-BR"
messages={{ pagination: { page: (page) => `Ir para a página ${page}` } }}
>
{children}
</MessagesProvider>
);
}
Read the texts in your own components with useMessages() (client) or <Message pick="spinner.label" /> (server). See Server Components and SSR.
Every message key
This is the Messages interface from packages/react/src/messages.tsx, with the bundled en and ptBR values. Function keys show the result of a sample call.
| Key | English | Português |
|---|---|---|
breadcrumb.label | Breadcrumb | Trilha de navegação |
breadcrumb.more | More pages | Mais páginas |
carousel.label | Carousel | Carrossel |
carousel.previous | Previous slide | Slide anterior |
carousel.next | Next slide | Próximo slide |
carousel.play | Play | Reproduzir |
carousel.pause | Pause | Pausar |
carousel.indicators | Choose slide | Escolher slide |
carousel.goToSlide(2) | Go to slide 2 | Ir para o slide 2 |
codeBlock.copy | Copy | Copiar |
codeBlock.copied | Copied | Copiado |
codeBlock.label | Code | Código |
combobox.clear | Clear | Limpar |
combobox.showOptions | Show options | Mostrar opções |
commandPalette.placeholder | Type a command or search… | Digite um comando ou pesquise… |
commandPalette.empty | No matching commands found. | Nenhum comando encontrado. |
dataTable.caption | Data table | Tabela de dados |
dataTable.empty | No records found. | Nenhum registro encontrado. |
dataTable.selectAll | Select all rows | Selecionar todas as linhas |
dataTable.selectRow(0) | Select row 1 | Selecionar linha 1 |
dataTable.pagination | Table pagination | Paginação da tabela |
drawer.close | Close | Fechar |
fileUpload.choose | Choose files | Escolher arquivos |
image.failed | Failed to load image | Não foi possível carregar a imagem |
image.unavailable | Unavailable image | Imagem indisponível |
inputGroup.showPassword | Show password | Mostrar senha |
inputGroup.hidePassword | Hide password | Ocultar senha |
inputGroup.clear | Clear search | Limpar busca |
numberField.decrease | Decrease | Diminuir |
numberField.increase | Increase | Aumentar |
otpField.cell(0, 6) | Character 1 of 6 | Caractere 1 de 6 |
pagination.label | Pagination | Paginação |
pagination.previous | Previous | Anterior |
pagination.next | Next | Próxima |
pagination.page(3) | Page 3 | Página 3 |
progressCircle.label | Progress | Progresso |
sidebar.collapse | Collapse sidebar | Recolher menu lateral |
sidebar.expand | Expand sidebar | Expandir menu lateral |
skipLink.label | Skip to content | Pular para o conteúdo |
spinner.label | Loading | Carregando |
splitter.handle | Resize panel | Redimensionar painel |
table.scroll | Table | Tabela |
tagsInput.remove("design") | Remove design | Remover design |
toast.close | Close | Fechar |
toggletip.close | Close tip | Fechar dica |
One text comes from a primitive, not from Camaleo: an indeterminate Progress is announced as "indeterminate progress" by Base UI, in English, whatever the provider says.
Dates and times
MessagesProvider doesn't touch dates. The date components (Calendar, RangeCalendar, DatePicker, DateRangePicker, DateField, TimeField) take a locale prop that sets the segment order, the month and weekday names, the first day of the week and the clock. The same date in three locales:
Without locale, they use the nearest I18nProvider (re-exported by Camaleo), then the browser's language. Set it on the server, so the server and the browser format the same way:
import { I18nProvider, MessagesProvider } from "@camaleo/react";
<MessagesProvider locale="pt-BR">
<I18nProvider locale="pt-BR">{children}</I18nProvider>
</MessagesProvider>
Right to left
The skins and the component CSS are written with logical properties (margin-inline-start, inset-inline-end, border-start-start-radius…), so the same CSS works in both directions. To turn a page or a section right to left, do three things together:
dir="rtl"on the element, for the browser's layout.langon the same element, so screen readers pick the right voice.<DirectionProvider direction="rtl">around it, so the components flip their keyboard (arrows in tabs, sliders, menus) and their popups.
Switch the direction below, then pick other skins in the "preview in" bar: tabs, slider, switch and buttons all mirror, in every skin.
الإعدادات
import { DirectionProvider } from "@camaleo/react";
const dir = lang === "ar" ? "rtl" : "ltr";
<html lang={lang} dir={dir}>
<body>
<DirectionProvider direction={dir}>{children}</DirectionProvider>
</body>
</html>
A few app-structure rules still use physical sides and don't mirror yet: the Sidebar edge, its active-item bar and counts, shortcut hints in Menubar and CommandPalette, the line of a vertical Stepper and the SkipLink position. Check those parts by eye if you ship a right-to-left app.
Popups opened inside an RTL section stay RTL: the portal wrapper copies the direction. Your own icons that mean "forward" or "back" (arrows, chevrons) need flipping in RTL; icons that show a thing (a bell, a trash can) don't.
Before you ship it
- One
MessagesProviderat the root, with the language of the page - Function messages are overridden from a client component
- Date components get a
locale, or sit inside anI18nProvider dir,langandDirectionProviderchange together- Arrows and chevrons in your own content flip in RTL