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

@i18n-bakery/react

v1.0.9

Published

The React Waiter. Delivers freshly baked translations right to your React components' table. Impeccable service with hooks and context.

Downloads

178

Readme

⚛️ @i18n-bakery/react

"The React Waiter" - Delivers freshly baked translations right to your React components' table. Impeccable service with hooks and context.

React integration for i18n-bakery - bringing the power of type-safe, auto-baking translations to your React applications. Built with modern React patterns (hooks, context) and designed for both simplicity and performance.

npm version License: MIT


npm version License: MIT


📦 Installation

npm install @i18n-bakery/core @i18n-bakery/react
# or
pnpm add @i18n-bakery/core @i18n-bakery/react
# or
yarn add @i18n-bakery/core @i18n-bakery/react

Note: You need both @i18n-bakery/core and @i18n-bakery/react installed.


🚀 Quick Start

1. Initialize i18n (Preheat the Oven)

// src/i18n.ts
import { initI18n } from "@i18n-bakery/core";

export const i18n = initI18n({
  locale: "en",
  fallbackLocale: "en",
  loader: async (locale, namespace) => {
    return import(`./locales/${locale}/${namespace}.json`);
  },
});

2. Wrap Your App (The Provider)

// src/App.tsx
import { I18nProvider } from "@i18n-bakery/react";
import { i18n } from "./i18n";

function App() {
  return (
    <I18nProvider locale="en">
      <YourComponents />
    </I18nProvider>
  );
}

export default App;

3. Use Translations (Serve Fresh)

// src/components/HomePage.tsx
import { useTranslation } from "@i18n-bakery/react";

function HomePage() {
  const t = useTranslation();

  return (
    <div>
      <h1>{t("home.title", "Welcome to our bakery!")}</h1>
      <p>{t("home.subtitle", "Fresh translations daily")}</p>
    </div>
  );
}

🎯 Core Features

1. useTranslation Hook (The Main Ingredient)

The primary hook for translating content in your components:

import { useTranslation } from "@i18n-bakery/react";

function MyComponent() {
  const t = useTranslation();

  return (
    <div>
      {/* Simple translation */}
      <h1>{t("title", "Hello World")}</h1>

      {/* With variables */}
      <p>{t("greeting", "Hello, {{name}}!", { name: "Baker" })}</p>

      {/* With pluralization */}
      <span>{t("items", { count: 5 })}</span>

      {/* With namespace */}
      <button>{t("auth:login.button", "Sign In")}</button>
    </div>
  );
}

2. Namespace Prefix (Organized Ingredients)

Scope translations to a specific namespace:

import { useTranslation } from "@i18n-bakery/react";

function AuthPage() {
  // All translations will be prefixed with 'auth:'
  const t = useTranslation("auth");

  return (
    <div>
      {/* Translates 'auth:login.title' */}
      <h1>{t("login.title", "Sign In")}</h1>

      {/* Translates 'auth:login.button' */}
      <button>{t("login.button", "Login")}</button>

      {/* Translates 'auth:register.link' */}
      <a href="/register">{t("register.link", "Create account")}</a>
    </div>
  );
}

3. useI18n Hook (The Full Toolkit)

Access the complete i18n instance:

import { useI18n } from "@i18n-bakery/react";

function LanguageSwitcher() {
  const { i18n, locale } = useI18n();

  const changeLanguage = async (newLocale: string) => {
    await i18n.setLocale(newLocale);
  };

  return (
    <div>
      <p>Current language: {locale}</p>
      <button onClick={() => changeLanguage("en")}>English</button>
      <button onClick={() => changeLanguage("es")}>Español</button>
      <button onClick={() => changeLanguage("fr")}>Français</button>
    </div>
  );
}

4. Reactive Updates (Fresh from the Oven)

Components automatically re-render when the locale changes:

import { useTranslation, useI18n } from "@i18n-bakery/react";

function Header() {
  const t = useTranslation();
  const { i18n } = useI18n();

  return (
    <header>
      {/* This updates automatically when locale changes */}
      <h1>{t("app.title", "My App")}</h1>

      <button onClick={() => i18n.setLocale("es")}>Español</button>
    </header>
  );
}

