<bpdm/ui />
Getting Started

Internationalization

Translating built-in labels, right-to-left (RTL) support, and locale-aware formatting across bpdm/ui form components — React (@bpdm/ui) and Angular (@bpdm/ng).

bpdm/ui form components adapt to internationalized apps three ways: their built-in screen-reader strings are translatable, their layout is right-to-left safe, and value formatting is locale-aware where it applies.

Translating labels

A few components render their own screen-reader text (button labels, live announcements). These default to English but are fully overridable — pass your translated strings and nothing else changes. In React it's a messages object (merged over the defaults); in Angular it's a messages input, except the OTP per-cell label which is a function.

ComponentPropDefault strings
Number Inputmessages={{ increase, decrease }}Increase · Decrease
Password Inputmessages={{ show, hide }} (strength via labels)Show password · Hide password
Secure Fieldmessages={{ reveal, hide, copy, copied }}Reveal · Hide · Copy · Copied to clipboard
OTP Inputaria-label (group) + cellLabel={(i, n) => …} (per cell)One-time code · Character N of M
secret-fr.tsx
import { SecureField } from '@bpdm/ui/secure-field';
import { InputOtp } from '@bpdm/ui/input-otp';

export function FrenchSecrets() {
  return (
    <>
      <SecureField
        copyable
        messages={{ reveal: 'Révéler', hide: 'Masquer', copy: 'Copier', copied: 'Copié' }}
      />
      <InputOtp
        length={6}
        integerOnly
        aria-label="Code de vérification"
        cellLabel={(i, n) => `Chiffre ${i + 1} sur ${n}`}
      />
    </>
  );
}
secret-fr.component.ts
import { Component } from '@angular/core';
import { BpdmSecureField, BpdmInputOtp } from '@bpdm/ng';

@Component({
  selector: 'app-french-secrets',
  standalone: true,
  imports: [BpdmSecureField, BpdmInputOtp],
  template: `
    <bpdm-secure-field copyable [messages]="secretMessages" />
    <bpdm-input-otp
      [length]="6"
      integerOnly
      aria-label="Code de vérification"
      [cellLabel]="cellLabel" />
  `,
})
export class FrenchSecretsComponent {
  readonly secretMessages = { reveal: 'Révéler', hide: 'Masquer', copy: 'Copier', copied: 'Copié' };
  readonly cellLabel = (i: number, n: number) => `Chiffre ${i + 1} sur ${n}`;
}

Wire these to your i18n library (react-i18next, @angular/localize, etc.) by passing the translated values through — the components hold no message catalogue of their own.

Right-to-left (RTL)

Every form component is built with CSS logical properties (padding-inline, text-start, inset-inline, logical border-radius) rather than physical left/right. So they mirror automatically inside a right-to-left context — set dir="rtl" on an ancestor and icons, affixes, alignment, and the connected OTP rounding all flip correctly. No component prop is required.

rtl.tsx
import { Input } from '@bpdm/ui/input';
import { MoneyInput } from '@bpdm/ui/money-input';

export function ArabicForm() {
  return (
    <div dir="rtl">
      <Input startIcon={<SearchIcon />} placeholder="بحث" />
      <MoneyInput currency="AED" locale="ar-AE" defaultValue="2500" />
    </div>
  );
}
rtl.html
<div dir="rtl">
  <input bpdmInput placeholder="بحث" />
  <bpdm-money-input currency="AED" locale="ar-AE" defaultValue="2500" />
</div>

The OTP Input's ←/→ arrow keys follow the visual cell order too: inside dir="rtl", ArrowLeft advances to the next cell and ArrowRight steps back, matching the mirrored layout.

Locale-aware formatting

The Money Input formats through the platform Intl.NumberFormat, so a single stored amount renders with the right grouping, symbol, and decimal count per currency + localeen-IN groups as 1,00,000, de-DE as 100.000,00 €, JPY shows no decimals. The value you read back stays a precise numeric string.

amounts.tsx
import { MoneyInput } from '@bpdm/ui/money-input';

export function Amounts() {
  return (
    <>
      <MoneyInput currency="INR" locale="en-IN" defaultValue="100000" /> {/* ₹1,00,000.00 */}
      <MoneyInput currency="EUR" locale="de-DE" defaultValue="100000" /> {/* 100.000,00 € */}
      <MoneyInput currency="JPY" locale="ja-JP" defaultValue="100000" /> {/* ¥100,000 */}
    </>
  );
}
amounts.html
<bpdm-money-input currency="INR" locale="en-IN" defaultValue="100000" />
<bpdm-money-input currency="EUR" locale="de-DE" defaultValue="100000" />
<bpdm-money-input currency="JPY" locale="ja-JP" defaultValue="100000" />

On this page