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

eze-lang

v1.0.0

Published

eze-lang

Downloads

64

Readme

Eze-Lang

Eze-Lang is a high-performance localization package designed to simplify translation management. Write your translations in simple YAML files, and let Eze-Lang generate language-specific JSON files along with type-safe TypeScript definitions. With smart dynamic message composition—including placeholders, conditions, variants, and nested key lookups (parrotHolders)—Eze-Lang helps you build robust, internationalized applications with ease.


Why Eze-Lang?

  • Organized Translations:
    Store translations in separate YAML files (e.g., actions.yml, errors.yml, greetings.yml, counts.yml) instead of one huge file. The file name defines the translation group, keeping your project clean and scalable.

  • Automatic TypeScript Definitions:
    Eze-Lang automatically generates a Parrot.Types.ts file at your project root for type safety and IDE autocompletion, reducing potential runtime errors.

  • Dynamic & Static Translations:

    • Static Keys: Return fixed strings.
    • Dynamic Keys: Use runtime placeholders, conditions, and variants to generate context-aware messages.
    • ParrotHolders: Reuse or nest translations dynamically without redundancy.
  • Variants & Conditions:
    Easily handle multiple message variations based on parameters like gender or numeric conditions.

    Insight: Variants allow the translation system to automatically select the correct message variant based on the first placeholder (e.g., gender in greetings). Multi-conditions add flexibility by handling special cases (e.g., specific names).

  • Default Parameters & Fallbacks:
    Set fallback values via DefaultPlaceholders and DefaultVariants to gracefully handle missing values, making debugging easier.

  • Performance-Optimized:
    With precompiled interpolation and smart merging, Eze-Lang delivers minimal runtime overhead.

  • Developer Experience:
    Integrated with Vite for live reloading and automatic updates, it works seamlessly in React and any JavaScript/TypeScript project.

  • Missing-Key Detection:
    The build tooling automatically generates a missing-keys.yml report per language, listing untranslated keys with their source-language values.


Installation

Install with npm, Yarn, or pnpm:

npm install eze-lang
# or
yarn add eze-lang
# or
pnpm add eze-lang

Eze-Lang requires Node.js 18 or newer.

Note: When you install and start your project, the parrot will auto-generate if no configuration is present. All YAML files in your project are processed as described below.


Quick Start

  1. Add the Vite plugin to your Vite configuration:
import { defineConfig } from "vite";
import parrotPlugin from "eze-lang/dist/vite-plugin-parrot.js";

export default defineConfig({
  plugins: [parrotPlugin()],
});

Auto-Inject: The plugin auto-injects the generated parrot/index.ts script into your HTML via transformIndexHtml. This registers the config at startup — no manual import needed.

  1. Run your project:
npm run dev
# or
yarn dev

Note: The Vite plugin automatically generates language-specific JSON files and TypeScript definitions on startup.

  1. Switch language in your application:
import { setLanguage } from "eze-lang";
import Parrot from "eze-lang";

// Switch to Arabic (config is already registered by auto-inject)
setLanguage("ar");

// Flat access (all keys in one namespace)
console.log(Parrot.upload); // "تحميل الملف"
console.log(Parrot.search({ query: "projects" })); // "البحث عن 'projects'..."

// Grouped access (namespaced by YAML file)
import { ParrotGroups } from "eze-lang";
console.log(ParrotGroups.actions.upload);

Generated output structure:

parrot/
  en/
    actions.json
    messages.json
    ui.json
  ar/
    actions.json
    messages.json
    ui.json
  index.ts          # Loads and merges per-group files
Parrot.Types.ts     # Generated at the project root

Components

Eze-Lang provides three components for rendering translations in your application:

  • Static Component Usage:
//  Renders a static key directly
import { ParrotStaticComponent } from "eze-lang";
return <ParrotStaticComponent k="upload" />;
  • Mixed Component Usage:
// Renders a mixed key, requiring some parameters for interpolation
import { ParrotMixedComponent } from "eze-lang";
return <div>
          <ParrotMixedComponent k="greeting" gender="female" name="Emma" />
          <ParrotMixedComponent k="itemCount" count={5} />
          <ParrotMixedComponent k="upload" />
       </div>;
  • Dynamic Component Usage:
// Renders a dynamic key with placeholders and parrotHolders
import { ParrotDynamicComponent } from "eze-lang";
return <ParrotDynamicComponent k="customMessage" startParrotAction="becauseOf" endParrotAction="tryAgain" anyText="Your text here" user="John" />;

Note: The ParrotDynamicComponent component is used for dynamic keys with placeholders and parrotHolders. The k prop and generated required placeholder props are type-checked.


Configuration

The parrot.config.json file is generated in your project root. All YAML files must reside in a yml folder inside the specified outputDir.

