@sheet-i18n/react
TypeScript icon, indicating that this package has built-in type declarations

1.4.1 • Public • Published

@sheet-i18n/react ✨

npm package

The client-side i18n library subpackage of sheet-i18n.

This package provides tools to handle translations in React applications using context and hooks. It simplifies internationalization workflows by offering functions and components to manage, access, and use locale-specific translation data.

✨ Package Introduction

  • I18nStore: Core Store Creation Class for managing translation data.
  • createI18nContext: React Context to generate providers and hooks for translation.
  • IntlProvider: React Translation Provider for managing current locale.
  • useTranslation: Client Side Translation Hook for easy access to translation messages on the client side.
  • getTranslation: Static Translation Function for easy access to translation messages on Static module files.

🚀 Getting Started(Manually)

Strongly recommended to use the init CLI for setup 👉 Please follow the Init CLI section

If you don't want to use the CLI, you can follow the Manual Setup below.

step 1. Define Translation Data

Prepare locale JSON files:

// en.json
{
  "header": {
    "login": "Login",
    "logout": "Logout"
  }
}

// ko.json
{
  "header": {
    "login": "로그인",
    "logout": "로그아웃"
  }
}

step 2. Initialize i18nStore

this store will be used as a core translations module.

import en from './en.json';
import ko from './ko.json';

import { I18nStore } from '@sheet-i18n/react';

export const i18nStore = new I18nStore({
  supportedLocales: ['ko', 'en'],
  defaultLocale: 'ko',

  /** if you want to load translation data statically */
  localeSet: {
    ko,
    en,
  },

  /** if you want to load translation data dynamically */
  // dynamicLoaders: {
  //   ko: () => import('./ko.json'),
  //   en: () => import('./en.json'),
  //   jp: () => import('./jp.json'),
  //   cn: () => import('./cn.json'),
  // },
});

step 3. Create i18n Context

import { i18nStore } from './file-path-of-i18nStore-initiated';
import { createI18nContext } from '@sheet-i18n/react';

export const { IntlProvider, useTranslation, getTranslation } =
  createI18nContext(i18nStore);

step 4. Mount Intl Context Provider in your App

import React from 'react';
import { IntlProvider } from './i18nContext';

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

  return (
    <IntlProvider currentLocale={locale}>
      <YourComponent />
    </IntlProvider>
  );
};

step 5. Use Translations

import React from 'react';
import { useTranslation } from './i18nContext';

const YourComponent = () => {
  const { t } = useTranslation('header');

  return (
    <div>
      <button>{t('login')}</button>
      <button>{t('logout')}</button>
    </div>
  );
};

📦 Base API Reference

I18nStore(core)

The I18nStore manages translation states, ensuring consistency across locales.

Parameters:

🧠 I18nStore Configuration Options

Option Type Required Description
supportedLocales string[] List of supported locale codes.
defaultLocale string Default locale to use when no match is found. Must be included in supportedLocales.
localeSet Record<string, object> (Static Loading Option) Preload all translation data for each locale in memory. Keys must match supportedLocales.
dynamicLoaders Record<string, () => Promise> (Recommended for large locale sets) Dynamically load translation data on demand, reducing initial bundle size.
typeSafe boolean Enable strict key checking and autocompletion (default: false).

💡 typeSafe?
I18nStore doesn't enforce adherence to your locale JSON definitions by default. This means that you can add translation data even if it isn’t pre-defined in your locale JSON files. However, if you prefer to enforce strict type-safety, you can manually enable the typeSafe option which allows you to notice the auto-completed list in translation data.

But to enable correct type checking, the full localeSet must be defined according to the supportedLocales in i18nStore

As a result, type auto-completion relies on having the complete localeSet defined at initialization. This means that using dynamicLoaders to fetch locales conditionally at runtime can be limiting when strict type-safety is enabled.

// typeSafe: true
const YourComponent = () => {
  // "useTranslation" shows the autocompletion suggestions
  const { t } = useTranslation('header');

  return (
    <div>
      {/* "t" function shows the autocompletion suggestions */}
      <button>{t('login')}</button>
    </div>
  );
};

⚠️ Caveats:

  1. supportedLocales must be an array of locale strings.
  2. defaultLocale must exist in supportedLocales.

🚀 Static vs Dynamic Loading

You can configure how translations are loaded:

  • Static (localeSet)
    Load all translations at once. Ideal for small projects or limited locale sets.

  • Dynamic (dynamicLoaders)Recommended
    Load only the locale needed, reducing the bundle size dramatically.
    Useful when supporting many languages or large translation files.

export const i18nStore = new I18nStore({
  supportedLocales: ['en', 'fr', 'ko'],
  defaultLocale: 'en',
  dynamicLoaders: {
    en: () => import('./en.json'),
    fr: () => import('./fr.json'),
    ko: () => import('./ko.json'),
  },
});

💡 When both localeSet and dynamicLoaders are defined, sheet-i18n automatically merges both sources — static data loads immediately, while dynamic data supplements on-demand.


🎯 Why Dynamic Loaders?

For projects with many locale datasets, it's often preferable to load translation data on demand rather than all at once. It is better using dynamicLoaders to enable this conditional loading strategy.

  • ✅ Reduces the initial JavaScript bundle size.
  • ✅ Keeps only required locales in memory.
  • ✅ Works well with code-splitting and SSR/CSR.
  • ✅ Enables lazy-loading for translations.

Example:

export const i18nStore = new I18nStore({
  supportedLocales: ['ko', 'en'],
  defaultLocale: 'ko',
  /** if you want to load translation data statically */
  localeSet: {
    ko,
    en,
  },

  /** if you want to load translation data dynamically */
  // dynamicLoaders: {
  //   ko: () => import('./ko.json'),
  //   en: () => import('./en.json'),
  //   jp: () => import('./jp.json'),
  //   cn: () => import('./cn.json'),
  // },
});

