# 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:

  • configureI18n inicializa i18next con los textos de tu aplicación.
  • DContextProvider define 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'),
      }}
    />
  );
}