# Idiomas y formatos
Esta página es para quien construye una aplicación o un widget con Dynamic UI y necesita mostrarlo en más de un idioma, o con montos y fechas en el formato de su mercado. Conviene leerla antes de escribir los primeros textos de la interfaz: la librería fija el formato de interpolación de i18next, y los componentes traen textos por defecto en inglés que se reemplazan por props.
Hay tres piezas independientes:
configureI18ninicializa i18next con los textos de tu aplicación.DContextProviderdefine el idioma y la moneda que lee tu aplicación, y la moneda que usan los componentes de montos.- Las props de texto de cada componente reemplazan sus textos por defecto.
# Dependencias
i18next y react-i18next son dependencias peer de @dynamic-framework/ui-react. Instálalas en tu proyecto con los rangos que declara el paquete:
| Paquete | Rango |
|---|---|
i18next | >=25 <26 |
react-i18next | >=16 <17 |
npm install i18next@25 react-i18next@16
# Configurar los textos con configureI18n
configureI18n(resources, options) registra react-i18next en la instancia por defecto de i18next, la inicializa con tus recursos y devuelve una promesa con la función t.
| Parámetro | Tipo | Por defecto |
|---|---|---|
resources | Resource de i18next | Obligatorio |
options.lng | string | 'en' |
options.fallbackLng | string | 'en' |
Resto de options | InitOptions de i18next | Se pasan a i18n.init |
Pasa siempre fallbackLng de forma explícita: si tu aplicación no tiene textos en inglés, el valor por defecto 'en' apunta a un idioma que no existe en tus recursos.
// 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',
});
Importa ese módulo una vez, en el punto de entrada de la aplicación o del widget, antes de renderizar. Los componentes leen los textos con useTranslation de 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>
);
}
# Interpolación con llave simple
configureI18n configura i18next con prefix: '{' y suffix: '}'. Las variables de tus textos van entre llaves simples:
// ✅ Se reemplaza: "Hola, Ana"
{ "greeting": "Hola, {name}" }
// ❌ No se reemplaza: se muestra "Hola, {{name}}"
{ "greeting": "Hola, {{name}}" }
La configuración también fija escapeValue: false, porque React ya escapa el texto que renderiza.
# Opciones de i18next que no conviene cambiar
Las opciones que pasas a configureI18n se mezclan de forma superficial con las de la librería: un objeto interpolation propio reemplaza el de la librería completo, se pierde la llave simple y vuelven {{var}} y escapeValue: true.
// ✅ Sin interpolation: se mantiene la llave simple
configureI18n(resources, { lng: 'es', fallbackLng: 'es' });
// ❌ Reemplaza prefix y suffix: "Hola, {name}" deja de interpolarse
configureI18n(resources, {
lng: 'es',
fallbackLng: 'es',
interpolation: { escapeValue: false },
});
Si necesitas otra opción de interpolación, incluye en el mismo objeto prefix: '{', suffix: '}' y escapeValue: false.
# Idioma y moneda en DContextProvider
DContextProvider recibe language y currency entre sus props (ver Context Provider).
| Prop | Tipo | Por defecto | A qué afecta |
|---|---|---|---|
language | string | 'en' | Queda disponible en useDContext().language para tu aplicación. Ningún componente lo lee y no cambia el idioma de i18next, que se fija con lng de configureI18n. |
currency | CurrencyProps | { symbol: '$', precision: 2, separator: ',', decimal: '.' } | Formato de DCurrencyText, DInputCurrency (muestra symbol salvo que pases currencyCode) y useFormatCurrency. |
CurrencyProps tiene cuatro campos, todos obligatorios:
| Campo | Tipo | Ejemplo |
|---|---|---|
symbol | string | '$' |
precision | number | 0 |
separator | string | '.' (separador de miles) |
decimal | string | ',' (separador decimal) |
Para que tu aplicación y los textos usen el mismo idioma, toma el valor de una sola constante:
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>
);
}
# Cambiar el idioma o la moneda en tiempo de ejecución
DContextProvider copia language y currency a su estado al montarse: cambiar esas props después no tiene efecto. Para cambiarlos, usa setContext de useDContext, y cambia el idioma de i18next con 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} />;
}
# Formatear montos con useFormatCurrency
useFormatCurrency(...valores) formatea con la moneda del contexto. Devuelve values, con los montos que recibe ya formateados, y format(valor, currencyOptions?), que formatea cualquier monto y acepta una moneda distinta para un caso puntual.
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>
);
}
Con currency={{ symbol: '$', precision: 0, separator: '.', decimal: ',' }} en el provider, 1234567.5 se muestra como $1.234.568, y format(99.9, …) con la moneda del ejemplo devuelve US$99.90.
Para mostrar un monto sin lógica adicional, usa DCurrencyText, que aplica el mismo formato:
import { DCurrencyText } from '@dynamic-framework/ui-react';
export function Total({ amount }: { amount: number }) {
return <DCurrencyText value={amount} />;
}
# Fechas en DDatePicker
DDatePicker no lee el idioma del contexto. Los nombres de meses y días salen del locale de date-fns que le pasas, que se aplica al calendario y a la cabecera:
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"
/>
);
}
# Textos propios de los componentes
Los componentes con texto visible o etiquetas accesibles traen valores por defecto en inglés. Para traducirlos, pasa tus textos por la prop correspondiente, por ejemplo con t:
| Componente | Prop | Nota |
|---|---|---|
DOtp | texts: submit, resend, title, contact, resendText, invalidCode | El objeto reemplaza al de la librería completo: hay que pasar todas las claves. |
DPasswordStrengthMeter | validationMessages: number, lowercaseLetter, uppercaseLetter, especialChar, notMatch; label | |
DDataStateWrapper | messages: loading, empty, error, retry | Cada clave que omitas usa su texto por defecto. |
DErrorBoundary | messages: error, retry | Cada clave que omitas usa su texto por defecto. |
DVoucher | downloadText, shareText | |
DCreditCard | holderText | |
DDatePicker | locale, headerPrevMonthAriaLabel, headerNextMonthAriaLabel | Ver Fechas en DDatePicker. |
DInputSearch | placeholder | |
DInputPassword | iconEndAriaLabel | |
DInputCounter | iconStartAriaLabel, iconEndAriaLabel | |
DAlert | closeAriaLabel | |
DInputPin | aria-label | |
useConfirmModal | title, message, confirmLabel, cancelLabel; critical.codeLabel, critical.inputPlaceholder | Son opciones del hook, no props de un componente. |
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'),
}}
/>
);
}