📚 API Reference

Components

<I18nProvider>

Provides i18n context to your React tree.

interface I18nProviderProps {
  locale: string; // Initial locale
  children: React.ReactNode; // Your app components
}

<I18nProvider locale="en">
  <App />
</I18nProvider>;

Hooks

useTranslation(namespace?: string): TranslationFunction

Returns a translation function.

// Without namespace
const t = useTranslation();
t("home.title", "Welcome");

// With namespace
const t = useTranslation("auth");
t("login.title", "Sign In"); // → Translates 'auth:login.title'

Translation Function Signature:

type TranslationFunction = (
  key: string,
  defaultText?: string,
  vars?: Record<string, any>
) => string;

useI18n(): I18nContext

Returns the i18n context.

const { i18n, locale } = useI18n();

// Access i18n instance
i18n.setLocale('es');
i18n.getCurrentLocale();
i18n.addTranslations('en', 'common', { ... });

// Current locale
console.log(locale); // → "en"

Context Interface:

interface I18nContext {
  i18n: I18nService; // The i18n instance
  locale: string; // Current locale
}

🎨 Usage Patterns

Pattern 1: Simple Component

import { useTranslation } from "@i18n-bakery/react";

function WelcomeMessage() {
  const t = useTranslation();

  return (
    <div className="welcome">
      <h1>{t("welcome.title", "Welcome!")}</h1>
      <p>{t("welcome.message", "Thank you for visiting our bakery.")}</p>
    </div>
  );
}

Pattern 2: With Variables

import { useTranslation } from "@i18n-bakery/react";

function UserGreeting({ user }: { user: { name: string; email: string } }) {
  const t = useTranslation();

  return (
    <div>
      <h2>{t("user.greeting", "Hello, {{name}}!", { name: user.name })}</h2>
      <p>{t("user.email", "Email: {{email}}", { email: user.email })}</p>
    </div>
  );
}

Pattern 3: With Pluralization

import { useTranslation } from "@i18n-bakery/react";

function ShoppingCart({ items }: { items: any[] }) {
  const t = useTranslation();
  const itemCount = items.length;

  return (
    <div>
      <h2>{t("cart.title", "Shopping Cart")}</h2>
      <p>{t("cart.items", { count: itemCount })}</p>
      {/* → "0 items" | "1 item" | "5 items" */}
    </div>
  );
}

Pattern 4: Namespaced Component

import { useTranslation } from "@i18n-bakery/react";

function LoginForm() {
  // All translations scoped to 'auth' namespace
  const t = useTranslation("auth");

  return (
    <form>
      <h2>{t("login.title", "Sign In")}</h2>

      <label>
        {t("login.email", "Email")}
        <input type="email" />
      </label>

      <label>
        {t("login.password", "Password")}
        <input type="password" />
      </label>

      <button type="submit">{t("login.submit", "Login")}</button>

      <a href="/register">{t("register.link", "Create an account")}</a>
    </form>
  );
}

Pattern 5: Language Switcher

import { useI18n, useTranslation } from "@i18n-bakery/react";

const LANGUAGES = [
  { code: "en", name: "English", flag: "🇺🇸" },
  { code: "es", name: "Español", flag: "🇪🇸" },
  { code: "fr", name: "Français", flag: "🇫🇷" },
  { code: "de", name: "Deutsch", flag: "🇩🇪" },
];

function LanguageSwitcher() {
  const { i18n, locale } = useI18n();
  const t = useTranslation();

  const handleLanguageChange = async (newLocale: string) => {
    await i18n.setLocale(newLocale);
  };

  return (
    <div className="language-switcher">
      <label>{t("settings.language", "Language")}</label>
      <select
        value={locale}
        onChange={(e) => handleLanguageChange(e.target.value)}
      >
        {LANGUAGES.map((lang) => (
          <option key={lang.code} value={lang.code}>
            {lang.flag} {lang.name}
          </option>
        ))}
      </select>
    </div>
  );
}

