Skip to content
UI

Locale Provider

beta

Gives the library the reader's locale, time zone and texts once per app, already translated.

@zenoxai/ui/components/locale-provider Markdown
AltaNão se aplica
function LocaleProviderDemo() {
  const [locale, setLocale] = React.useState("pt-BR")
  // In an app these come from its dictionary, once, kept stable.
  const labels = React.useMemo<LocaleLabels>(
    () =>
      locale === "pt-BR"
        ? {
            pagination: {
              previous: "Anterior",
              next: "Próxima",
              goToPrevious: "Ir para a página anterior",
              goToNext: "Ir para a próxima página",
              morePages: "Mais páginas",
            },
            numberInput: { decrease: "Diminuir", increase: "Aumentar" },
            severityBadge: {
              critical: "Crítica",
              high: "Alta",
              medium: "Média",
              low: "Baixa",
              unknown: "Desconhecida",
            },
            emptyValue: {
              unknown: "Sem valor",
              notApplicable: "Não se aplica",
              redacted: "Oculto",
            },
          }
        : {},
    [locale]
  )

  return (
    <div className="flex w-full max-w-md flex-col gap-6">
      <ToggleGroup
        variant="segmented"
        size="sm"
        value={[locale]}
        onValueChange={(value) => {
          if (value[0]) setLocale(value[0])
        }}
        aria-label="Locale"
      >
        <ToggleGroupItem value="en-US">en-US</ToggleGroupItem>
        <ToggleGroupItem value="pt-BR">pt-BR</ToggleGroupItem>
      </ToggleGroup>
      <LocaleProvider locale={locale} labels={labels}>
        <div className="flex flex-wrap items-center gap-4">
          <SeverityBadge level="high" />
          <EmptyValue reason="not-applicable" />
          <NumberInput defaultValue={12040} ariaLabel="Events per day" />
        </div>
        <Pagination>
          <PaginationContent>
            <PaginationItem>
              <PaginationPrevious href="#" />
            </PaginationItem>
            <PaginationItem>
              <PaginationLink href="#" isActive>
                1
              </PaginationLink>
            </PaginationItem>
            <PaginationItem>
              <PaginationEllipsis />
            </PaginationItem>
            <PaginationItem>
              <PaginationNext href="#" />
            </PaginationItem>
          </PaginationContent>
        </Pagination>
      </LocaleProvider>
    </div>
  )
}

Usage

import { LocaleProvider } from "@zenoxai/ui/components/locale-provider"
import { ptBR } from "react-day-picker/locale"
<LocaleProvider
  locale="pt-BR"
  timeZone="America/Sao_Paulo"
  labels={labels}
  dateLocale={ptBR}
>
  <App />
  <Toaster />
  <ConfirmDialogHost />
</LocaleProvider>

Mount one at the root of the app, around the Toaster and the confirmation host too. It sets:

  • locale, a BCP 47 tag: the locale of every number the library formats, and the default of Number Input, Progress, Slider and the chart tooltip.
  • timeZone, an IANA name, for the app’s own dates through useLocale().
  • labels, the library’s own texts grouped by part, already translated. The app passes the results of its own t(); the library ships no translations.
  • dateLocale, the locale of the Calendar, from react-day-picker/locale, which also translates the calendar’s navigation.

Keep labels stable: build it once, in a module constant or a useMemo on the language. The groups, their keys and how they fall back are on the Localization page.

Without a provider every part shows its English default, as before.

Examples

Reading the locale in the app

useLocale() returns the provider’s locale and time zone, and the functions of lib/format bound to them, for the app’s own text.

const { format } = useLocale()

format.number(1204)                                   // "1.204"
format.dateTime(alert.detectedAt, { seconds: true })  // "27/10/2026 15:05:09"

Outside a provider it returns en-US and the runtime’s time zone, and never throws.

A different text in one part of the page

A provider inside another merges its labels into the parent’s, one group deep, and keeps the parent’s locale. An instance’s own prop still wins over both.

<LocaleProvider labels={{ dataTable: { empty: "Nenhuma regra" } }}>
  <DataTable table={rules} label="Regras" />
</LocaleProvider>

Accessibility

  • Set lang on <html> to the same language, so screen readers choose the right voice. The provider renders no element.
  • The accessible names the library writes on its own (Close, Loading, Previous page) come from labels. In development, a part that falls back to English under another locale logs a warning that names the missing key.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

LocaleContext

No props of its own. Accepts the props of the element it renders.

DEFAULT_LOCALE

The locale of the library's own texts without a provider.

No props of its own. Accepts the props of the element it renders.

createLocaleFormatters

The formatters of a locale and a time zone.

No props of its own. Accepts the props of the element it renders.

useLocaleFormatters

The formatters for the nearest provider, or en-US without one.

PropTypeDefault
dateLocale*DayPickerLocale | undefined-
labels*LocaleLabels-
locale*string | undefined

The provider's locale, or undefined when only labels were given.

-
timeZone*string | undefined-

LocaleProvider

LocaleProvider gives the library the reader's locale, time zone and texts, once per app, instead of passing them to every instance. Text still arrives already translated: the app passes the results of its own `t()`, and the library ships no translations.

PropTypeDefault
dateLocaleDayPickerLocale

The date-fns locale of the Calendar and of FilterDate's calendar. Pass the one from `react-day-picker/locale`, which also translates the calendar's navigation ("Ir para o próximo mês").

-
labelsLocaleLabels

The library's texts, already translated, by part. Keep the object stable (a module constant or a useMemo): a new object renders every part again.

-
localestring

The BCP 47 tag of the reader's language, such as `pt-BR`: the locale of every number and date the library formats, and the default `locale` of NumberInput, Progress, Slider, FilterNumberRange and Chart's tooltip. A nested provider that only changes labels can leave it out.

-
timeZonestring

The IANA name of the reader's time zone, such as `America/Sao_Paulo`. The library formats no zoned date of its own: useLocale hands it to the app, and useFilterDateToday reads it. Defaults to the runtime's zone.

-

useLocale

useLocale returns the nearest LocaleProvider's locale and time zone, and the formatters bound to them, for an app's own text: `format.number(1204)`, `format.dateTime(alert.detectedAt, { seconds: true })`. Outside a provider it returns en-US and the runtime's time zone, and never throws.

No props of its own. Accepts the props of the element it renders.

useLocaleContext

The provider's locale, or undefined outside one; internal.

No props of its own. Accepts the props of the element it renders.

useLabels

useLabels returns a part's texts. The instance's `prop` wins over the provider's group, which wins over the `common` keys named in `fallbacks`, which win over the English `defaults`. Every function in the result gets the locale's formatters as its last argument, so a label function typed `(count: number, format: LocaleFormatters) => string` is called as `labels.x(count)`. Pass every argument before it, `undefined` included.

No props of its own. Accepts the props of the element it renders.