or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-core.mdadvanced.mdauth.mddata-management.mddetail-views.mdforms-inputs.mdi18n.mdindex.mdlayout-navigation.mdlists-data-display.mdui-components.md

i18n.mddocs/

0

# Internationalization

1

2

React Admin provides comprehensive internationalization (i18n) support for building multi-language admin applications. The system handles UI translations, locale-specific formatting, right-to-left (RTL) languages, and translatable content management.

3

4

## i18n Provider Interface

5

6

The i18n provider defines how your application handles translations and locale management.

7

8

```typescript { .api }

9

import { I18nProvider } from 'react-admin';

10

11

interface I18nProvider {

12

translate: (key: string, options?: TranslateOptions) => string;

13

changeLocale: (locale: string) => Promise<void>;

14

getLocale: () => string;

15

getLocales?: () => Locale[];

16

}

17

18

interface TranslateOptions {

19

smart_count?: number;

20

_?: string;

21

[key: string]: any;

22

}

23

24

interface Locale {

25

locale: string;

26

name: string;

27

}

28

```

29

30

### Basic i18n Provider Example

31

32

```typescript

33

import polyglotI18nProvider from 'ra-i18n-polyglot';

34

import englishMessages from 'ra-language-english';

35

import frenchMessages from 'ra-language-french';

36

import spanishMessages from 'ra-language-spanish';

37

38

const messages = {

39

en: englishMessages,

40

fr: frenchMessages,

41

es: spanishMessages,

42

};

43

44

const i18nProvider = polyglotI18nProvider(

45

locale => messages[locale],

46

'en', // default locale

47

[

48

{ locale: 'en', name: 'English' },

49

{ locale: 'fr', name: 'Français' },

50

{ locale: 'es', name: 'Español' }

51

]

52

);

53

54

// Usage in Admin

55

<Admin

56

dataProvider={dataProvider}

57

i18nProvider={i18nProvider}

58

>

59

<Resource name="posts" list={PostList} />

60

</Admin>

61

```

62

63

## Translation Hooks

64

65

### useTranslate

66

67

Hook for translating messages in components.

68

69

```typescript { .api }

70

import { useTranslate } from 'react-admin';

71

72

type TranslateFunction = (key: string, options?: TranslateOptions) => string;

73

74

const useTranslate: () => TranslateFunction;

75

```

76

77

#### Usage Examples

78

79

```typescript

80

import { useTranslate } from 'react-admin';

81

82

const MyComponent = () => {

83

const translate = useTranslate();

84

85

return (

86

<div>

87

<h1>{translate('page.dashboard.title')}</h1>

88

<p>{translate('page.dashboard.welcome', { name: 'John' })}</p>

89

<p>{translate('post.count', { smart_count: 5 })}</p>

90

</div>

91

);

92

};

93

94

// Message files:

95

// en.json: { "page.dashboard.title": "Dashboard", "page.dashboard.welcome": "Welcome, %{name}!", "post.count": "One post |||| %{smart_count} posts" }

96

// fr.json: { "page.dashboard.title": "Tableau de bord", "page.dashboard.welcome": "Bienvenue, %{name} !", "post.count": "Un article |||| %{smart_count} articles" }

97

```

98

99

### useTranslateLabel

100

101

Hook for translating field and resource labels.

102

103

```typescript { .api }

104

import { useTranslateLabel } from 'react-admin';

105

106

const useTranslateLabel: () => (params: TranslateLabelParams) => string;

107

108

interface TranslateLabelParams {

109

source?: string;

110

resource?: string;

111

label?: string;

112

}

113

```

114

115

#### Usage Example

116

117

```typescript

118

import { useTranslateLabel } from 'react-admin';

119

120

const CustomField = ({ source, resource, label }) => {

121

const translateLabel = useTranslateLabel();

122

123

const fieldLabel = translateLabel({ source, resource, label });

124

125

return (

126

<div>

127

<label>{fieldLabel}</label>

128

{/* field content */}

129

</div>

130

);

131

};

132

```

133

134

## Locale Management

135

136

### useLocale & useSetLocale

137

138

Hooks for getting and setting the current locale.

139

140

```typescript { .api }

141

import { useLocale, useSetLocale } from 'react-admin';

142

143

const useLocale: () => string;

144

const useSetLocale: () => (locale: string) => Promise<void>;

145

```

146

147

### useLocales

148

149

Hook for getting available locales.

150

151

```typescript { .api }

152

import { useLocales } from 'react-admin';

153

154

const useLocales: () => Locale[] | undefined;

155

```

156

157

### Locale Management Examples

158

159

```typescript

160

import { useLocale, useSetLocale, useLocales } from 'react-admin';

161

162

const LanguageSwitcher = () => {

163

const locale = useLocale();

164

const setLocale = useSetLocale();

165

const locales = useLocales();

166

167

const handleLocaleChange = (newLocale) => {

168

setLocale(newLocale);

169

};

170

171

return (

172

<select value={locale} onChange={(e) => handleLocaleChange(e.target.value)}>

173

{locales?.map(locale => (

174

<option key={locale.locale} value={locale.locale}>

175

{locale.name}

176

</option>

177

))}

178

</select>

179

);

180

};

181

182

// Using LocalesMenuButton component

183

import { LocalesMenuButton } from 'react-admin';

184

185

const AppBar = () => (

186

<AppBar>

187

<TitlePortal />

188

<Box flex="1" />

189

<LocalesMenuButton />

190

<UserMenu />

191

</AppBar>

192

);

193

```

194

195

## Translatable Content

196

197

### TranslatableInputs

198

199

Component for managing translatable form inputs.

200

201

```typescript { .api }

202

import { TranslatableInputs } from 'react-admin';

203

204

interface TranslatableInputsProps {

205

locales?: string[];

206

defaultLocale?: string;

207

selector?: React.ComponentType<TranslatableInputsTabsProps>;

208

children: React.ReactNode;

209

className?: string;

210

sx?: any;

211

}

212

213

const TranslatableInputs: React.FC<TranslatableInputsProps>;

214

```

215

216

#### TranslatableInputs Example

217

218

```typescript

219

import {

220

TranslatableInputs,

221

TextInput,

222

Edit,

223

SimpleForm

224

} from 'react-admin';

225

226

const PostEdit = () => (

227

<Edit>

228

<SimpleForm>

229

<TextInput source="id" disabled />

230

231

<TranslatableInputs locales={['en', 'fr', 'es']}>

232

<TextInput source="title" validate={required()} />

233

<TextInput

234

source="content"

235

multiline

236

rows={4}

237

validate={required()}

238

/>

239

<TextInput source="metaDescription" />

240

</TranslatableInputs>

241

242

<DateInput source="publishedAt" />

243

</SimpleForm>

244

</Edit>

245

);

246

247

// Data structure:

248

// {

249

// id: 1,

250

// title: { en: "Hello", fr: "Bonjour", es: "Hola" },

251

// content: { en: "Content", fr: "Contenu", es: "Contenido" },

252

// publishedAt: "2024-01-15"

253

// }

254

```

255

256

### TranslatableFields

257

258

Component for displaying translatable content in show/list views.

259

260

```typescript { .api }

261

import { TranslatableFields } from 'react-admin';

262

263

interface TranslatableFieldsProps {

264

locales?: string[];

265

selector?: React.ComponentType<TranslatableFieldsTabsProps>;

266

children: React.ReactNode;

267

className?: string;

268

sx?: any;

269

}

270

271

const TranslatableFields: React.FC<TranslatableFieldsProps>;

272

```

273

274

#### TranslatableFields Example

275

276

