# Languages and Formats
This page is for developers building an application or widget with Dynamic UI who need to show it in more than one language, or with amounts and dates in the format of their market. Read it before writing the first interface texts: the library sets the i18next interpolation format, and components ship English default texts that you replace through props.
There are three independent pieces:
configureI18ninitializes i18next with your application's texts.DContextProvidersets the language and currency your application reads, and the currency used by amount components.- Each component's text props replace its default texts.
# Dependencies
i18next and react-i18next are peer dependencies of @dynamic-framework/ui-react. Install them in your project with the ranges the package declares:
| Package | Range |
|---|---|
i18next | >=25 <26 |
react-i18next | >=16 <17 |
npm install i18next@25 react-i18next@16
# Configuring Texts with configureI18n
configureI18n(resources, options) registers react-i18next on the default i18next instance, initializes it with your resources, and returns a promise with the t function.
| Parameter | Type | Default |
|---|---|---|
resources | i18next Resource | Required |
options.lng | string | 'en' |
options.fallbackLng | string | 'en' |
Other options | i18next InitOptions | Passed to i18n.init |
Always pass fallbackLng explicitly: if your application has no English texts, the 'en' default points to a language that does not exist in your resources.
// i18n.ts
import { configureI18n } from '@dynamic-framework/ui-react';
const resources = {
es: {
translation: {
greeting: 'Hola, {name}',
balance: 'Saldo disponible',
},
},
en: {
translation: {
greeting: 'Hello, {name}',
balance: 'Available balance',
},
},
};
export const i18nReady = configureI18n(resources, {
lng: 'es',
fallbackLng: 'es',
});
Import that module once, in the entry point of the application or widget, before rendering. Components read texts with useTranslation from react-i18next:
import { useTranslation } from 'react-i18next';
export function Greeting({ name }: { name: string }) {
const { t } = useTranslation();
return (
<div>
<p>{t('greeting', { name })}</p>
<p>{t('balance')}</p>
</div>
);
}
# Single-Brace Interpolation
configureI18n configures i18next with prefix: '{' and suffix: '}'. Variables in your texts go between single braces:
// ✅ Replaced: "Hello, Ana"
{ "greeting": "Hello, {name}" }
// ❌ Not replaced: shows "Hello, {{name}}"
{ "greeting": "Hello, {{name}}" }
The configuration also sets escapeValue: false, because React already escapes the text it renders.
# i18next Options You Should Not Change
The options you pass to configureI18n are merged shallowly with the library's: your own interpolation object replaces the library's entirely, single braces are lost, and {{var}} and escapeValue: true come back.
// ✅ No interpolation: single braces are kept
configureI18n(resources, { lng: 'es', fallbackLng: 'es' });
// ❌ Replaces prefix and suffix: "Hello, {name}" is no longer interpolated
configureI18n(resources, {
lng: 'es',
fallbackLng: 'es',
interpolation: { escapeValue: false },
});
If you need another interpolation option, include prefix: '{', suffix: '}', and escapeValue: false in the same object.
# Language and Currency in DContextProvider
DContextProvider takes language and currency among its props (see Context Provider).
| Prop | Type | Default | What it affects |
|---|---|---|---|
language | string | 'en' | Available to your application in useDContext().language. No component reads it and it does not change the i18next language, which is set with lng in configureI18n. |
currency | CurrencyProps | { symbol: '$', precision: 2, separator: ',', decimal: '.' } | Format of DCurrencyText, DInputCurrency (shows symbol unless you pass currencyCode), and useFormatCurrency. |
CurrencyProps has four fields, all required:
| Field | Type | Example |
|---|---|---|
symbol | string | '$' |
precision | number | 0 |
separator | string | '.' (thousands separator) |
decimal | string | ',' (decimal separator) |
To keep your application and its texts in the same language, take the value from a single constant:
import { DContextProvider, configureI18n } from '@dynamic-framework/ui-react';
import type { ReactNode } from 'react';
const LANGUAGE = 'es';
const resources = {
es: { translation: { balance: 'Saldo disponible' } },
};
configureI18n(resources, { lng: LANGUAGE, fallbackLng: LANGUAGE });
export function Providers({ children }: { children: ReactNode }) {
return (
<DContextProvider
language={LANGUAGE}
currency={{ symbol: '$', precision: 0, separator: '.', decimal: ',' }}
>
{children}
</DContextProvider>
);
}
# Changing the Language or Currency at Runtime
DContextProvider copies language and currency into its state when it mounts: changing those props afterwards has no effect. To change them, use setContext from useDContext, and change the i18next language with changeLanguage:
import { DButton, useDContext } from '@dynamic-framework/ui-react';
import { useTranslation } from 'react-i18next';
export function LanguageSwitch() {
const { setContext } = useDContext();
const { i18n } = useTranslation();
const switchToEnglish = async () => {
await i18n.changeLanguage('en');
setContext({
language: 'en',
currency: { symbol: 'US$', precision: 2, separator: ',', decimal: '.' },
});
};
return <DButton text="English" onClick={switchToEnglish} />;
}
# Formatting Amounts with useFormatCurrency
useFormatCurrency(...values) formats with the context currency. It returns values, with the amounts it receives already formatted, and format(value, currencyOptions?), which formats any amount and accepts a different currency for a one-off case.
import { useFormatCurrency } from '@dynamic-framework/ui-react';
export function Balance({ amount, fee }: { amount: number; fee: number }) {
const { values: [formattedAmount], format } = useFormatCurrency(amount);
return (
<div>
<p>{formattedAmount}</p>
<p>{format(fee, { symbol: 'US$', precision: 2, separator: ',', decimal: '.' })}</p>
</div>
);
}
With currency={{ symbol: '$', precision: 0, separator: '.', decimal: ',' }} on the provider, 1234567.5 is shown as $1.234.568, and format(99.9, …) with the example currency returns US$99.90.
To show an amount without extra logic, use DCurrencyText, which applies the same format:
import { DCurrencyText } from '@dynamic-framework/ui-react';
export function Total({ amount }: { amount: number }) {
return <DCurrencyText value={amount} />;
}
# Dates in DDatePicker
DDatePicker does not read the language from the context. Month and day names come from the date-fns locale you pass it, which applies to the calendar and the header:
import { useState } from 'react';
import { DDatePicker } from '@dynamic-framework/ui-react';
import { es } from 'date-fns/locale';
export function DueDate() {
const [date, setDate] = useState<Date | null>(null);
return (
<DDatePicker
locale={es}
selected={date}
onChange={(value: Date | null) => setDate(value)}
inputLabel="Fecha de vencimiento"
headerPrevMonthAriaLabel="Mes anterior"
headerNextMonthAriaLabel="Mes siguiente"
/>
);
}
# Component Texts
Components with visible text or accessible labels ship English defaults. To translate them, pass your texts through the matching prop, for example with t:
| Component | Prop | Note |
|---|---|---|
DOtp | texts: submit, resend, title, contact, resendText, invalidCode | The object replaces the library's entirely: pass every key. |
DPasswordStrengthMeter | validationMessages: number, lowercaseLetter, uppercaseLetter, especialChar, notMatch; label | |
DDataStateWrapper | messages: loading, empty, error, retry | Each key you omit uses its default text. |
DErrorBoundary | messages: error, retry | Each key you omit uses its default text. |
DVoucher | downloadText, shareText | |
DCreditCard | holderText | |
DDatePicker | locale, headerPrevMonthAriaLabel, headerNextMonthAriaLabel | See Dates in DDatePicker. |
DInputSearch | placeholder | |
DInputPassword | iconEndAriaLabel | |
DInputCounter | iconStartAriaLabel, iconEndAriaLabel | |
DAlert | closeAriaLabel | |
DInputPin | aria-label | |
useConfirmModal | title, message, confirmLabel, cancelLabel; critical.codeLabel, critical.inputPlaceholder | These are hook options, not component props. |
import { DOtp } from '@dynamic-framework/ui-react';
import { useTranslation } from 'react-i18next';
export function VerifyCode({ onSubmit }: { onSubmit: () => Promise<void> }) {
const { t } = useTranslation();
return (
<DOtp
action={onSubmit}
texts={{
title: t('otp.title'),
submit: t('otp.submit'),
resend: t('otp.resend'),
resendText: t('otp.resendText'),
invalidCode: t('otp.invalidCode'),
contact: t('otp.contact'),
}}
/>
);
}