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

  • configureI18n initializes i18next with your application's texts.
  • DContextProvider sets 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'),
      }}
    />
  );
}