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

pnar-i18n

v2.1.0

Published

Framework-agnostic internationalization library with support for React, Vue, and other JavaScript frameworks

Readme

pnar-i18n

A framework-agnostic internationalization library with first-class support for React and Vue.

Features

  • Framework Agnostic - Core logic works with any JavaScript framework
  • React Support - Ready-to-use React components and hooks
  • Vue Support - Vue 3 composition API and plugin
  • TypeScript Support - Full type safety
  • Interpolation - Variable substitution in translations
  • Nested Keys - Support for deep translation objects using dot notation
  • Dynamic Loading - Asynchronously load translations on demand
  • Language Persistence - Save language preferences to localStorage
  • Lightweight - Small footprint, minimal dependencies
  • Extensible - Easy to extend for additional functionality

Installation

npm install pnar-i18n
# or
yarn add pnar-i18n

Import Paths

This library uses a modular approach. Import from the appropriate path based on your framework:

// React components and hooks
import { TranslationProvider, useTranslation } from 'pnar-i18n/react';

// Vue composition API and plugin
import { createI18n, useI18n } from 'pnar-i18n/vue';

// Core functionality (framework-agnostic)
import { interpolate } from 'pnar-i18n/core';

Quick Start

React

import { TranslationProvider, useTranslation } from 'pnar-i18n/react';

// Define your translations
const translations = {
  en: {
    welcome: 'Welcome',
    greeting: 'Hello {{name}}!',
    nested: {
      key: 'This is a nested translation'
    }
  },
  pnar: {
    welcome: 'Alæ',
    greeting: 'Hey {{name}}!',
    nested: {
      key: 'Tha esp ippk keñan'
    }
  },
};

// Wrap your app with the provider
function App() {
  return (
    <TranslationProvider
      config={{
        defaultLanguage: 'en',
        fallbackLanguage: 'en',
        translations,
        persistLanguage: true
      }}
    >
      <MyComponent />
    </TranslationProvider>
  );
}

// Use translations in your components
function MyComponent() {
// Vue example
import { createI18n } from 'pnar-i18n/vue';

const i18n = createI18n({
  defaultLanguage: 'en',
  translations: {
    en: {
      /* initial English translations */
    },
  },
  loadTranslations: async (language) => {
    // Fetch translations from your API
    const response = await fetch(`/api/translations/${language}`);
    return response.json();
  },
});

Using Nested Translations

// Define nested translations
const translations = {
  en: {
    ui: {
      buttons: {
        submit: 'Submit',
        cancel: 'Cancel',
      },
      forms: {
        errors: {
          required: 'This field is required',
        },
      },
    },
  },
};

// Access with dot notation
t('ui.buttons.submit'); // "Submit"
t('ui.forms.errors.required'); // "This field is required"

Integration with Framework Router

React Router

import { useTranslation } from 'pnar-i18n/react';
import { useParams } from 'react-router-dom';

function LocalizedPage() {
  const { lang } = useParams();
  const { setLanguage } = useTranslation();

  // Update language based on URL param
  useEffect(() => {
    if (lang) {
      setLanguage(lang);
    }
  }, [lang, setLanguage]);

  // Rest of your component...
}

Vue Router

<script setup>
import { useI18n } from 'pnar-i18n/vue';
import { useRoute, useRouter } from 'vue-router';
import { watch } from 'vue';

const route = useRoute();
const router = useRouter();
const { setLanguage, language } = useI18n();

// Update language based on route param
watch(
  () => route.params.lang,
  (newLang) => {
    if (newLang) {
      setLanguage(newLang);
    }
  },
  { immediate: true }
);

// Update route when language changes
watch(language, (newLang) => {
  if (route.params.lang !== newLang) {
    router.push({
      params: { ...route.params, lang: newLang },
    });
  }
});
</script>

API Reference

Core API

interpolate(text, options, config)

Interpolates variables into a translation string.

import { interpolate } from 'pnar-i18n/core';

const result = interpolate(
  'Hello {{name}}!',
  { name: 'World' },
  { prefix: '{{', suffix: '}}' }
);
// "Hello World!"

React API

TranslationProvider

Provider component that wraps your application and provides translation context.

<TranslationProvider config={I18nConfig}>{children}</TranslationProvider>

useTranslation()

Hook to access translation functions and state within components.

const {
  t, // Translation function (key, options) => string
  language, // Current language code
  setLanguage, // Function to change language
  translations, // Complete translations object
  isLoading, // Boolean indicating if translations are loading
  availableLanguages, // Array of available language codes
} = useTranslation();

Vue API

createI18n(config)

Creates an i18n instance for Vue applications.

const i18n = createI18n(I18nConfig);

// Use as a Vue plugin
app.use(i18n);

useI18n()

Composition API hook for use within Vue components.

const {
  t, // Translation function (key, options) => string
  language, // Ref to current language code
  setLanguage, // Function to change language
  translations, // Ref to complete translations object
  isLoading, // Ref to loading state
  availableLanguages, // Array of available language codes
} = useI18n();

TypeScript Support

The library includes complete TypeScript definitions for both React and Vue adapters:

// Common types
interface I18nConfig {
  defaultLanguage: string;
  fallbackLanguage?: string;
  translations?: Translations;
  loadTranslations?: (language: string) => Promise<TranslationMap>;
  persistLanguage?: boolean;
  storageKey?: string;
  interpolation?: {
    prefix: string;
    suffix: string;
  };
}

interface TranslationOptions {
  fallback?: string;
  [key: string]: any; // For interpolation variables
}

// Define strongly typed translations
interface CustomTranslations {
  en: {
    greeting: string;
    welcome: string;
    // etc...
  };
  pnar: {
    greeting: string;
    welcome: string;
    // etc...
  };
}

// Use with React
const { t } = useTranslation<CustomTranslations>();

// Use with Vue
const { t } = useI18n<CustomTranslations>();

return (

  <button onClick={() => setLanguage(language === 'en' ? 'pnar' : 'en')}>
    Switch Language
  </button>
</div>

); }


