CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-admin

A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

i18n.mddocs/

Internationalization

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.

i18n Provider Interface

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

import { I18nProvider } from 'react-admin';

interface I18nProvider {
  translate: (key: string, options?: TranslateOptions) => string;
  changeLocale: (locale: string) => Promise<void>;
  getLocale: () => string;
  getLocales?: () => Locale[];
}

interface TranslateOptions {
  smart_count?: number;
  _?: string;
  [key: string]: any;
}

interface Locale {
  locale: string;
  name: string;
}

Basic i18n Provider Example

import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from 'ra-language-english';
import frenchMessages from 'ra-language-french';
import spanishMessages from 'ra-language-spanish';

const messages = {
  en: englishMessages,
  fr: frenchMessages,
  es: spanishMessages,
};

const i18nProvider = polyglotI18nProvider(
  locale => messages[locale],
  'en', // default locale
  [
    { locale: 'en', name: 'English' },
    { locale: 'fr', name: 'Français' },
    { locale: 'es', name: 'Español' }
  ]
);

// Usage in Admin
<Admin 
  dataProvider={dataProvider}
  i18nProvider={i18nProvider}
>
  <Resource name="posts" list={PostList} />
</Admin>

Translation Hooks

useTranslate

Hook for translating messages in components.

import { useTranslate } from 'react-admin';

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

const useTranslate: () => TranslateFunction;

Usage Examples

import { useTranslate } from 'react-admin';

const MyComponent = () => {
  const translate = useTranslate();
  
  return (
    <div>
      <h1>{translate('page.dashboard.title')}</h1>
      <p>{translate('page.dashboard.welcome', { name: 'John' })}</p>
      <p>{translate('post.count', { smart_count: 5 })}</p>
    </div>
  );
};

// Message files:
// en.json: { "page.dashboard.title": "Dashboard", "page.dashboard.welcome": "Welcome, %{name}!", "post.count": "One post |||| %{smart_count} posts" }
// fr.json: { "page.dashboard.title": "Tableau de bord", "page.dashboard.welcome": "Bienvenue, %{name} !", "post.count": "Un article |||| %{smart_count} articles" }

useTranslateLabel

Hook for translating field and resource labels.

import { useTranslateLabel } from 'react-admin';

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

interface TranslateLabelParams {
  source?: string;
  resource?: string;
  label?: string;
}

Usage Example

import { useTranslateLabel } from 'react-admin';

const CustomField = ({ source, resource, label }) => {
  const translateLabel = useTranslateLabel();
  
  const fieldLabel = translateLabel({ source, resource, label });
  
  return (
    <div>
      <label>{fieldLabel}</label>
      {/* field content */}
    </div>
  );
};

Locale Management

useLocale & useSetLocale

Hooks for getting and setting the current locale.

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

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

useLocales

Hook for getting available locales.

import { useLocales } from 'react-admin';

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

Locale Management Examples

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

const LanguageSwitcher = () => {
  const locale = useLocale();
  const setLocale = useSetLocale();
  const locales = useLocales();
  
  const handleLocaleChange = (newLocale) => {
    setLocale(newLocale);
  };
  
  return (
    <select value={locale} onChange={(e) => handleLocaleChange(e.target.value)}>
      {locales?.map(locale => (
        <option key={locale.locale} value={locale.locale}>
          {locale.name}
        </option>
      ))}
    </select>
  );
};

// Using LocalesMenuButton component
import { LocalesMenuButton } from 'react-admin';

const AppBar = () => (
  <AppBar>
    <TitlePortal />
    <Box flex="1" />
    <LocalesMenuButton />
    <UserMenu />
  </AppBar>
);

Translatable Content

TranslatableInputs

Component for managing translatable form inputs.

import { TranslatableInputs } from 'react-admin';

interface TranslatableInputsProps {
  locales?: string[];
  defaultLocale?: string;
  selector?: React.ComponentType<TranslatableInputsTabsProps>;
  children: React.ReactNode;
  className?: string;
  sx?: any;
}

const TranslatableInputs: React.FC<TranslatableInputsProps>;

TranslatableInputs Example

import { 
  TranslatableInputs, 
  TextInput, 
  Edit, 
  SimpleForm 
} from 'react-admin';

const PostEdit = () => (
  <Edit>
    <SimpleForm>
      <TextInput source="id" disabled />
      
      <TranslatableInputs locales={['en', 'fr', 'es']}>
        <TextInput source="title" validate={required()} />
        <TextInput 
          source="content" 
          multiline 
          rows={4} 
          validate={required()} 
        />
        <TextInput source="metaDescription" />
      </TranslatableInputs>
      
      <DateInput source="publishedAt" />
    </SimpleForm>
  </Edit>
);

// Data structure:
// {
//   id: 1,
//   title: { en: "Hello", fr: "Bonjour", es: "Hola" },
//   content: { en: "Content", fr: "Contenu", es: "Contenido" },
//   publishedAt: "2024-01-15"
// }

TranslatableFields

Component for displaying translatable content in show/list views.

