# Internationalization (i18n)

> LocaleFormats handles formatting; text translation is manual — where the framework helps and where it doesn't.

Source: https://crossfly.dev/docs/best-practices/internacionalizacao — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Internationalization (i18n)

The split is clear: **formatting is automatic** (`LocaleFormats`), **text translation is manual**
(you). Knowing which is which avoids hunting for an API that doesn't exist.

## Automatic formatting — `LocaleFormats`
`LocaleFormats` (in `dev.crossfly.util`) concentrates the locale-sensitive separators and formats, and
is respected by `DatePicker`/`TimePicker`/currency masks **automatically**:
| Field | Type | Controls |
|---|---|---|
| `dateFormat` | `UIConstants.DateFormat` | date order (DMY/MDY/YMD) |
| `dateSeparator` | `char` | date separator (`/`, `-`, `.`) |
| `timeSeparator` | `char` | time separator (`:`) |
| `is24Hour` | `boolean` | 12h vs 24h |
| `decimalSeparator` | `char` | decimal (`,` or `.`) |
| `thousandsSeparator` | `char` | thousands |
| `weekStart` | `byte` | first day of the week |

```java
LocaleFormats.decimalSeparator = ',';
LocaleFormats.thousandsSeparator = '.';
LocaleFormats.is24Hour = true;
// from here on, the currency mask and the pickers already format in the pt-BR standard
```

## Text translation — manual
**There is no built-in string catalog.** Externalize your text (your own `Map`/enum) and swap it
according to the language:
```java
enum Idioma { PT, EN }
static Idioma idioma = Idioma.PT;
static final Map<String, Map<Idioma,String>> T = Map.of(
   "salvar", Map.of(Idioma.PT, "Salvar", Idioma.EN, "Save"),
   "cancelar", Map.of(Idioma.PT, "Cancelar", Idioma.EN, "Cancel")
);
static String t(String chave) { return T.get(chave).get(idioma); }

// usage:
new Button(t("salvar"));
```
Month/day names and picker labels are **public fields** that you reassign to localize.

## Why the layout already helps
Layouts based on **fraction/`Relative`** tolerate text of different lengths across languages ("Salvar"
vs. "Enregistrer") without breaking — another gain from not using fixed widths.

## Best practices
- Configure `LocaleFormats` **once** at bootstrap according to the user's locale.
- Externalize **all** visible text from the start (retrofitting later is expensive).
- Test the "widest" languages to make sure the layout accommodates them.

## Common mistakes
- Looking for a built-in string/`R.string` system — it doesn't exist; build your own.
- Hardcoding `/` in the date instead of using `LocaleFormats.dateSeparator`.

## See also
- [DatePicker](/docs/ui/widgets/datepicker) · [Masks and keyboards](/docs/ui/forms-and-input/masks-and-keyboards)