### Vue

```typescript
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { createI18n } from 'pnar-i18n/vue';

// Define your translations
const translations = {
  en: {
    welcome: 'Welcome',
    greeting: 'Hello {{name}}!',
    nested: {
      key: 'This is a nested translation'
    }
  },
  pnar: {
    welcome: 'Alæ',
    greeting: 'Hey {{name}}!',
    nested: {
      key: 'Tha esp ippk keñan'
    }
  },
};

// Create and configure i18n instance
const i18n = createI18n({
  defaultLanguage: 'en',
  fallbackLanguage: 'en',
  translations,
  persistLanguage: true
});

// Create and mount your app with i18n
const app = createApp(App);
app.use(i18n);
app.mount('#app');
<!-- Component.vue -->
<script setup>
import { useI18n } from 'pnar-i18n/vue';
import { computed } from 'vue';

const { t, language, setLanguage } = useI18n();

const toggleLanguage = () => {
  setLanguage(language.value === 'en' ? 'pnar' : 'en');
};
</script>

<template>
  <div>
    <h1>{{ t('welcome') }}</h1>
    <p>{{ t('greeting', { name: 'John' }) }}</p>
    <p>{{ t('nested.key') }}</p>

    <button @click="toggleLanguage">Switch Language</button>
  </div>
</template>

Configuration Options

Both React and Vue adapters accept the same configuration options:

| Option | Type | Default | Description | | ------------------ | ---------- | -------------------------------- | --------------------------------------------------- | | defaultLanguage | string | 'en' | The default language to use | | fallbackLanguage | string | 'en' | Language to use when a translation is not found | | translations | object | {} | Object containing all translations | | loadTranslations | function | — | Async function to load translations on demand | | persistLanguage | boolean | true | Whether to save language preference to localStorage | | storageKey | string | 'pnar-i18n-language' | Key to use for localStorage | | interpolation | object | { prefix: '{{', suffix: '}}' } | Configure interpolation markers |

Advanced Usage

Dynamic Translation Loading

// React example
<TranslationProvider
  config={{
    defaultLanguage: 'en',
    translations: {
      en: {
        /* initial English translations */
      },
    },
    loadTranslations: async (language) => {
      // Fetch translations from your API
      const response = await fetch(`/api/translations/${language}`);
      return response.json();
    },
  }}
>
  {/* Your app */}
</TranslationProvider>

setLanguage, // Change language isLoading, // Loading state availableLanguages, // Available languages } = useTranslation();


### usePluralize Hook

```typescript
const pluralize = usePluralize();

// Basic usage
pluralize('item', 1); // "item"
pluralize('item', 5); // "items" (if plural form exists)

