Big News: Socket Selected for OpenAI's Cybersecurity Grant Program.Details
Socket
Book a DemoSign in
Socket

@qzlcorp/typed-i18n-react

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@qzlcorp/typed-i18n-react

React bindings for @qzlcorp/typed-i18n

latest
Source
npmnpm
Version
1.1.0
Version published
Maintainers
1
Created
Source

@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

Buy Me a Coffee

License

MIT © Q.Z.L Corp.

Keywords

i18n

FAQs

Package last updated on 18 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts