TranslatableFields

Shows one field in several locales, one tab per locale. A child field with source title reads title.en and title.fr.

Installation

pnpm dlx shadcn@latest add @chelone/translatable-fields

<TranslatableFields> is the read-only twin of TranslatableInputs. List the locales and give it the fields to repeat. Each locale gets a tab, and each tab renders the children against that locale's value.

import { TextField, TranslatableFields } from "@chelone/react";
 
<TranslatableFields locales={["en", "fr"]}>
  <TextField source="title" />
</TranslatableFields>

The <TextField source="title" /> reads title.en in the first tab and title.fr in the second, from a record shaped { title: { en, fr } }. See Translation for how translatable records are stored.

Inside a <Show> it uses the ambient record and resource. Pass record or resource to use it elsewhere. Without a record it throws, except under a <Show> whose record has not loaded yet, where the React component renders nothing. See Vue for the difference.

Every locale stays mounted

A hidden tab is not unmounted, so the fields of every locale are in the page whichever tab is showing.

The start locale

defaultLocale picks the tab selected first. Without it, or when it is not one of locales, the start locale is the app locale when that is listed, and the first locale otherwise.

Props

PropTypeDefaultDescription
childrenReactNode—
classNamestring—
defaultLocalestring—Ignored when it is not one of `locales`. Defaults to the app locale when listed, else the first locale.
groupKeystring—Prefix that keeps the ids of two groups apart. Defaults to `""`.
localesreadonly string[]—
recordRecord<string, unknown>—The record to show. Defaults to the ambient record.
resourcestring—The resource the fields belong to. Defaults to the ambient resource.
selectorReactNode—Replaces the default `<TranslatableFieldsTabs>` locale selector.

The parts

The selector prop replaces <TranslatableFieldsTabs> when you want a different locale picker.

TranslatableFieldsTabs

The default locale selector: one tab per locale of the ambient translatable context.

PropTypeDefaultDescription
groupKeystring—Prefix that keeps the ids of two groups apart. Defaults to `""`.
variant"default" | "line" | null—

TranslatableFieldsTab

The header of one locale. Its label is ra.locales.<locale> from the catalog, or the capitalised locale code when that key is missing.

PropTypeDefaultDescription
groupKeystring—Prefix that keeps the ids of two groups apart. Defaults to `""`.
localestring—

TranslatableFieldsTabContent

The panel of one locale. It renders the children against that locale's value.

PropTypeDefaultDescription
childrenReactNode—
classNamestring—
groupKeystring—Prefix that keeps the ids of two groups apart. Defaults to `""`.
localestring—

Reading the selected locale

useTranslatable, TranslatableContextProvider and useTranslatableContext are the same ones TranslatableInputs documents. A custom selector reads selectedLocale and calls selectLocale from useTranslatableContext().

Vue

@chelone/vue ships the same components with these differences:

  • The #selector slot replaces the default locale selector, in place of the selector prop.
  • The tabs are native <button role="tab"> elements, not Radix tabs.
  • provideTranslatable(translatable) takes the place of <TranslatableContextProvider>.
  • While a <Show> record is still loading, the component still renders the tab strip, over empty panels, instead of nothing. It throws only when there is no record context at all.
<script setup lang="ts">
import { TextField, TranslatableFields } from "@chelone/vue";
</script>
 
<template>
  <TranslatableFields :locales="['en', 'fr']">
    <TextField source="title" />
  </TranslatableFields>
</template>