import { TranslatableFields } from 'react-admin';

interface TranslatableFieldsProps {
  locales?: string[];
  selector?: React.ComponentType<TranslatableFieldsTabsProps>;
  children: React.ReactNode;
  className?: string;
  sx?: any;
}

const TranslatableFields: React.FC<TranslatableFieldsProps>;

TranslatableFields Example

import { 
  TranslatableFields, 
  TextField, 
  Show, 
  SimpleShowLayout 
} from 'react-admin';

const PostShow = () => (
  <Show>
    <SimpleShowLayout>
      <TextField source="id" />
      
      <TranslatableFields locales={['en', 'fr', 'es']}>
        <TextField source="title" />
        <TextField source="content" />
        <TextField source="metaDescription" />
      </TranslatableFields>
      
      <DateField source="publishedAt" />
    </SimpleShowLayout>
  </Show>
);

Translatable Context and Hooks

import { 
  useTranslatable, 
  useTranslatableContext,
  TranslatableContext,
  TranslatableContextProvider 
} from 'react-admin';

interface TranslatableContextValue {
  locale: string;
  locales: string[];
  selectLocale: (locale: string) => void;
  getSource: (source: string) => string;
  getLabel: (source: string) => string;
}

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

Message Structure and Patterns

Resource and Field Labels

React Admin follows specific conventions for translation keys:

// Translation message structure
const messages = {
  resources: {
    posts: {
      name: 'Post |||| Posts',
      fields: {
        title: 'Title',
        content: 'Content',
        publishedAt: 'Published At',
        author: 'Author'
      }
    },
    users: {
      name: 'User |||| Users', 
      fields: {
        firstName: 'First Name',
        lastName: 'Last Name',
        email: 'Email Address'
      }
    }
  },
  ra: {
    action: {
      save: 'Save',
      delete: 'Delete',
      cancel: 'Cancel',
      edit: 'Edit',
      show: 'Show',
      create: 'Create'
    },
    boolean: {
      true: 'Yes',
      false: 'No'
    },
    page: {
      list: 'List of %{name}',
      edit: 'Edit %{name} #%{id}',
      show: '%{name} #%{id}',
      create: 'Create %{name}',
      dashboard: 'Dashboard'
    },
    input: {
      file: {
        upload_several: 'Drop some files to upload, or click to select one.',
        upload_single: 'Drop a file to upload, or click to select it.'
      },
      image: {
        upload_several: 'Drop some pictures to upload, or click to select one.',
        upload_single: 'Drop a picture to upload, or click to select it.'
      }
    },
    message: {
      yes: 'Yes',
      no: 'No',
      are_you_sure: 'Are you sure?',
      about: 'About',
      not_found: 'Either you typed a wrong URL, or you followed a bad link.'
    },
    navigation: {
      no_results: 'No results found',
      no_more_results: 'The page number %{page} is out of boundaries. Try the previous page.',
      page_out_of_boundaries: 'Page number %{page} out of boundaries',
      page_out_from_end: 'Cannot go after last page',
      page_out_from_begin: 'Cannot go before page 1',
      page_range_info: '%{offsetBegin}-%{offsetEnd} of %{total}',
      page_rows_per_page: 'Rows per page:',
      next: 'Next',
      prev: 'Previous'
    },
    notification: {
      updated: 'Element updated |||| %{smart_count} elements updated',
      created: 'Element created',
      deleted: 'Element deleted |||| %{smart_count} elements deleted',
      bad_item: 'Incorrect element',
      item_doesnt_exist: 'Element does not exist',
      http_error: 'Server communication error',
      data_provider_error: 'dataProvider error. Check the console for details.',
      i18n_error: 'Cannot load the translations for the specified language',
      canceled: 'Action cancelled',
      logged_out: 'Your session has ended, please reconnect.'
    },
    validation: {
      required: 'Required',
      minLength: 'Must be %{min} characters at least',
      maxLength: 'Must be %{max} characters or less',
      minValue: 'Must be at least %{min}',
      maxValue: 'Must be %{max} or less',
      number: 'Must be a number',
      email: 'Must be a valid email',
      oneOf: 'Must be one of: %{options}',
      regex: 'Must match a specific format (regexp): %{pattern}'
    }
  }
};

Custom Message Keys

// Custom application messages
const customMessages = {
  // Custom page titles
  page: {
    analytics: 'Analytics Dashboard',
    reports: 'Reports',
    settings: 'Application Settings'
  },
  
  // Custom actions
  action: {
    publish: 'Publish',
    unpublish: 'Unpublish',
    archive: 'Archive',
    duplicate: 'Duplicate',
    export_pdf: 'Export to PDF'
  },
  
  // Custom notifications
  notification: {
    post_published: 'Post published successfully',
    export_started: 'Export started. You will be notified when complete.',
    bulk_update_success: '%{smart_count} item updated |||| %{smart_count} items updated'
  },
  
  // Status messages
  status: {
    draft: 'Draft',
    published: 'Published', 
    archived: 'Archived',
    pending: 'Pending Review'
  }
};