{
  "$schema": "./node_modules/eze-lang/dist/parrot.config.schema.json",
  "languages": ["en", "ar"],
  "ymlValuesLanguage": "en",
  "defaultLanguage": "en",
  "outputDir": "./parrot"
}

| Field | Required | Description | | ------------------- | -------- | ----------------------------------------------------------------------- | | languages | ✅ | All supported language codes | | defaultLanguage | ✅ | Startup language fallback | | ymlValuesLanguage | ❌ | Language the YAML values are written in (defaults to defaultLanguage) | | outputDir | ✅ | Directory for generated files |

Note: Organize your translations into different files as needed. The file name defines the translation group.


YAML Blueprint Examples

Below are several example YAML files with real-world scenarios, inline usage examples, and expected outputs.

1. Actions (actions.yml)

upload:
  desc: "Action for uploading a file"
  value: "Upload File"

download:
  desc: "Action for downloading a file"
  value: "Download File"

search:
  desc: "Search action with a query"
  placeholders: query
  value: "Searching for '{query}'..."

Usage & Expected Output:

console.log(Parrot.upload); // "Upload File"
console.log(Parrot.download); // "Download File"
console.log(Parrot.search({ query: "docs" })); // "Searching for 'docs'..."

2. Errors (errors.yml)

serverError:
  desc: "Generic server error message"
  value: "A server error occurred. Please try again later."

errorWhile:
  desc: "Error message with a dynamic action"
  placeholders: action
  value: "An error occurred while {action}"

errorWithDetail:
  desc: "Nested key lookup error message using parrotHolders"
  parrotHolders: action
  value: "Failed to perform {action}"

Usage & Expected Output:

console.log(Parrot.serverError); // "A server error occurred. Please try again later."
console.log(Parrot.errorWhile({ action: "uploading" })); // "An error occurred while uploading"
console.log(Parrot.errorWithDetail({ action: "download" })); // "Failed to perform Download File"

3. Greetings & Variants (greetings.yml)

greeting:
  desc: "Basic greeting with variants based on gender"
  placeholders: gender, name
  value: "Hello {name}"
  variants:
    male: "Hello Mr. {name}"
    female: "Hello Ms. {name}"
    default: "Hello {name}"

genderGreeting:
  desc: "Greeting with multi-conditions based on gender and name"
  placeholders: gender, name
  value: "Hi {name}"
  conditions:
    "{gender} === 'female' && {name} === 'Alice'": "Welcome, Queen Alice!"
    "{gender} === 'male'": "Greetings, Sir {name}"
    "{gender} === 'female'": "Greetings, Lady {name}"

Usage & Expected Output:

console.log(Parrot.greeting({ gender: "male", name: "John" })); // "Hello Mr. John"
console.log(Parrot.greeting({ gender: "female", name: "Sarah" })); // "Hello Ms. Sarah"
console.log(Parrot.greeting({ gender: "default", name: "Alex" })); // "Hello Alex"

console.log(Parrot.genderGreeting({ gender: "female", name: "Alice" })); // "Welcome, Queen Alice!"
console.log(Parrot.genderGreeting({ gender: "male", name: "Robert" })); // "Greetings, Sir Robert"
console.log(Parrot.genderGreeting({ gender: "female", name: "Emma" })); // "Greetings, Lady Emma"

4. Item Counts with Conditions (counts.yml)

itemCount:
  desc: "Displays item count with context-aware messages"
  placeholders: count
  value: "{count} items available"
  conditions:
    "{count} < 0": "Invalid count"
    "{count} === 0": "No items available"
    "{count} === 1": "One item available"
    "{count} > 1 && {count} < 10": "A few items available"
    "{count} >= 10 && {count} < 50": "Several items available"
    "{count} >= 50": "Many items available"

Note: Conditions are evaluated using a CSP-safe parser (no eval or new Function). Supported operators: ===, !==, >, <, >=, <=, &&, ||. The || operator has lower precedence than &&, matching JavaScript semantics.

Usage & Expected Output:

console.log(Parrot.itemCount({ count: -1 })); // "Invalid count"
console.log(Parrot.itemCount({ count: 0 })); // "No items available"
console.log(Parrot.itemCount({ count: 1 })); // "One item available"
console.log(Parrot.itemCount({ count: 5 })); // "A few items available"
console.log(Parrot.itemCount({ count: 20 })); // "Several items available"
console.log(Parrot.itemCount({ count: 75 })); // "Many items available"

5. Pluralization (items.yml)

Eze-Lang supports CLDR plural categories for proper Arabic (and other language) pluralization. Eze-Lang uses Intl.PluralRules for automatic CLDR plural support across all locales. Override any language with custom rules via the exported pluralRules map.

