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

anylang-dev

v0.2.2

Published

Bring-your-own-key website translation JSON generator.

Readme

anylang-dev

anylang-dev is a bring-your-own-key website translation CLI with Vite and Next.js support. It scans your source code, writes JSON locale files, and can automatically translate static JSX text.

<h1>Translate your website with anylang</h1>
<p tr="false">This text stays as it is</p>

For dynamic text, use the generated useTr hook:

export function Hero() {
  const $tr = useTr();

  return (
    <section>
      <h1>Welcome back</h1>
      <button>{$tr("actions.save", "Save")}</button>
      <p tr="false">BrandName</p>
    </section>
  );
}

By default, anylang scans .js, .jsx, .ts, .tsx, .vue, and .html files under src.

Language selector

anylang does not require a built-in selector. Build any selector UI you want and pass the selected locale to setLanguage.

const { language, languages, setLanguage } = useLanguage();

return (
  <select
    value={language}
    onChange={(event) => setLanguage(event.target.value as LanguageCode)}
  >
    {languages.map((language) => (
      <option key={language.code} value={language.code}>
        {language.nativeLabel}
      </option>
    ))}
  </select>
);

Use $tr("key", "source text") anywhere in the same render tree:

<h1>{$tr("hero.title", "Translate your website with anylang")}</h1>

Quick start

npm link
anylang init
anylang scan

Add the Vite plugin:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import anylang from "anylang-dev/vite";

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

For Next.js, use the config wrapper in next.config.mjs:

import anylang from "anylang-dev/next";

const nextConfig = {};

export default anylang({
  runtimeImport: "@/anylang",
})(nextConfig);

If you use tr="false", add the JSX type augmentation once in src/vite-env.d.ts:

import "anylang-dev/jsx-runtime";

That makes this TypeScript-safe:

<p tr="false">BrandName</p>

In a Next.js app, you can put the same import in any global declaration file, such as src/anylang-env.d.ts.

anylang scan creates locale files without calling a translation provider. To translate for real with Gemini, add your own API key to .env in the project where you run anylang:

GEMINI_API_KEY=your-provider-key

Then run:

anylang translate

Config

anylang init creates:

{
  "sourceLocale": "en",
  "targetLocales": ["hi"],
  "include": ["src/**/*.{js,jsx,ts,tsx,vue,html}"],
  "exclude": ["node_modules", ".git", "dist", "build", ".next"],
  "outDir": "locales",
  "runtime": {
    "output": "src/anylang.ts",
    "importFrom": "anylang-dev/runtime"
  },
  "functionName": "$tr",
  "autoTranslate": {
    "jsx": true,
    "keyPrefix": "auto"
  },
  "provider": {
    "name": "gemini",
    "model": "gemini-2.5-flash"
  }
}

The provider is intentionally BYOK. anylang does not include a platform key, proxy requests, track usage, or store billing data. It automatically loads .env from the current project before calling the provider.

Providers

Choose a provider by setting provider.name. Each provider reads its standard API key from .env.

| Provider | provider.name | .env key | | --- | --- | --- | | Gemini | gemini | GEMINI_API_KEY | | OpenAI | openai | OPENAI_API_KEY | | Anthropic | anthropic | ANTHROPIC_API_KEY | | Cohere | cohere | COHERE_API_KEY | | Mistral | mistral | MISTRAL_API_KEY | | DeepSeek | deepseek | DEEPSEEK_API_KEY | | Groq | groq | GROQ_API_KEY | | OpenRouter | openrouter | OPENROUTER_API_KEY | | Perplexity | perplexity | PERPLEXITY_API_KEY | | xAI | xai | XAI_API_KEY | | Together AI | together | TOGETHER_API_KEY | | Fireworks AI | fireworks | FIREWORKS_API_KEY | | Custom OpenAI-compatible | openai-compatible | ANYLANG_API_KEY |

Example:

{
  "provider": {
    "name": "anthropic",
    "model": "claude-3-5-haiku-latest"
  }
}

For custom OpenAI-compatible gateways, provide baseUrl and model:

{
  "provider": {
    "name": "openai-compatible",
    "baseUrl": "https://your-gateway.example.com/v1",
    "model": "your-model"
  }
}

Output

Scanning or translating creates:

locales/
  en.json
  hi.json
  anylang.lock.json
src/
  anylang.ts

The lock file stores SHA-256 fingerprints so unchanged strings are skipped on later runs.

Workflow

  1. Write normal static JSX text:
<h1>Translate your website with anylang</h1>
<p tr="false">Do not translate this text</p>

Use $tr("key", "source text") only for dynamic or special cases.

  1. Scan the project:
anylang scan

This writes keyed source entries to locales/en.json and creates placeholder entries in each target locale. It also generates src/anylang.ts, which imports all locale JSON files and exports runtime helpers.

  1. Translate with Gemini:
GEMINI_API_KEY=your-gemini-api-key
anylang translate

This scans again, sends missing or changed target entries to Gemini, and writes the translated text into files like locales/hi.json.

Source locale output:

{
  "auto.src_app.translate_your_website_with_anylang_a1b2c3d4": {
    "text": "Translate your website with anylang",
    "variables": []
  }
}

Target locale output:

{
  "auto.src_app.translate_your_website_with_anylang_a1b2c3d4": {
    "source": "Translate your website with anylang",
    "text": "anylang से अपनी वेबसाइट का अनुवाद करें",
    "variables": []
  }
}

On later runs, anylang translate compares targetEntry.source against the current source text. If they differ, it retranslates that key. If they match, it keeps the existing translation and skips the AI call.

Runtime

Import the generated runtime file in your app:

import {
  AnyLangProvider,
  useLanguage,
  useTr,
  type LanguageCode
} from "@/anylang";

You do not manually import en.json, hi.json, ja.json, etc. The generated file does that for you based on sourceLocale and targetLocales.

Wrap your app once:

root.render(
  <AnyLangProvider>
    <App />
  </AnyLangProvider>
);

For the Next.js App Router, create a client provider:

// app/providers.tsx
"use client";

import { AnyLangProvider } from "@/anylang";

export function Providers({ children }: { children: React.ReactNode }) {
  return <AnyLangProvider>{children}</AnyLangProvider>;
}

Then wrap your layout:

// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Then use translations in any component:

function Hero() {
  return <h1>Translate your website with anylang</h1>;
}

For dynamic text:

function SaveButton() {
  const $tr = useTr();
  return <button>{$tr("actions.save", "Save")}</button>;
}

And build any selector with useLanguage:

function LanguageSelector() {
  const { language, languages, setLanguage } = useLanguage();

  return (
    <select
      value={language}
      onChange={(event) => setLanguage(event.target.value as LanguageCode)}
    >
      {languages.map((language) => (
        <option key={language.code} value={language.code}>
          {language.nativeLabel}
        </option>
      ))}
    </select>
  );
}