// With custom translations
pluralize('apple', 3); // Uses translation key 'apple_many' if available

useDateLocalization Hook

const localizeDate = useDateLocalization();

// Format dates according to current language
localizeDate(new Date(), 'long'); // "December 25, 2023" (localized)
localizeDate(new Date(), 'short'); // "12/25/23" (localized)

Advanced Usage

Dynamic Translation Loading

const loadTranslations = async (language: string) => {
  const response = await fetch(`/locales/${language}.json`);
  return response.json();
};

<TranslationProvider
  config={{
    defaultLanguage: 'en',
    loadTranslations,
  }}
>
  <App />
</TranslationProvider>;

Pluralization

import { usePluralize } from 'pnar-i18n/react';

function MyComponent() {
  const pluralize = usePluralize();

  return <p>{pluralize('item', count)}</p>;
}

Language Switching

You can create your own language switcher component using the provided language switching functionality:

import { useTranslation } from 'pnar-i18n/react';

function LanguageSwitcher() {
  const { currentLanguage, setLanguage, availableLanguages } = useTranslation();

  return (
    <div>
      {availableLanguages.map((lang) => (
        <button
          key={lang}
          onClick={() => setLanguage(lang)}
          style={{ fontWeight: currentLanguage === lang ? 'bold' : 'normal' }}
        >
          {lang.toUpperCase()}
        </button>
      ))}
    </div>
  );
}

For Vue applications:

<script setup>
import { useI18n } from 'pnar-i18n/vue';

const { currentLanguage, setLanguage, availableLanguages } = useI18n();
</script>

<template>
  <div>
    <button
      v-for="lang in availableLanguages"
      :key="lang"
      @click="setLanguage(lang)"
      :style="{ fontWeight: currentLanguage === lang ? 'bold' : 'normal' }"
    >
      {{ lang.toUpperCase() }}
    </button>
  </div>
</template>

Configuration Options

| Option | Type | Default | Description | | ------------------ | -------------- | ------------------------------ | ------------------------ | | defaultLanguage | string | 'en' | Default language | | fallbackLanguage | string | 'en' | Fallback language | | translations | Translations | {} | Static translations | | loadTranslations | Function | - | Dynamic loading function | | persistLanguage | boolean | true | Save language preference | | storageKey | string | 'pnar-i18n-language' | localStorage key | | interpolation | object | {prefix: '{{', suffix: '}}'} | Interpolation markers |

Examples

Basic Usage with JSON Files

// translations/en.json
{
  "welcome": "Welcome",
  "greeting": "Hello {{name}}!",
  "items": "item",
  "items_many": "items"
}

// translations/pnar.json
{
  "welcome": "Alæ",
  "greeting": "Hey[Hei/Hoi/Hiw] {{name}}!",
  "items": "ki item",
  "items_many": "Bõn item"
}

// App.tsx
import React from 'react';
import { TranslationProvider, useTranslation, usePluralize } from 'pnar-i18n/react';

const loadTranslations = async (language: string) => {
  const response = await fetch(`/translations/${language}.json`);
  return response.json();
};

function App() {
  return (
    <TranslationProvider
      config={{
        defaultLanguage: 'en',
        loadTranslations
      }}
    >
      <Header />
      <Content />
    </TranslationProvider>
  );
}

function Header() {
  const { currentLanguage, setLanguage, availableLanguages } = useTranslation();

  return (
    <header>
      <div>
        {availableLanguages.map(lang => (
          <button
            key={lang}
            onClick={() => setLanguage(lang)}
            style={{ fontWeight: currentLanguage === lang ? 'bold' : 'normal' }}
          >
            {lang.toUpperCase()}
          </button>
        ))}
      </div>
    </header>
  );
}

function Content() {
  const { t } = useTranslation();
  const pluralize = usePluralize();

  return (
    <div>
      <h1>{t('welcome')}</h1>
      <p>{t('greeting', { name: 'John' })}</p>
      <p>{pluralize('items', 5)}</p>
    </div>
  );
}

Development

# Install dependencies
npm install

# Build the project
npm run build

# Run in development mode
npm run dev

# Run tests
npm run test

# Run linting
npm run lint

# Type checking
npm run type-check

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT /Users/armegochylla/Projects/pnar-world-admin-vite-react-ts/pnar-i18n/README.md