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

zintljs

v0.1.0-alpha.20

Published

Compile-time internationalization for Vite — no keys, no wrappers, and only the translations each screen actually needs.

Readme

Zintl is a compile-time internationalization engine.

Most i18n libraries ask you to change how you write code — wrap every string in t(), invent a key for it, keep a dictionary in sync by hand. Zintl doesn't. You write normal strings; the compiler finds them, works out which ones each part of your app actually needs, and ships exactly those.

Install

npm install -D zintljs

or pnpm add -D zintljs · yarn add -D zintljs

Use

1. Add the plugin.

// vite.config.ts
import { defineConfig } from "vite";
import zintl from "zintljs/vite";

export default defineConfig({
  plugins: [zintl({ locales: ["en", "ar", "fr"] })],
});
// rsbuild.config.ts
import { defineConfig } from "@rsbuild/core";
import zintl from "zintljs/rsbuild";

export default defineConfig({
  plugins: [...zintl({ locales: ["en", "ar", "fr"] })],
});

Same plugin, same options — note the spread, since the Rsbuild entry point returns an array. Everything below applies unchanged. What's covered there.

2. Set a locale.

// src/main.ts
import { zintl } from "zintljs/macro";

const locale = new URLSearchParams(location.search).get("lang") ?? "en";
await zintl(locale);

document.querySelector("#app").innerHTML = `<h1>Welcome back!</h1>`;

That's the whole API. No keys, no wrappers, no dictionary to maintain.

What you pass matters. A variable ships every locale and lets users switch at runtime. A literal — zintl("fr") — tells the compiler this page is French: it bakes that locale in, emits no catalog chunk, and leaves every other locale out of the bundle, source language included. Use a literal for per-locale static builds, a variable whenever language is a choice.

zintljs/vite (or zintljs/rsbuild) is the plugin; zintljs/macro is what you call in app code. Importing the wrong one into your config gives you an async no-op rather than a plugin.

3. Run your dev server. Zintl extracts your strings and writes one file per locale, ready to fill in:

// zintl/src/main.fr.json
{
  "Welcome back!": ""
}

Fill it in and the page updates without a reload. Leave it empty and the production build refuses to ship — a blank string is a bug, not a fallback.

What you get

Only what each screen needs. Zintl works out which strings are reachable from which entry point, and splits catalogs along the same lines your bundler splits code. Opening the settings page downloads the settings translations, not all of them.

Nothing extra in the bundle. Plurals and grammar rules compile to plain JavaScript at build time — no ICU parser reaches the browser. Your source locale is never written to disk; the compiler already has those strings.

Translations that survive refactors. Identity is content-based, not path-based — so translations aren't attached to a file, a line, or a key you have to keep stable. Rename components, move files, restructure whole directories: the translations follow. Even a source string edited into a near-identical one keeps what it had, instead of being orphaned and sent back to translators.

Restructuring an app usually means a day of reconciling catalogs afterwards. Here it means zero.

Grammar where it belongs. Source files keep simple template literals; plural and gender rules live in the catalog, with a generated JSON schema so translators get autocomplete.

const msg = `You have ${count} items in your cart`;
{
  "You have {count} items in your cart": "{count, plural, =0 {سلتك فارغة} one {لديك عنصر واحد} other {لديك {count} عناصر}}"
}

Where it runs

Works with React, Vue, Svelte, and vanilla apps, on Vite 6, 7 or 8 or on Rsbuild 2 — install whichever you build with; both are optional peer dependencies. Node ^22.18.0 || >=24.11.0.

On Vite that covers client-rendered, server-rendered and multi-page apps, with all four frameworks. On Rsbuild it covers single-page and multi-page apps, also with all four, in production builds and in dev. Two exclusions there are worth knowing before you start: per-locale HTML fan-out (multiplex) and SSR are Vite-only, and combining them with Rsbuild fails your build with a clear error rather than doing nothing quietly. Vue components work with either <script setup> or the Options API, on both hosts. The full comparison.

That list is a starting point, not the design. The extractor carries no framework knowledge and the compiler is bundler-agnostic, so another framework or another build tool is something you add rather than something the core is rewritten around. More of both are coming.

Docs

Status

Alpha. The ideas are settled and the test suite is thorough, but the API can still move between releases. Pin your version, and please open an issue when something surprises you — early reports shape this more than anything else right now.

License

MIT © Khalid F. Shuhail