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

@qzlcorp/typed-i18n-react

v1.1.0

Published

React bindings for @qzlcorp/typed-i18n

Readme

@qzlcorp/typed-i18n-react

React bindings for @qzlcorp/typed-i18n with full TypeScript support.

Typed-i18n type-safety demo

See how TypeScript ensures type-safe translations in React apps with full autocomplete and compile-time validation.

Live Demo

🚀 View Live Demo - See dynamic module loading, locale switching, and type-safe translations in action.

Used By

  • QZ-L.com — Fast, secure URL shortening to share links easily. Shorten, track, and manage your URLs at https://qz-l.com.

Installation

pnpm add @qzlcorp/typed-i18n @qzlcorp/typed-i18n-react

Usage

Important: Preserve Key Type Safety

To get static type errors for incorrect translation keys you MUST pass your module type (or use a pre-bound wrapper hook). Without the generic, t() falls back to a loose pattern like ${string}.${string} and invalid keys will not error at compile time.

// i18n.ts
const app = defineModule('app')<typeof enApp>({ en: enApp, fr: frApp });
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en', modules: { app } });
export type I18nModules = { app: typeof app };

// Component: GOOD (strict key union)
const { t } = useTranslation<I18nModules>();
t('app.title');           // OK
t('app.missing');         // ❌ Type error at compile time

// Component: WEAK (no generic) – do NOT use this form if you want key checking
const { t: tLoose } = useTranslation();
tLoose('app.missing');    // ✅ Compiles (no static safety)

Recommended ergonomic wrapper so you never forget the generic:

// i18n-hooks.ts
export const useAppTranslation = () => useTranslation<I18nModules>();
export const useAppLocale = () => useLocale<I18nModules>();
export const useAppI18n = () => useI18n<I18nModules>();

// Component
const { t } = useAppTranslation();

If you later add modules dynamically, you can widen the type:

const dashboard = defineModule('dashboard')<typeof enDashboard>({ en: enDashboard });
const updated = useAppI18n().addModule(dashboard);
// For immediate strict typing in this scope:
const { t: tWithDashboard } = useTranslation<I18nModules & { dashboard: typeof dashboard }>();

Dynamic Modules (Page-Level Splitting)

Load only the namespaces a feature needs by passing a modules object to useTranslation. Each module is registered once and cached by namespace, so repeated renders stay cheap.

import { dashboardModule } from "./modules/dashboard";

const { t } = useTranslation<
  I18nModules,
  { dashboard: typeof dashboardModule }
>({
  dashboard: dashboardModule,
});

console.log(t("dashboard.title"));

Keep the modules object stable (imported or memoized) to avoid unnecessary effect churn. After the hook registers a module, the provider widens the shared i18n instance so other components can access the new namespace without reloading it.

Setup Provider

import { createI18n, defineModule } from '@qzlcorp/typed-i18n';
import { I18nProvider } from '@qzlcorp/typed-i18n-react';

// Define your modules
const common = defineModule('common')<typeof enCommon>({
  en: enCommon,
  fr: frCommon,
});

// Create i18n instance
const i18n = createI18n({
  locale: 'en',
  fallbackLocale: 'en',
  modules: { common },
});

// Wrap your app
function App() {
  return (
    <I18nProvider i18n={i18n}>
      <YourApp />
    </I18nProvider>
  );
}

Use in Components

import { useTranslation, useLocale } from '@qzlcorp/typed-i18n-react';

function MyComponent() {
  const { t, locale } = useTranslation();
  const { setLocale, locales } = useLocale();

  return (
    <div>
      <h1>{t('common.hello')}</h1>
      <p>Current locale: {locale}</p>
      
      <select value={locale} onChange={(e) => setLocale(e.target.value)}>
        {locales.map((loc) => (
          <option key={loc} value={loc}>{loc}</option>
        ))}
      </select>
    </div>
  );
}

API

I18nProvider

Provider component that makes i18n instance available to all child components.

Props:

  • i18n - The i18n instance created with createI18n()
  • children - React children

useTranslation()

Returns translation function and current locale.