```typescript

277

import {

278

TranslatableFields,

279

TextField,

280

Show,

281

SimpleShowLayout

282

} from 'react-admin';

283

284

const PostShow = () => (

285

<Show>

286

<SimpleShowLayout>

287

<TextField source="id" />

288

289

<TranslatableFields locales={['en', 'fr', 'es']}>

290

<TextField source="title" />

291

<TextField source="content" />

292

<TextField source="metaDescription" />

293

</TranslatableFields>

294

295

<DateField source="publishedAt" />

296

</SimpleShowLayout>

297

</Show>

298

);

299

```

300

301

### Translatable Context and Hooks

302

303

```typescript { .api }

304

import {

305

useTranslatable,

306

useTranslatableContext,

307

TranslatableContext,

308

TranslatableContextProvider

309

} from 'react-admin';

310

311

interface TranslatableContextValue {

312

locale: string;

313

locales: string[];

314

selectLocale: (locale: string) => void;

315

getSource: (source: string) => string;

316

getLabel: (source: string) => string;

317

}

318

319

const useTranslatable: (options?: UseTranslatableOptions) => TranslatableContextValue;

320

const useTranslatableContext: () => TranslatableContextValue;

321

```

322

323

## Message Structure and Patterns

324

325

### Resource and Field Labels

326

327

React Admin follows specific conventions for translation keys:

328

329

```typescript

330

// Translation message structure

331

const messages = {

332

resources: {

333

posts: {

334

name: 'Post |||| Posts',

335

fields: {

336

title: 'Title',

337

content: 'Content',

338

publishedAt: 'Published At',

339

author: 'Author'

340

}

341

},

342

users: {

343

name: 'User |||| Users',

344

fields: {

345

firstName: 'First Name',

346

lastName: 'Last Name',

347

email: 'Email Address'

348

}

349

}

350

},

351

ra: {

352

action: {

353

save: 'Save',

354

delete: 'Delete',

355

cancel: 'Cancel',

356

edit: 'Edit',

357

show: 'Show',

358

create: 'Create'

359

},

360

boolean: {

361

true: 'Yes',

362

false: 'No'

363

},

364

page: {

365

list: 'List of %{name}',

366

edit: 'Edit %{name} #%{id}',

367

show: '%{name} #%{id}',

368

create: 'Create %{name}',

369

dashboard: 'Dashboard'

370

},

371

input: {

372

file: {

373

upload_several: 'Drop some files to upload, or click to select one.',

374

upload_single: 'Drop a file to upload, or click to select it.'

375

},

376

image: {

377

upload_several: 'Drop some pictures to upload, or click to select one.',

378

upload_single: 'Drop a picture to upload, or click to select it.'

379

}

380

},

381

message: {

382

yes: 'Yes',

383

no: 'No',

384

are_you_sure: 'Are you sure?',

385

about: 'About',

386

not_found: 'Either you typed a wrong URL, or you followed a bad link.'

387

},

388

navigation: {

389

no_results: 'No results found',

390

no_more_results: 'The page number %{page} is out of boundaries. Try the previous page.',

391

page_out_of_boundaries: 'Page number %{page} out of boundaries',

392

page_out_from_end: 'Cannot go after last page',

393

page_out_from_begin: 'Cannot go before page 1',

394

page_range_info: '%{offsetBegin}-%{offsetEnd} of %{total}',

395

page_rows_per_page: 'Rows per page:',

396

next: 'Next',

397

prev: 'Previous'

398

},

399

notification: {

400

updated: 'Element updated |||| %{smart_count} elements updated',

401

created: 'Element created',

402

deleted: 'Element deleted |||| %{smart_count} elements deleted',

403

bad_item: 'Incorrect element',

404

item_doesnt_exist: 'Element does not exist',

405

http_error: 'Server communication error',

406

data_provider_error: 'dataProvider error. Check the console for details.',

407

i18n_error: 'Cannot load the translations for the specified language',

408

canceled: 'Action cancelled',

409

logged_out: 'Your session has ended, please reconnect.'

410

},

411

validation: {

412

required: 'Required',

413

minLength: 'Must be %{min} characters at least',

414

maxLength: 'Must be %{max} characters or less',

415

minValue: 'Must be at least %{min}',

416

maxValue: 'Must be %{max} or less',

417

number: 'Must be a number',

418

email: 'Must be a valid email',

419

oneOf: 'Must be one of: %{options}',

420

regex: 'Must match a specific format (regexp): %{pattern}'

421

}

422

}

423

};

424

```

