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

@pidchashyi/i18n-next

v1.1.0

Published

A simple i18n library for React applications with TypeScript support

Readme

@pidchashyi/i18n-next

A type-safe i18n library for Next.js applications with inline editing capabilities and automatic translation management.

🚀 Features

  • 🔒 Full TypeScript support with type-safe translation keys
  • 🔗 Variable interpolation for dynamic content
  • Server-side rendering compatible with Next.js App Router
  • ✏️ Inline editing with double-click to edit translations
  • 🍪 Automatic language persistence via cookies
  • 📁 Configurable locale directory through i18n.json
  • 🎯 Missing translation detection with visual indicators
  • 🔄 Real-time updates with optimistic UI updates

🎬 Demo

See the library in action:

Demo

📋 Installation

npm install @pidchashyi/i18n-next
# or
bun add @pidchashyi/i18n-next

Dependencies

This library requires the following dependencies:

  • @pidchashyi/try-catch: ^2.1.0
  • next: 15.3.5
  • react: ^18.3.1
  • react-hot-toast: ^2.5.2

🛠️ Setup

1. Create translation files

Create your locale JSON files in src/i18n/locales (default) or your custom directory:

src/i18n/locales/
├── en.json
├── uk.json
└── fr.json

Example en.json:

{
  "common": {
    "welcome": "Welcome",
    "submit": "Submit"
  },
  "navigation": {
    "home": "Home",
    "about": "About"
  }
}

Example uk.json:

{
  "common": {
    "welcome": "Ласкаво Просимо",
    "submit": "Підтвердити"
  },
  "navigation": {
    "home": "Головна",
    "about": "Детальніше"
  }
}

2. Configure custom locale directory (optional)

Create i18n.json in your project root to customize the locale folder:

{
  "path": "locales"
}

3. Setup the Provider

Wrap your app with I18nProvider in your root layout:

import { I18nProvider } from "@pidchashyi/i18n-next";
import type { ReactNode } from "react";

interface Properties {
  children: ReactNode;
}

const RootLayout = ({ children }: Properties) => {
  return (
    <html>
      <body>
        <I18nProvider
          config={{
            initialLanguage: "en",
            supportedLanguages: ["en", "uk", "fr"],
            enabled: true, // Optional: enables/disables inline editing (default: true)
          }}
        >
          {children}
        </I18nProvider>
      </body>
    </html>
  );
};

export default RootLayout;

🎯 Usage

Basic Usage with Type Safety

Create a typed hook for your translations:

import { createTypedI18nHook } from "@pidchashyi/i18n-next";
// pass the base language json file in order to get type-safety
import jsonData from "@/i18n/locales/en.json";

const SUPPORTED_LANGUAGES = ["en", "uk", "fr"] as const;

export const useI18n = createTypedI18nHook<
  typeof SupportedLanguages,
  typeof jsonData
>();

Use in your components:

"use client";

import { useI18n } from "./hooks/useI18n";

const HomePage = () => {
  const { t, language, changeLanguage } = useI18n();

  return (
    <>
      <h1>{t("common.welcome")}</h1>
      <p>Current language: {language}</p>

      <button onClick={() => changeLanguage("uk")}>Ukrainian</button>
    </>
  );
};

export default HomePage;

Inline Editing

All translations rendered by t() support multiple ways to trigger inline editing:

Edit Triggers:

  • Right-click - Context menu trigger for editing (Traditional method)
  • Ctrl/Cmd + Click - Most reliable method, works inside buttons and other interactive elements
  • Keyboard navigation - Tab to focus the text, then press Enter or Space to edit

Edit Controls:

  • Enter to save changes
  • Escape to cancel editing
  • Click outside to cancel editing

Placeholder-Safe Editing:

When editing translations that contain variable placeholders like {name} or {count}, the editor automatically:

  • Shows the template - You edit the original template with placeholders, not the interpolated text
  • Validates placeholders - Prevents saving if required placeholders are missing
  • Provides hints - Shows available placeholders in the tooltip and during editing
  • Warns about changes - Notifies when new placeholders are added
// Example: "Welcome, {name}!" with variables
<h1>{t("welcome", { name: "John" })}</h1>
// Displays: "Welcome, John!"
// When editing: Shows "Welcome, {name}!" for editing
// Validates: Ensures {name} placeholder is preserved

// Works even inside buttons - use Ctrl/Cmd + Click
<button className="px-4 py-2 bg-blue-500 text-white">
  {t("common.submit")}
</button>

