npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ttoss/react-i18n

v2.0.26

Published

**@ttoss/react-i18n** is a React internationalization library built on [FormatJS](https://formatjs.io/) that seamlessly integrates with the ttoss ecosystem. It provides component-level i18n capabilities for translating text elements like buttons, labels,

Readme

@ttoss/react-i18n

@ttoss/react-i18n is a React internationalization library built on FormatJS that seamlessly integrates with the ttoss ecosystem. It provides component-level i18n capabilities for translating text elements like buttons, labels, and headings, making your React applications accessible to global audiences.

Key Features

  • FormatJS Integration: Built on industry-standard FormatJS with full ICU message format support
  • Dynamic Locale Loading: Async loading of translation files with automatic caching
  • TypeScript Support: Full TypeScript definitions for type-safe internationalization
  • Developer Experience: Simple hooks and components for easy integration
  • ttoss Ecosystem: Works seamlessly with other ttoss packages and tooling
  • Performance Optimized: Efficient message loading and rendering with minimal overhead

When to Use

Choose @ttoss/react-i18n for component-level internationalization when you need to:

  • Translate UI text elements (buttons, labels, form fields, notifications)
  • Support multiple languages within React components
  • Implement user language switching functionality
  • Handle pluralization, number formatting, and date localization

For routing-level internationalization (SEO, CMS content, URL localization), consider Next.js built-in i18n features alongside this library.

:::note Declare your messages following FormatJS message declaration guidelines for optimal extraction and compilation. :::

Installation

pnpm add @ttoss/react-i18n
pnpm add -D @ttoss/i18n-cli

The @ttoss/i18n-cli package handles message extraction and compilation. See the i18n-cli documentation for complete workflow setup.

Quick Start

1. Provider Setup

Wrap your application with I18nProvider and configure locale data loading:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { I18nProvider, LoadLocaleData } from '@ttoss/react-i18n';
import App from './App';

const loadLocaleData: LoadLocaleData = async (locale) => {
  switch (locale) {
    case 'pt-BR':
      return (await import('../i18n/compiled/pt-BR.json')).default;
    case 'es':
      return (await import('../i18n/compiled/es.json')).default;
    default:
      return (await import('../i18n/compiled/en.json')).default;
  }
};

ReactDOM.createRoot(document.getElementById('root')!).render(
  <I18nProvider locale={navigator.language} loadLocaleData={loadLocaleData}>
    <App />
  </I18nProvider>
);

2. Component Usage

Use the useI18n hook to access internationalization features:

import React, { useState } from 'react';
import { useI18n } from '@ttoss/react-i18n';

export default function App() {
  const { intl, setLocale, locale } = useI18n();
  const [name, setName] = useState('User');

  const toggleLanguage = () => {
    setLocale(locale === 'en' ? 'pt-BR' : 'en');
  };

  return (
    <div>
      <h1>
        {intl.formatMessage(
          {
            description: 'Welcome message',
            defaultMessage: 'Welcome, {name}!',
          },
          { name }
        )}
      </h1>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />
      <button onClick={toggleLanguage}>
        {intl.formatMessage({
          description: 'Change language button',
          defaultMessage: 'Change Language',
        })}
      </button>
      <p>Current locale: {locale}</p>
    </div>
  );
}

Framework Integration

Vite Configuration

Configure Vite to properly handle message extraction. Choose between Babel or SWC based on your setup:

Option 1: Using Babel (with @ttoss/config)

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { babelConfig } from '@ttoss/config';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: babelConfig().plugins,
      },
    }),
  ],
});

Option 2: Using Babel (manual configuration)

First, install the Babel plugin:

pnpm add -D babel-plugin-formatjs

Then configure Vite:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'formatjs',
            {
              idInterpolationPattern: '[sha512:contenthash:base64:6]',
              ast: true,
            },
          ],
        ],
      },
    }),
  ],
});

Option 3: Using SWC

First, install the SWC plugin:

pnpm add -D @swc/plugin-formatjs

Then configure Vite with SWC:

import react from '@vitejs/plugin-react-swc';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    react({
      plugins: [
        [
          '@swc/plugin-formatjs',
          {
            idInterpolationPattern: '[sha512:contenthash:base64:6]',
            ast: true,
          },
        ],
      ],
    }),
  ],
});

Configuration Options

  • idInterpolationPattern: Generates unique, deterministic IDs for messages using content hash
  • ast: Enables AST-based extraction for better performance and accuracy

For more configuration options, see:

Next.js Setup

For Next.js applications, configure the provider in _app.tsx:

import { I18nProvider, LoadLocaleData } from '@ttoss/react-i18n';
import { useRouter } from 'next/router';
import type { AppProps } from 'next/app';

const loadLocaleData: LoadLocaleData = async (locale) => {
  switch (locale) {
    case 'pt-BR':
      return (await import('../../i18n/compiled/pt-BR.json')).default;
    case 'es':
      return (await import('../../i18n/compiled/es.json')).default;
    default:
      return (await import('../../i18n/compiled/en.json')).default;
  }
};

