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

@mango.dev/core

v0.1.1

Published

A TypeScript-first library that translates any JavaScript object into multiple languages in one call, powered by [lingo.dev](https://lingo.dev).

Downloads

166

Readme

@mango.dev/core

A TypeScript-first library that translates any JavaScript object into multiple languages in one call, powered by lingo.dev.

Features

  • Deep object traversal — translates strings inside nested objects, arrays, and mixed structures
  • Type-safe APIPaths<T> and Translated<T, Excluded, Lang> utility types give full IntelliSense on your data shape
  • Smart skipping — automatically ignores URLs, emails, numbers, hex colors, dates, and slugs
  • Exclude paths — opt specific fields out of translation using dot-notation paths ("user.email", "tags[]")
  • Progress callback — receive 0–100 progress updates during translation
  • Fast mode — trade quality for speed when needed
  • Configurable batching — tune batchSize and idealBatchItemSize for your use case

Installation

# npm
npm install @mango.dev/core

# pnpm
pnpm add @mango.dev/core

# yarn
yarn add @mango.dev/core

Usage

Basic example

import { Mango } from "@mango.dev/core";

// Get your API key at https://lingo.dev — free Hobby tier is sufficient
// Store it in an environment variable, never hardcode it
const mango = new Mango({
  api_key: process.env.LINGODOTDEV_API_KEY!,
  langs: ["en", "es", "fr", "de"],
  sourceLang: "en",
});

const data = {
  title: "Hello World",
  description: "A simple greeting",
};

const translated = await mango.translate(data);

// translated.title  → { en: "Hello World", es: "Hola Mundo", fr: "Bonjour le Monde", de: "Hallo Welt" }
// translated.description → { en: "...", es: "...", fr: "...", de: "..." }

Nested objects & arrays

const product = {
  id: "prod-123",           // skipped — slug pattern
  price: 9.99,              // skipped — number
  name: "Running Shoes",
  tags: ["sport", "outdoor"],
  details: {
    material: "Mesh",
    care: "Machine washable",
  },
};

const result = await mango.translate(product);
// result.name     → { en: "Running Shoes", es: "Zapatos para Correr", ... }
// result.tags[0]  → { en: "sport", es: "deporte", ... }
// result.id       → "prod-123"  (unchanged)
// result.price    → 9.99        (unchanged)

Translating an array of items

Wrap arrays in an object so that exclude autocomplete shows real field names instead of array prototype methods (map, at, concat…).

const posts = [
  { id: 1, title: "Hello World", body: "My first post." },
  { id: 2, title: "Getting Started", body: "Here is how to begin." },
];

// ✅ wrap in an object
const result = await mango.translate({ posts });

// result.posts[0].title → { en: "Hello World", es: "Hola Mundo", fr: "Bonjour le Monde", ... }
// result.posts[0].body  → { en: "My first post.", es: "Mi primera publicación.", ... }
// result.posts[0].id    → 1  (unchanged — number)

Excluding paths

const article = {
  slug: "my-article",
  author: "[email protected]",
  title: "Getting Started",
  body: "Welcome to the guide.",
};

const result = await mango.translate(article, {
  exclude: ["slug", "author"],
});

// result.slug   → "my-article"      (excluded)
// result.author → "[email protected]" (excluded)
// result.title  → { en: "...", es: "...", ... }

Progress tracking

const result = await mango.translate(largeDataset, {
  onProgress: (progress) => {
    console.log(`Translation progress: ${progress}%`);
  },
});

Fast mode

const result = await mango.translate(data, { fast: true });

Configuration

MangoConfig

| Option | Type | Required | Default | Description | | -------------------- | ---------- | -------- | ------- | ------------------------------------------------ | | api_key | string | ✅ | — | Your lingo.dev API key — get one free at lingo.dev | | langs | string[] | ✅ | — | All languages to translate into (includes source)| | sourceLang | string | ✅ | — | The language of the input data | | batchSize | number | ❌ | 50 | Max items per API request (max: 250) | | idealBatchItemSize | number | ❌ | 500 | Target word count per batch (max: 2500) |

TranslateOptions

| Option | Type | Description | | ------------ | --------------------------- | -------------------------------------------------- | | exclude | Paths<T>[] | Dot-notation paths to skip during translation | | fast | boolean | Prioritize speed over translation quality | | onProgress | (progress: number) => void| Callback fired with progress value between 0–100 |

Security

@mango.dev/core requires a lingo.dev API key. Never use it in client-side (browser) code — the key will be exposed in your JavaScript bundle.

Safe patterns:

  • Server actions / API routes — call mango.translate() on the server only. The key never reaches the client.

    // app/actions.ts (Next.js server action)
    "use server";
    import { Mango } from "@mango.dev/core";
    
    const mango = new Mango({ api_key: process.env.LINGODOTDEV_API_KEY!, ... });
    
    export async function translatePosts(posts) {
      return mango.translate({ posts });
    }
  • Build-time translation — translate once during the build, persist the results, and ship only the translated data to the client. No API key in production at all.

  • Environment variable — always load the key from an environment variable, never hardcode it.

    const mango = new Mango({ api_key: process.env.LINGODOTDEV_API_KEY!, ... });

Type Utilities

Paths<T>

Not intended for direct use. Paths<T> works silently behind the scenes to power autocomplete and type-checking on the exclude option. You never need to import or call it yourself.

Translated<T, Excluded, Lang>

Transforms a type so that all non-excluded string fields become Record<Lang, string>.

type Result = Translated<{ title: string; id: string }, "id", "en" | "es">
// → { title: Record<"en" | "es", string>; id: string }

How it works

mg.translate() does three things in sequence: traverse → skip → translate.

mg.translate({ posts }, { exclude: ["posts[].id"] })
         |
         v
+-----------------------------------------------------+
|                   traverse()                        |
|                                                     |
|  walks every node in the object tree recursively    |
|                                                     |
|  for each node:                                     |
|                                                     |
|  number / boolean -----------------> return as-is   |
|                                                     |
|  string                                             |
|    +-- excluded path? -----------> return as-is     |
|    +-- URL / email / slug /                         |
|    |   hex / date / number? ------> return as-is    |
|    +-- translatable                                 |
|              |                                      |
|              v                                      |
|    engine.batchLocalizeText(value, {                |
|      sourceLocale: "en",                            |
|      targetLocales: ["hi", "fr"]  <- one API call   |
|    })                               per string      |
|              |                                      |
|              v                                      |
|    { en: "...", hi: "...", fr: "..." }              |
|                                                     |
|  array  --> recurse each item with indexed path     |
|  object --> recurse each key with dot-notation      |
+-----------------------------------------------------+
         |
         v
  Translated<T, Excluded, Lang>  <- fully typed result

Key behaviours:

  • Each translatable string is one batchLocalizeText call — all target languages returned at once, not one call per language
  • Circular references are tracked via a seen: Set<object> and safely skipped
  • exclude paths support dot-notation ("author.email"), array wildcards ("tags[]"), and nested array fields ("posts[].id")

How Lingo.dev is used

Mango.dev doesn't implement any translation logic itself. Under the hood it uses LingoDotDevEngine from the lingo.dev SDK:

new Mango({ api_key, langs })
        |
        v
  LingoDotDevEngine          <- from "lingo.dev/sdk"
        |
        +-- handles API communication
        +-- batches strings for efficiency
        +-- retries on failure
        +-- batchLocalizeText(value, { sourceLocale, targetLocales })

Mango.dev's job is the layer on top — traversing your object tree, deciding what to skip, mapping results back to the original shape, and exposing a type-safe TypeScript API. The actual AI translation is entirely handled by Lingo.dev.