createI18nContext

Generates React context, including the IntlProvider and useTranslation.

Parameters:

  • i18nStore: Instance of I18nStore.

⚠️ Caveats:

  1. i18nStore passed to createI18nContext must be an instance of I18nStore.
  2. custom object is not allowed to be passed to createI18nContext.

Example:

const { IntlProvider, useTranslation } = createI18nContext(i18nStore);

IntlProvider

A Context provider to provide translations to child components.

Properties:

  • currentLocale (optional):

    • A key representing the current locale to use for translations.
    • If not provided, the user's preferred language is automatically detected based on the browser setting.
    • The fallback is the default locale in i18nStore if the detected user-preferred language is not unsupported in the supportedLocales.
  • children: React children.

Example:

// Add currentLocale if you want to manually handle the locale

// This example is Next.js app routing
interface LayoutProps {
  params: {
    locale: Locale;
  };
}

export default function Layout({
  children,
  params,
}: PropsWithChildren<PageProps>) {
  return <IntlProvider currentLocale={params.locale}>{children}</IntlProvider>;
}

useTranslation

A hook to access translations in your components.

Parameters:

  • sheetTitle: The sheet title of the translation group.

Example:

const { t } = useTranslation('header');
const translatedMessage = t('login');

getTranslation

A function to access translations in the environment where cannot use react context and hooks.

Parameters:

  • sheetTitle: The sheet title of the translation group.

t Function

The t function is used to retrieve a translation string based on a key and optionally interpolate variables. It provides flexibility to include dynamic values, such as plain strings or React components, for enhanced localization capabilities.

Parameters:

  • key (string): The translation key to retrieve the localized string.
const translatedMessage = t('login'); // login is key
  • values (object, optional): An object containing key-value pairs to interpolate into the translation string.
// John Doe shown
const translatedMessage = t('{username} shown', { username: 'John Doe' });

💡 Note: The values object can contain any type of data, including React components.

// <Username /> shown
const translatedMessage = t('{username} shown', { username: <Username /> });

📄 Best Practices of Translation utilities

A robust and flexible translation library designed for efficient handling of translations in React applications.

Features

  • Client-Side Translation (Explicit Strings): Translate explicit string keys directly in your components.
  • Client-Side Translation (Dynamic Strings): Dynamically match translations from JSON for unknown variables.
  • Module Translation: Use translations in the place where the react context or hooks cannot be used.

Usage

1. Client-Side Translation (Explicit Strings)

Retrieve translations for explicit keys in your React components using useTranslation.

import React from 'react';
import { useTranslation } from './i18nContext';

const YourComponent = () => {
  const { t } = useTranslation('header');

  return (
    <div>
      <button>{t('login')}</button>
      <button>{t('logout')}</button>
    </div>
  );
};

2. Client-Side Translation (Dynamic Strings)

For scenarios where the string key is not explicitly known (e.g., coming from a server), use t.dynamic. Ensure that your translation JSON and sheet are updated to include the variable values. The t.dynamic function will automatically match the values from the JSON with the provided arguments and display the result.

// App.tsx
import { useTranslation } from './i18nContext';

const { t } = useTranslation('header');
const { data } = useQuery(...queryOptions);

const deviceName = data?.device?.name;

return <div>{t.dynamic(deviceName)}</div>;

3. Module Translation

The getTranslation function allows you to use translations outside of React components, such as in static configuration files, constants, or utility modules.

It provide two possible supports

  1. t (Synchronous Translation) – Use when translation values are available at runtime.
  2. t.promise (Asynchronous Translation) – Use when the module’s evaluation order is uncertain.

✅ Usage Scenarios

[Scenario 1]: Context where the call expression of t function is evaluated at runtime

  • For the common example, t call expression in a function module
  • This behaves the same as useTranslation's t function.
Example
// module.ts
import { getTranslation } from './i18nContext';

const { t } = getTranslation('header');

export function getStatusCategory {
  return [t('Total Energy Usage'), t('Total Waste Usage')];
};

// App.tsx
// the "t" call expression is evaluated at function call timing
import { getStatusCategory } from './module';

export default function App() {
  return getStatusCategory().map((item) => <div>{item}</div>);
}

Result: The translation is resolved immediately during runtime.

[Scenario 2]: Context where the translation value is evaluated immediately

  • If the module is imported dynamically in a client-side component, the evaluation order of getTranslation and t call expression may not be guaranteed.
  • Since JavaScript evaluates modules at import time, it may attempt to access translation values before IntlProvider has fully initialized the locale.
  • In this case, use t.promise to ensure the translation resolves asynchronously.

Example

// module.ts
// The "t" result in the array will be resolved asynchronously
import { getTranslation } from './i18nContext';

const { t } = getTranslation('header');

export const STATUS_CATEGORY = [
  t.promise('Total Energy Usage'),
  t.promise('Total Waste Usage'),
];

// App.tsx
// So, t.promise ensure the current client-side locale data
// is fully initialized before the translations are resolved
import { STATUS_CATEGORY } from './module.ts';

export default function App() {
  return (
    <div>
      {STATUS_CATEGORY.map((item, index) => {
        return <div key={index}>{item}</div>;
      })}
    </div>
  );
}

License 📜

This project is licensed under the ISC License. See the LICENSE file for details.

Author ✍️

Created by devAnderson.

Readme

Keywords

Package Sidebar

Install

npm i @sheet-i18n/react

Weekly Downloads

160

Version

1.4.1

License

ISC

Unpacked Size

16.5 kB

Total Files

6

Last publish

Collaborators

  • devandersonchoi