425

426

### Custom Message Keys

427

428

```typescript

429

// Custom application messages

430

const customMessages = {

431

// Custom page titles

432

page: {

433

analytics: 'Analytics Dashboard',

434

reports: 'Reports',

435

settings: 'Application Settings'

436

},

437

438

// Custom actions

439

action: {

440

publish: 'Publish',

441

unpublish: 'Unpublish',

442

archive: 'Archive',

443

duplicate: 'Duplicate',

444

export_pdf: 'Export to PDF'

445

},

446

447

// Custom notifications

448

notification: {

449

post_published: 'Post published successfully',

450

export_started: 'Export started. You will be notified when complete.',

451

bulk_update_success: '%{smart_count} item updated |||| %{smart_count} items updated'

452

},

453

454

// Status messages

455

status: {

456

draft: 'Draft',

457

published: 'Published',

458

archived: 'Archived',

459

pending: 'Pending Review'

460

}

461

};

462

```

463

464

## Advanced i18n Features

465

466

### Pluralization

467

468

React Admin supports pluralization using the Polyglot.js format:

469

470

```typescript

471

const messages = {

472

post: {

473

count: 'One post |||| %{smart_count} posts',

474

notification: {

475

deleted: 'Post deleted |||| %{smart_count} posts deleted'

476

}

477

}

478

};

479

480

// Usage

481

const translate = useTranslate();

482

const message1 = translate('post.count', { smart_count: 1 }); // "One post"

483

const message5 = translate('post.count', { smart_count: 5 }); // "5 posts"

484

```

485

486

### Interpolation

487

488

Variable interpolation in translations:

489

490

```typescript

491

const messages = {

492

welcome: 'Welcome back, %{name}!',

493

last_login: 'Last login: %{date} at %{time}',

494

notification: 'You have %{count} new %{type}'

495

};

496

497

// Usage

498

const translate = useTranslate();

499

const welcome = translate('welcome', { name: 'John Doe' });

500

const login = translate('last_login', {

501

date: '2024-01-15',

502

time: '14:30'

503

});

504

```

505

506

### RTL Language Support

507

508

```typescript

509

import { Admin } from 'react-admin';

510

import { createTheme } from '@mui/material/styles';

511

512

// RTL theme configuration

513

const rtlTheme = createTheme({

514

direction: 'rtl',

515

palette: {

516

primary: { main: '#1976d2' }

517

}

518

});

519

520

const App = () => {

521

const [locale, setLocale] = useState('en');

522

const theme = locale === 'ar' ? rtlTheme : defaultTheme;

523

524

return (

525

<Admin

526

theme={theme}

527

i18nProvider={i18nProvider}

528

dataProvider={dataProvider}

529

>

530

<Resource name="posts" list={PostList} />

531

</Admin>

532

);

533

};

534

```

535

536

### Dynamic Language Loading

537

538

```typescript

539

const i18nProvider = polyglotI18nProvider(

540

async (locale) => {

541

if (locale === 'en') {

542

return import('ra-language-english').then(messages => messages.default);

543

}

544

if (locale === 'fr') {

545

return import('ra-language-french').then(messages => messages.default);

546

}

547

// Fallback to English

548

return import('ra-language-english').then(messages => messages.default);

549

},

550

'en'

551

);

552

```

553

554

### Custom Translation Component

555

556

```typescript

557

import { useTranslate } from 'react-admin';

558

559

const T = ({ message, values, children, ...props }) => {

560

const translate = useTranslate();

561

const translatedText = translate(message, values);

562

563

if (children) {

564

return children(translatedText);

565

}

566

567

return <span {...props}>{translatedText}</span>;

568

};

569

570

// Usage

571

<T message="welcome.title" values={{ name: 'John' }} />

572

573

<T message="post.count" values={{ smart_count: 5 }}>

574

{(text) => <strong>{text}</strong>}

575

</T>

576

```

577

578

## Testing i18n

579

580

### Test Translation Provider

581

582

```typescript

583

import { TestTranslationProvider } from 'react-admin';

584

585

const TestComponent = () => (

586

<TestTranslationProvider messages={{ 'test.message': 'Test Message' }}>

587

<MyComponent />

588

</TestTranslationProvider>

589

);

590

```

591

592

### Mock i18n in Tests

593

594

```typescript

595

const mockTranslate = (key, options = {}) => {

596

// Simple mock that returns the key with interpolated values

597

return key.replace(/%\{(\w+)\}/g, (match, param) => options[param] || match);

598

};

599

600

jest.mock('react-admin', () => ({

601

...jest.requireActual('react-admin'),

602

useTranslate: () => mockTranslate

603

}));

604

```

605

606

## Complete i18n Example

607

608

```typescript

609

import { Admin, Resource, List, Edit, Create, SimpleForm, TextInput } from 'react-admin';

610

import polyglotI18nProvider from 'ra-i18n-polyglot';

611

import englishMessages from 'ra-language-english';

612

import frenchMessages from 'ra-language-french';

613

614

// Custom messages

615

const customEnglishMessages = {

616

...englishMessages,

617

resources: {

618

posts: {

619

name: 'Post |||| Posts',

620

fields: {

621

title: 'Title',

622

content: 'Content',

623

status: 'Publication Status'

624

}

625

}

626

},

627

custom: {

628

actions: {

629

publish: 'Publish Post',

630

schedule: 'Schedule Publication'

631

},

632

status: {

633

draft: 'Draft',

634

published: 'Published',

635

scheduled: 'Scheduled'

636

}

637

}

638

};

639

640

const customFrenchMessages = {

641

...frenchMessages,

642

resources: {

643

posts: {

644

name: 'Article |||| Articles',

645

fields: {

646

title: 'Titre',

647

content: 'Contenu',

648

status: 'Statut de publication'

649

}

650

}

651

},

652

custom: {

653

actions: {

654

publish: 'Publier l\'article',

655

schedule: 'Programmer la publication'

656

},

657

status: {

658

draft: 'Brouillon',

659

published: 'Publié',

660

scheduled: 'Programmé'

661

}

662

}

663

};

664

665

const messages = {

666

en: customEnglishMessages,

667

fr: customFrenchMessages

668

};

669

670

const i18nProvider = polyglotI18nProvider(

671

locale => messages[locale],

672

'en',

673

[

674

{ locale: 'en', name: 'English' },

675

{ locale: 'fr', name: 'Français' }

676

]

677

);

678

679

const PostEdit = () => (

680

<Edit>

681

<SimpleForm>

682

<TranslatableInputs locales={['en', 'fr']}>

683

<TextInput source="title" />

684

<TextInput source="content" multiline rows={4} />

685

</TranslatableInputs>

686

</SimpleForm>

687

</Edit>

688

);

689

690

const App = () => (

691

<Admin

692

dataProvider={dataProvider}

693

i18nProvider={i18nProvider}

694

>

695

<Resource name="posts" list={PostList} edit={PostEdit} />

696

</Admin>

697

);

698

```

699

700

React Admin's internationalization system provides comprehensive multi-language support with flexible translation management, locale-specific formatting, and robust tools for building global admin applications.