Returns:

  • t(key, params?) - Translate function with parameter interpolation
  • t(key, options?) - Translate function with options (supports returnObjects)
  • locale - Current active locale
function MyComponent() {
  const { t, locale } = useTranslation();
  
  // Simple translation
  const title = t('common.title');
  
  // With parameters
  const greeting = t('common.greeting', { name: 'React' });
  
  // Get nested objects (i18next compatible)
  const config = t('app.config', { returnObjects: true });
  
  // Both params and returnObjects
  const data = t('dashboard.stats', { 
    params: { count: 5 }, 
    returnObjects: false 
  });
  
  return <div>{title}</div>;
}

⚠️ Pass your module type generic (e.g. useTranslation<I18nModules>()) for strict key unions.

Optional argument: useTranslation<I18nModules, ExtraModules>({ ...ExtraModules }) dynamically registers more namespaces (e.g. page-level modules) and widens t to cover them.

useLocale()

Returns locale management functions.

Returns:

  • locale - Current active locale
  • setLocale(locale) - Change locale (triggers re-renders)
  • locales - Array of all available locales

useI18n()

Returns the full i18n instance for advanced use cases.

Returns:

  • Full I18nInstance with all methods

<Trans> Component

Component for translating JSX with embedded HTML tags and React components. Safer than dangerouslySetInnerHTML - parses translation strings and builds React elements.

Props:

  • i18nKey - Translation key (optional - if not provided, uses children)
  • children - Can be used as the translation source instead of i18nKey (optional)
  • values - Interpolation parameters (optional)
  • components - Custom React components to use for tags (optional)
  • defaults - Include default HTML components like <strong>, <em>, etc. (default: true)

Note: Either i18nKey or children should be provided. If both are present, i18nKey takes precedence.

Default supported tags (when defaults={true}):

  • <strong>, <b> - Bold text
  • <em>, <i> - Italic text
  • <u> - Underlined text
  • <br/> - Line break
  • <p>, <span> - Block/inline containers
// Translation JSON
{
  "welcome": "Hello <strong>{{name}}</strong>!",
  "action": "Click <link>here</link> to continue"
}

// Using i18nKey (recommended for externalized translations)
<Trans i18nKey="common.welcome" values={{ name: "John" }} />
// Renders: Hello <strong>John</strong>!

// Using children as translation source (inline translations)
<Trans values={{ name: "John" }}>
  Hello <strong>{{name}}</strong>!
</Trans>
// Renders: Hello <strong>John</strong>!

// Using children with JSX elements (preserves attributes)
const description = (
  <>
    Check the <a href="/docs">documentation</a> for more info
  </>
);
<Trans components={{ a: <a /> }}>{description}</Trans>
// Renders: Check the <a href="/docs">documentation</a> for more info

// Custom components with i18nKey
<Trans 
  i18nKey="common.action"
  components={{ 
    link: <a href="/next" className="btn" />
  }}
/>
// Renders: Click <a href="/next" class="btn">here</a> to continue

// Custom components with children
<Trans components={{ link: <a href="/next" className="btn" /> }}>
  Click <link>here</link> to continue
</Trans>
// Renders: Click <a href="/next" class="btn">here</a> to continue

// Multiple tags
// Translation: "This is <strong>bold</strong> and <em>italic</em>"
<Trans i18nKey="common.formatted" />

// Nested tags
// Translation: "Text with <strong>bold and <em>italic</em> nested</strong>"
<Trans i18nKey="common.nested" />

// Disable default components
<Trans i18nKey="common.text" defaults={false} components={{ custom: <CustomComponent /> }} />

Safety note: The <Trans> component does NOT use dangerouslySetInnerHTML. It safely parses the translation string and builds React elements using React.cloneElement(), preventing XSS vulnerabilities.

Features

Automatic Re-renders - Components re-render when locale changes
Full Type Safety - All translation keys are typed
Trans Component - Safe HTML/JSX translation without XSS risks
React 18 Ready - Built with modern React patterns
Small Bundle - Minimal overhead
Tree Shakeable - Only import what you use

Support

License

MIT © Q.Z.L Corp.