Pattern 6: With ICU MessageFormat

import { useTranslation } from "@i18n-bakery/react";

function NotificationList({ notifications }: { notifications: any[] }) {
  const t = useTranslation();

  return (
    <div>
      {notifications.map((notif) => (
        <div key={notif.id}>
          {/* ICU plural syntax */}
          {t(
            "notification.message",
            "{count, plural, =0 {No new messages} one {# new message} other {# new messages}}",
            { count: notif.count }
          )}

          {/* ICU select syntax */}
          {t(
            "notification.action",
            "{gender, select, male {He} female {She} other {They}} {action}",
            { gender: notif.user.gender, action: notif.action }
          )}
        </div>
      ))}
    </div>
  );
}

Pattern 7: With Number Formatting Plugin

import { useTranslation } from "@i18n-bakery/react";

function ProductCard({ product }: { product: any }) {
  const t = useTranslation();

  return (
    <div className="product-card">
      <h3>{product.name}</h3>

      {/* Currency formatting */}
      <p className="price">
        {t("product.price", "Price: {amount|currency:USD}", {
          amount: product.price,
        })}
      </p>

      {/* Compact number formatting */}
      <p className="views">
        {t("product.views", "{count|compact} views", { count: product.views })}
      </p>

      {/* Percentage formatting */}
      <p className="discount">
        {t("product.discount", "Save {amount|percent}!", {
          amount: product.discount,
        })}
      </p>
    </div>
  );
}

🏗️ Advanced Usage

Custom Provider Setup

import { I18nProvider } from "@i18n-bakery/react";
import { initI18n } from "@i18n-bakery/core";
import { useState, useEffect } from "react";

function App() {
  const [locale, setLocale] = useState("en");

  useEffect(() => {
    // Initialize i18n
    initI18n({
      locale,
      fallbackLocale: "en",
      saveMissing: true,
      loader: async (locale, namespace) => {
        const response = await fetch(
          `/api/translations/${locale}/${namespace}`
        );
        return response.json();
      },
    });
  }, [locale]);

  return (
    <I18nProvider locale={locale}>
      <YourApp onLocaleChange={setLocale} />
    </I18nProvider>
  );
}

With TypeScript

import { useTranslation, useI18n } from "@i18n-bakery/react";
import type { FC } from "react";

interface UserProps {
  name: string;
  email: string;
}

const UserProfile: FC<UserProps> = ({ name, email }) => {
  const t = useTranslation("profile");
  const { locale } = useI18n();

  return (
    <div>
      <h2>{t("title", "User Profile")}</h2>
      <p>{t("name", "Name: {{name}}", { name })}</p>
      <p>{t("email", "Email: {{email}}", { email })}</p>
      <p>Current locale: {locale}</p>
    </div>
  );
};

With React Router

import { BrowserRouter, Routes, Route } from "react-router-dom";
import { I18nProvider } from "@i18n-bakery/react";

function App() {
  return (
    <I18nProvider locale="en">
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/contact" element={<ContactPage />} />
        </Routes>
      </BrowserRouter>
    </I18nProvider>
  );
}

🧪 Testing

Testing Components with Translations

import { render, screen } from "@testing-library/react";
import { I18nProvider } from "@i18n-bakery/react";
import { initI18n, addTranslations } from "@i18n-bakery/core";
import MyComponent from "./MyComponent";

describe("MyComponent", () => {
  beforeEach(() => {
    initI18n({ locale: "en" });
    addTranslations("en", "common", {
      title: "Test Title",
      button: "Click Me",
    });
  });

  it("renders translated content", () => {
    render(
      <I18nProvider locale="en">
        <MyComponent />
      </I18nProvider>
    );

    expect(screen.getByText("Test Title")).toBeInTheDocument();
    expect(screen.getByText("Click Me")).toBeInTheDocument();
  });
});

🔗 Related Packages



📜 License

MIT © Arturo Sáenz


🙏 Support


⚛️ React bindings for your internationalization bakery

Made with 🍩 and React Hooks