export default function MyApp({ Component, pageProps }: AppProps) {
  const { locale } = useRouter();

  return (
    <I18nProvider locale={locale} loadLocaleData={loadLocaleData}>
      <Component {...pageProps} />
    </I18nProvider>
  );
}

Advanced Usage

FormattedMessage Component

Use FormattedMessage for declarative message rendering:

import { FormattedMessage } from '@ttoss/react-i18n';

function UserStats({ userCount, lastSeen }) {
  return (
    <div>
      <FormattedMessage
        description="Number of users"
        defaultMessage="{count, plural, =0 {No users} =1 {One user} other {# users}}"
        values={{ count: userCount }}
      />
      <FormattedMessage
        description="Last seen timestamp"
        defaultMessage="Last seen: {timestamp, date, short}"
        values={{ timestamp: lastSeen }}
      />
    </div>
  );
}

:::warning Library Development When creating reusable libraries that use this package, prefer intl.formatMessage over FormattedMessage component. During the build process, FormattedMessage gets transformed into JSX syntax like return /* @__PURE__ */jsx(FormattedMessage, { defaultMessage: "..." }), which prevents the message extraction tool from properly detecting and generating IDs for the messages.

// ✅ Good for libraries - extracts correctly
function MyLibraryComponent() {
  const { intl } = useI18n();
  return (
    <div>
      {intl.formatMessage({
        description: 'Library message',
        defaultMessage: 'This message will be extracted correctly',
      })}
    </div>
  );
}

// ❌ Avoid in libraries - extraction may fail
function MyLibraryComponent() {
  return (
    <FormattedMessage
      description="Library message"
      defaultMessage="This may not extract properly in built libraries"
    />
  );
}

:::

Error Handling

Handle translation errors gracefully:

import { I18nProvider } from '@ttoss/react-i18n';

function App() {
  const handleTranslationError = (error: Error) => {
    console.warn('Translation error:', error.message);
    // Log to error tracking service
  };

  return (
    <I18nProvider
      locale="en"
      loadLocaleData={loadLocaleData}
      onError={handleTranslationError}
    >
      <MyApp />
    </I18nProvider>
  );
}

Rich Text Formatting

Format messages with embedded components:

import { useI18n } from '@ttoss/react-i18n';

function SignupForm() {
  const { intl } = useI18n();

  return (
    <p>
      {intl.formatMessage(
        {
          description: 'Terms and conditions',
          defaultMessage:
            'By signing up, you agree to our {termsLink} and {privacyLink}.',
        },
        {
          termsLink: <a href="/terms">Terms of Service</a>,
          privacyLink: <a href="/privacy">Privacy Policy</a>,
        }
      )}
    </p>
  );
}

Dynamic Locale Loading

Load locales based on user preferences:

import { useI18n } from '@ttoss/react-i18n';

function LanguageSelector() {
  const { setLocale, locale } = useI18n();

  const languages = [
    { code: 'en', name: 'English' },
    { code: 'pt-BR', name: 'Português' },
    { code: 'es', name: 'Español' },
  ];

  const handleLanguageChange = async (newLocale: string) => {
    // Persist user choice
    localStorage.setItem('preferredLocale', newLocale);
    setLocale(newLocale);
  };

  return (
    <select
      value={locale}
      onChange={(e) => handleLanguageChange(e.target.value)}
    >
      {languages.map((lang) => (
        <option key={lang.code} value={lang.code}>
          {lang.name}
        </option>
      ))}
    </select>
  );
}

API Reference

I18nProvider

Main provider component that configures internationalization context.

Props:

  • locale?: string - Initial locale (defaults to browser language)
  • loadLocaleData?: LoadLocaleData - Function to load translation data
  • onError?: (error: Error) => void - Error handler for translation issues
  • children: ReactNode - Child components
type LoadLocaleData = (locale: string) => Promise<Messages> | Messages;
type Messages = Record<string, string> | Record<string, MessageFormatElement[]>;

useI18n Hook

Returns internationalization utilities and state.

Returns:

{
  intl: IntlShape;           // FormatJS intl object
  locale: string;            // Current locale
  defaultLocale: string;     // Default locale ('en')
  setLocale: (locale: string) => void; // Change locale function
  messages?: Messages;       // Current translation messages
}

Example:

const { intl, locale, setLocale } = useI18n();

// Format messages
const greeting = intl.formatMessage({
  description: 'Simple greeting',
  defaultMessage: 'Hello!',
});

// Format with variables
const welcome = intl.formatMessage(
  {
    description: 'Welcome message with name',
    defaultMessage: 'Welcome, {name}!',
  },
  { name: 'John' }
);

// Format numbers and dates
const price = intl.formatNumber(29.99, { style: 'currency', currency: 'USD' });
const date = intl.formatDate(new Date(), { dateStyle: 'medium' });

defineMessages / defineMessage

Type-safe message definition utilities from FormatJS.

import { defineMessages, defineMessage } from '@ttoss/react-i18n';