Pro Tips:

  • When translations are inside buttons or other interactive elements, use Ctrl/Cmd + Click for the most reliable editing experience
  • When editing translations with placeholders, make sure to preserve all {placeholderName} variables
  • Hover over translations to see available placeholders in the tooltip

Missing translations are automatically highlighted in red and can also be edited inline using any of the above methods.

Language Switching

Change language programmatically:

const { changeLanguage } = useI18n();

// Switch to Spanish
await changeLanguage("uk");

Language preference is automatically saved in cookies and persists across sessions.

Production Mode

Disable inline editing in production environments:

<I18nProvider
  config={{
    initialLanguage: "en",
    supportedLanguages: ["en", "uk", "fr"],
    enabled: process.env.NODE_ENV === "development", // Only enable in development
  }}
>
  {children}
</I18nProvider>

🔧 API Reference

I18nProvider

Server component that provides i18n context to your app.

interface I18nProviderProps {
  children: React.ReactNode;
  config: {
    initialLanguage: string;
    supportedLanguages: string[];
    enabled?: boolean; // Optional: enables/disables inline editing (default: true)
  };
}

createTypedI18nHook<TSupportedLanguages, TTranslations>()

Creates a type-safe hook for accessing translations.

Returns: useI18n hook with the following interface:

{
  t: (key: keyof TTranslations, variables?: Record<string, string | number>) => ReactNode;
  language: TSupportedLanguages[number];
  changeLanguage: (lang: TSupportedLanguages[number]) => Promise<void>;
  enabled?: boolean; // Whether inline editing is enabled
}

The t function provides:

  • Full type safety for translation keys
  • Optional variables parameter for interpolation
  • Runtime interpolation of {placeholder} values

Variable Interpolation

Support for dynamic variables in translations:

{
  "welcome": "Welcome, {name}!",
  "itemCount": "You have {count} items",
  "greeting": "Hello, {name}! You have {count} unread messages",
  "simple": "This has no variables"
}
// Usage with variables
<h1>{t("welcome", { name: "John" })}</h1>
<p>{t("itemCount", { count: 5 })}</p>
<div>{t("greeting", { name: "Alice", count: 3 })}</div>
<span>{t("simple")}</span>

// Variables are optional
<h1>{t("welcome")}</h1>                          // Will show "Welcome {name}"
<p>{t("itemCount", { name: "John" })}</p>        // Unused variables are ignored

Key Features:

  • Type Safety: TypeScript automatically detects which variables are required for each translation key
  • IntelliSense: Auto-completion shows exactly which variables each translation needs
  • Compile-time Validation: Catches missing or incorrect variables before runtime
  • Flexible: Variables are only required if the translation contains {variableName} placeholders
  • Safe Editing: When editing translations with placeholders, the original template is preserved and validated

Variables are interpolated using the {variableName} syntax. If a variable is not provided at runtime, the placeholder will remain unchanged in the output.

Editing with Variables: When you edit a translation that contains placeholders like "Welcome, {name}!", the inline editor:

  • Shows the original template for editing (not the interpolated version)
  • Validates that all required placeholders are preserved when saving
  • Provides helpful hints about available placeholders
  • Prevents accidental removal of variable placeholders

Configuration Options

i18n.json (optional)

{
  "path": "custom/path/to/locales"
}
  • path: Custom directory path for locale files (relative to project root)
  • Default: src/i18n/locales

📁 Project Structure

your-project/
├── i18n.json                    # Optional config
├── src/i18n/locales/           # Default locale directory
│   ├── en.json
│   ├── uk.json
│   └── fr.json
└── app/
    ├── layout.tsx              # Setup I18nProvider
    └── page.tsx                # Use translations

🚀 Future Ideas

Global Loading UI (Planned)

Optional global loading indicators during language switching and translation loading:

<I18nProvider
  config={{
    initialLanguage: "en",
    supportedLanguages: ["en", "uk", "fr"],
    enabled: true,
    loadingComponent: <GlobalSpinner />, // Optional global loading UI
    showLoadingOverlay: true, // Optional overlay during language switching
  }}
>
  {children}
</I18nProvider>

Namespace Support (Planned)

Organize translations into logical namespaces:

// Usage with namespaces
const { t } = useI18n("auth"); // Load specific namespace
<h1>{t("login.title")}</h1>;

Lazy Loading (Planned)

Load translation files on-demand for better performance:

<I18nProvider
  config={{
    lazyLoad: true,
    chunkSize: "page", // Load translations per page/route
  }}
>
  {children}
</I18nProvider>

🤝 Contributing

Contributions, issues, and feature requests are welcome!

📄 License

MIT License - see LICENSE file for details.