Advanced i18n Features

Pluralization

React Admin supports pluralization using the Polyglot.js format:

const messages = {
  post: {
    count: 'One post |||| %{smart_count} posts',
    notification: {
      deleted: 'Post deleted |||| %{smart_count} posts deleted'
    }
  }
};

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

Interpolation

Variable interpolation in translations:

const messages = {
  welcome: 'Welcome back, %{name}!',
  last_login: 'Last login: %{date} at %{time}',
  notification: 'You have %{count} new %{type}'
};

// Usage
const translate = useTranslate();
const welcome = translate('welcome', { name: 'John Doe' });
const login = translate('last_login', { 
  date: '2024-01-15', 
  time: '14:30' 
});

RTL Language Support

import { Admin } from 'react-admin';
import { createTheme } from '@mui/material/styles';

// RTL theme configuration
const rtlTheme = createTheme({
  direction: 'rtl',
  palette: {
    primary: { main: '#1976d2' }
  }
});

const App = () => {
  const [locale, setLocale] = useState('en');
  const theme = locale === 'ar' ? rtlTheme : defaultTheme;
  
  return (
    <Admin
      theme={theme}
      i18nProvider={i18nProvider}
      dataProvider={dataProvider}
    >
      <Resource name="posts" list={PostList} />
    </Admin>
  );
};

Dynamic Language Loading

const i18nProvider = polyglotI18nProvider(
  async (locale) => {
    if (locale === 'en') {
      return import('ra-language-english').then(messages => messages.default);
    }
    if (locale === 'fr') {
      return import('ra-language-french').then(messages => messages.default);
    }
    // Fallback to English
    return import('ra-language-english').then(messages => messages.default);
  },
  'en'
);

Custom Translation Component

import { useTranslate } from 'react-admin';

const T = ({ message, values, children, ...props }) => {
  const translate = useTranslate();
  const translatedText = translate(message, values);
  
  if (children) {
    return children(translatedText);
  }
  
  return <span {...props}>{translatedText}</span>;
};

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

<T message="post.count" values={{ smart_count: 5 }}>
  {(text) => <strong>{text}</strong>}
</T>

Testing i18n

Test Translation Provider

import { TestTranslationProvider } from 'react-admin';

const TestComponent = () => (
  <TestTranslationProvider messages={{ 'test.message': 'Test Message' }}>
    <MyComponent />
  </TestTranslationProvider>
);

Mock i18n in Tests

const mockTranslate = (key, options = {}) => {
  // Simple mock that returns the key with interpolated values
  return key.replace(/%\{(\w+)\}/g, (match, param) => options[param] || match);
};

jest.mock('react-admin', () => ({
  ...jest.requireActual('react-admin'),
  useTranslate: () => mockTranslate
}));

Complete i18n Example

import { Admin, Resource, List, Edit, Create, SimpleForm, TextInput } from 'react-admin';
import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from 'ra-language-english';
import frenchMessages from 'ra-language-french';

// Custom messages
const customEnglishMessages = {
  ...englishMessages,
  resources: {
    posts: {
      name: 'Post |||| Posts',
      fields: {
        title: 'Title',
        content: 'Content',
        status: 'Publication Status'
      }
    }
  },
  custom: {
    actions: {
      publish: 'Publish Post',
      schedule: 'Schedule Publication'
    },
    status: {
      draft: 'Draft',
      published: 'Published',
      scheduled: 'Scheduled'
    }
  }
};

const customFrenchMessages = {
  ...frenchMessages,
  resources: {
    posts: {
      name: 'Article |||| Articles',
      fields: {
        title: 'Titre',
        content: 'Contenu',
        status: 'Statut de publication'
      }
    }
  },
  custom: {
    actions: {
      publish: 'Publier l\'article',
      schedule: 'Programmer la publication'
    },
    status: {
      draft: 'Brouillon',
      published: 'Publié',
      scheduled: 'Programmé'
    }
  }
};

const messages = {
  en: customEnglishMessages,
  fr: customFrenchMessages
};

const i18nProvider = polyglotI18nProvider(
  locale => messages[locale],
  'en',
  [
    { locale: 'en', name: 'English' },
    { locale: 'fr', name: 'Français' }
  ]
);

const PostEdit = () => (
  <Edit>
    <SimpleForm>
      <TranslatableInputs locales={['en', 'fr']}>
        <TextInput source="title" />
        <TextInput source="content" multiline rows={4} />
      </TranslatableInputs>
    </SimpleForm>
  </Edit>
);

const App = () => (
  <Admin
    dataProvider={dataProvider}
    i18nProvider={i18nProvider}
  >
    <Resource name="posts" list={PostList} edit={PostEdit} />
  </Admin>
);

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.

Install with Tessl CLI

npx tessl i tessl/npm-react-admin

docs

admin-core.md

advanced.md

auth.md

data-management.md

detail-views.md

forms-inputs.md

i18n.md

index.md

layout-navigation.md

lists-data-display.md

ui-components.md

tile.json