// Single message (recommended pattern)
const errorMessage = defineMessage({
  description: 'Generic error message',
  defaultMessage: 'Something went wrong. Please try again.',
});

// Multiple messages (alternative approach)
const messages = defineMessages({
  title: {
    description: 'Page title',
    defaultMessage: 'My Application',
  },
  subtitle: {
    description: 'Page subtitle',
    defaultMessage: 'Welcome to our platform',
  },
});

// Usage in component
function MyComponent() {
  const { intl } = useI18n();
  return <div>{intl.formatMessage(errorMessage)}</div>;
}

:::note The current ttoss pattern is to define messages inline within components rather than using defineMessages. This approach provides better co-location and reduces the need for separate message definitions. :::

FormattedMessage

Component for declarative message rendering.

Props:

  • id?: string - Message ID (auto-generated if using defineMessage)
  • defaultMessage: string - Default message text
  • description?: string - Message description for translators
  • values?: Record<string, any> - Interpolation values
<FormattedMessage
  description="Item count message"
  defaultMessage="You have {count, plural, =0 {no items} =1 {one item} other {# items}}"
  values={{ count: itemCount }}
/>

Best Practices

Message Organization

Structure your messages for maintainability:

// Inline messages with components (recommended ttoss pattern)
function AuthComponent() {
  const { intl } = useI18n();

  return (
    <div>
      <h1>
        {intl.formatMessage({
          description: 'Login page title',
          defaultMessage: 'Sign In',
        })}
      </h1>
      <button>
        {intl.formatMessage({
          description: 'Login submit button',
          defaultMessage: 'Log In',
        })}
      </button>
      <a href="/forgot-password">
        {intl.formatMessage({
          description: 'Forgot password link',
          defaultMessage: 'Forgot your password?',
        })}
      </a>
    </div>
  );
}

// For reusable messages across components, you can still use defineMessage
import { defineMessage } from '@ttoss/react-i18n';

const successMessage = defineMessage({
  description: 'Success message when user profile is updated',
  defaultMessage: 'Your profile has been successfully updated.',
});

function ProfileForm() {
  const { intl } = useI18n();

  const handleSuccess = () => {
    toast.success(intl.formatMessage(successMessage));
  };

  // ... rest of component
}

Performance Optimization

Optimize loading and rendering:

// Lazy load locale data to reduce initial bundle size
const loadLocaleData: LoadLocaleData = async (locale) => {
  const localeModule = await import(`../i18n/compiled/${locale}.json`);
  return localeModule.default;
};

// Use React.memo for components with many translated strings
const TranslatedComponent = React.memo(function TranslatedComponent({ data }) {
  const { intl } = useI18n();

  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>
          {intl.formatMessage(
            {
              description: 'Item title in list',
              defaultMessage: 'Title: {title}',
            },
            { title: item.title }
          )}
        </div>
      ))}
    </div>
  );
});

Locale Detection

Implement intelligent locale detection:

function getInitialLocale(): string {
  // 1. Check user preference from localStorage
  const saved = localStorage.getItem('preferredLocale');
  if (saved) return saved;

  // 2. Check URL parameters
  const urlParams = new URLSearchParams(window.location.search);
  const urlLocale = urlParams.get('locale');
  if (urlLocale) return urlLocale;

  // 3. Use browser language with fallback
  const browserLocale = navigator.language;
  const supportedLocales = ['en', 'pt-BR', 'es'];

  return supportedLocales.includes(browserLocale) ? browserLocale : 'en';
}

Troubleshooting

Common Issues

Missing translation warnings:

MessageError: MISSING_TRANSLATION
  • Ensure all locales have corresponding translation files
  • Check that message IDs match between code and translation files
  • Use onError prop to handle gracefully

Babel plugin not working:

Messages not being extracted during build
  • Verify @ttoss/config babel plugins are configured correctly
  • Check that message definitions use inline intl.formatMessage calls
  • Ensure babel configuration is applied to your build process

Locale not switching:

setLocale called but UI doesn't update
  • Verify loadLocaleData function returns correct translation data
  • Check browser console for loading errors
  • Ensure I18nProvider is at the correct level in component tree

Development Tips

Enable verbose logging during development:

const loadLocaleData: LoadLocaleData = async (locale) => {
  console.log(`Loading locale: ${locale}`);
  try {
    const data = (await import(`../i18n/compiled/${locale}.json`)).default;
    console.log(`Loaded ${Object.keys(data).length} messages for ${locale}`);
    return data;
  } catch (error) {
    console.error(`Failed to load locale ${locale}:`, error);
    throw error;
  }
};

Related Resources

ttoss Ecosystem

  • @ttoss/i18n-cli - Extract and compile translations using FormatJS workflow
  • @ttoss/forms - Form components with built-in i18n support
  • @ttoss/ui - UI components that work seamlessly with react-i18n

External Resources

This library enables efficient internationalization following FormatJS standards while integrating seamlessly with the ttoss development ecosystem. For complete workflow setup including message extraction and compilation, see the @ttoss/i18n-cli documentation.