itemCount:
  desc: "Item count with proper Arabic plurals"
  placeholders: count
  value: "{count} items"
  plurals:
    zero: "لا عناصر"
    one: "عنصر واحد"
    two: "عنصران"
    few: "{count} عناصر"
    many: "{count} عنصراً"
    other: "{count} عنصر"

Usage & Expected Output (Arabic):

import { setLanguage } from "eze-lang";

setLanguage("ar");

console.log(Parrot.itemCount({ count: 0 })); // "لا عناصر"
console.log(Parrot.itemCount({ count: 1 })); // "عنصر واحد"
console.log(Parrot.itemCount({ count: 2 })); // "عنصران"
console.log(Parrot.itemCount({ count: 5 })); // "5 عناصر"
console.log(Parrot.itemCount({ count: 15 })); // "15 عنصراً"
console.log(Parrot.itemCount({ count: 100 })); // "100 عنصر"

Adding custom plural rules (override):

import { pluralRules } from "eze-lang";

// Override for any language — takes priority over Intl.PluralRules
pluralRules.custom = (n) => (n === 1 ? "one" : "other");

6. Composite Custom Messages (custom.yml)

failed:
  desc: "Custom message combining dynamic elements"
  placeholders: detail, action
  value: "Failed to {action} {detail}"

becauseOf:
  desc: "Provides a reason message"
  placeholders: user
  value: "because of {user}"

customMessage:
  desc: "Custom message combining dynamic elements from various sources"
  placeholders: anyText
  parrotHolders: startParrotAction,endParrotAction
  value: "{startParrotAction} {anyText} {endParrotAction}"

Usage & Expected Output:

// Using Static keys
console.log(
  Parrot.customMessage({
    startParrotAction: "upload",
    endParrotAction: "download",
    anyText: "and",
  }),
); // "Upload File and Download File"

// Using Dynamic keys
console.log(
  Parrot.customMessage({
    startParrotAction: "becauseOf",
    endParrotAction: "tryAgain",
    anyText: "SOME CUSTOM TEXT",
    user: "John",
  }),
); // "because of John SOME CUSTOM TEXT Please try again"

console.log(
  Parrot.customMessage({
    anyText: ",",
    startParrotAction: "becauseOf",
    user: "John",
    endParrotAction: "failed",
    action: "download",
    detail: "Image URL",
  }),
); // "because of John , Failed to download Image URL"

Automatic Language Detection

The generated parrot/index.ts auto-detects the startup language using this priority:

  1. URL parameter — ?lang=ar in the URL (great for testing and shareable links)
  2. localStorage — user's stored preference (lang key)
  3. Browser languages — iterates navigator.languages array with prefix matching (e.g., en-US → en)
  4. Cookie — reads lang cookie from document.cookie
  5. Config default — defaultLanguage from parrot.config.json

No manual detection code is needed — the Vite plugin handles it automatically. Each source is guarded independently, so blocked localStorage does not prevent a valid URL parameter, browser preference, cookie, or configured default from being used. Calling setLanguage() persists the selection for the next reload.


Additional API

Beyond Parrot and setLanguage, these utilities are exported:

| Export | Description | | ---------------------- | --------------------------------------------------------------------------------------------------------------------- | | getLanguage() | Returns the current language code | | detectLanguage() | Resolves URL, stored, browser, cookie, and default language sources safely | | initLanguage(lang?) | Reads stored language from localStorage, calls setLanguage() | | onLanguageChange(cb) | Register a listener that fires after every setLanguage() or patchKeys() call | | patchKeys(config) | Additive key merge — overwrites matching keys, keeps the rest. See PATCHKEYS.md | | DefaultPlaceholders | Global fallback values for placeholders (.replace(), .update(), .set(), .delete(), .get(), .clear()) | | DefaultVariants | Global fallback values for variant selection (.replace(), .update(), .set(), .delete(), .get(), .clear()) | | pluralRules | User override map for plural rules. Intl.PluralRules is the default for all CLDR locales | | ParrotGroups | Grouped access to translations by YAML file name |


Agent Guidance

Published packages include self-contained guidance and relevant source code. Agents reviewing an installed dependency should start at node_modules/eze-lang/AGENTS.md, which routes to node_modules/eze-lang/.agent/_index.md and the focused runtime/tooling docs.


Final Thoughts

Eze-Lang empowers developers by:

  • Effortless Organization: Managing translations in simple YAML files.
  • Type Safety & Autocompletion: With generated TypeScript definitions (Parrot.Types.ts).
  • Dynamic Message Composition: Using placeholders, conditions, variants, and parrotHolders for flexible translations.
  • Seamless Integration: Leveraging the Vite plugin for real-time localization.
  • Optimized Performance: Enjoying minimal runtime overhead with precompiled interpolation.

Eze-Lang is crafted to make localization intuitive, efficient, and enjoyable. Dive in and experience a smoother workflow for managing translations in your projects!