# Locale Field

> Content in several languages and switching context: LocaleField, LocaleSwitcher, WorkspaceSwitcher.

Component · Forms · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/LocaleField/)

Content in several languages and switching context: `LocaleField`, `LocaleSwitcher`, `WorkspaceSwitcher`.

LocaleField: `label`, `locales` ({code, label, flag}), `value` / `defaultValue` ({[code]: text}) + `onChange`, `source` (language others copy from; default the first), `active`, `multiline`, `rows`, `onTranslate(text, to)` (return or resolve the translation), `hint`, `error`. Missing languages carry a warn diamond; empty fields show the source text as placeholder.
LocaleSwitcher: `languages` ({code, label, short, flag}), `language` / `onLanguage`, `currencies`, `currency` / `onCurrency`, `variant` ("frame" for the dark top bar).
WorkspaceSwitcher: `items` ({id, name, sub, tint, logo}), `value` / `onChange`, `label`, `footer`, `variant` ("frame").

## Example

```tsx
import * as React from "react";
import { LocaleField, LocaleSwitcher, WorkspaceSwitcher } from "@twentyfirstmedia/pro";

const langs = [
  {
    code: "de",
    label: "Deutsch",
    short: "de",
    flag: "🇩🇪",
  },
  {
    code: "en",
    label: "English",
    short: "en",
    flag: "🇬🇧",
  },
  {
    code: "fr",
    label: "Français",
    short: "fr",
    flag: "🇫🇷",
  },
];
export default function Preview() {
  return (
    <div
      className="tf-grid"
      style={
        {
          "--cols": "1.3fr 1fr",
          gap: 16,
          alignItems: "start",
        } as React.CSSProperties
      }
    >
      <div
        className="tf-panel"
        style={{
          gap: 20,
        }}
      >
        <LocaleField
          label="Product name"
          locales={[
            {
              code: "de",
              label: "DE",
            },
            {
              code: "en",
              label: "EN",
            },
            {
              code: "fr",
              label: "FR",
            },
            {
              code: "es",
              label: "ES",
            },
          ]}
          defaultValue={{
            de: "Instagram Follower kaufen",
            en: "Buy Instagram followers",
          }}
          onTranslate={(t, to) => "[" + to + "] " + t}
        />
        <LocaleField
          label="Short description"
          multiline
          active="en"
          locales={[
            {
              code: "de",
              label: "DE",
            },
            {
              code: "en",
              label: "EN",
            },
          ]}
          defaultValue={{
            de: "Echte Follower, Lieferung in einer Stunde.",
          }}
        />
      </div>
      <div
        className="tf-panel"
        style={{
          gap: 16,
          alignItems: "flex-start",
        }}
      >
        <WorkspaceSwitcher
          label="Shops"
          items={[
            {
              id: "fp",
              name: "Northwind",
              sub: "northwind.de",
              tint: "sky",
            },
            {
              id: "fm",
              name: "FollowerMarket",
              sub: "followermarket.com",
              tint: "mint",
            },
            {
              id: "fr",
              name: "FollowerRocket",
              sub: "followerrocket.de",
              tint: "butter",
            },
          ]}
        />
        <LocaleSwitcher languages={langs} currencies={["EUR", "CHF", "USD"]} />
      </div>
    </div>
  );
}
```

## Props

### LocaleField

| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `ReactNode` |  |
| `locales` | `{ code: string; label?: string; flag?: string }[]` | Required |
| `value?` | `Record<string, string>` |  |
| `defaultValue?` | `Record<string, string>` |  |
| `onChange?` | `(v: Record<string, string>) => void` |  |
| `source?` | `string` |  |
| `active?` | `string` |  |
| `multiline?` | `boolean` |  |
| `rows?` | `number` |  |
| `placeholder?` | `string` |  |
| `onTranslate?` | `(text: string, to: string) => string \| Promise<string>` |  |
| `hint?` | `ReactNode` |  |
| `error?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### LocaleSwitcher

| Prop | Type | Notes |
| --- | --- | --- |
| `languages?` | `{ code: string; label: string; short?: string; flag?: string }[]` |  |
| `language?` | `string` |  |
| `onLanguage?` | `(code: string) => void` |  |
| `currencies?` | `string[]` |  |
| `currency?` | `string` |  |
| `onCurrency?` | `(c: string) => void` |  |
| `variant?` | `"frame"` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### WorkspaceSwitcher

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `{ id: string; name: string; sub?: string; tint?: Tint; logo?: ReactNode }[]` | Required |
| `value?` | `string` |  |
| `onChange?` | `(id: string) => void` |  |
| `label?` | `string` |  |
| `footer?` | `ReactNode` |  |
| `variant?` | `"frame"` |  |
| `width?` | `number` |  |
| `compact?` | `boolean` | Only the workspace's mark, its name as the accessible name (a collapsed sidebar). |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
