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

featurely-i18n

v1.0.0

Published

Translation management SDK for Featurely — load, cache, and interpolate translations with automatic cache invalidation

Readme

featurely-i18n

Translation SDK for Featurely — load, cache, and interpolate translations managed in your Featurely dashboard.

📦 Installation

npm install featurely-i18n

🚀 Quick Start

import { FeaturelyI18n } from "featurely-i18n";

const i18n = new FeaturelyI18n({
  apiKey: "ft_live_your_api_key",
  projectId: "your-project-id",
  locale: "en",
});

await i18n.init();

console.log(i18n.t("welcome.title"));
// → "Welcome to our app"

console.log(i18n.t("welcome.greeting", "Hello!", { name: "Jane" }));
// → "Hello, Jane!"

✨ Features

  • Dashboard-managed translations — add keys and translations from your Featurely dashboard
  • ETag-based caching — translations are stored in localStorage and only re-downloaded when content changes; cache hits cost zero payload
  • Variable interpolation{{variableName}} placeholders replaced at runtime
  • Fallback locale — fall back to another locale for missing keys
  • Locale switching — change locale at runtime without page reload
  • Preloading — warm up a locale's cache in the background
  • Debug integration — registers with the featurely-site-manager debug overlay when both packages are on the page

📖 Usage

Basic Setup

import { FeaturelyI18n } from "featurely-i18n";

const i18n = new FeaturelyI18n({
  apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
  projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
  locale: "en",
});

await i18n.init();

Variable Interpolation

Use {{variableName}} in your translation strings:

// Dashboard key: "welcome.greeting"
// Default text:  "Hello, {{name}}! You have {{count}} messages."
i18n.t("welcome.greeting", undefined, { name: "Jane", count: 3 });
// → "Hello, Jane! You have 3 messages."

Fallback Locale

When a key is missing in the active locale, the SDK tries the fallback locale:

const i18n = new FeaturelyI18n({
  apiKey: "ft_live_...",
  projectId: "proj_...",
  locale: "no",
  fallbackLocale: "en", // use English if key missing in Norwegian
});

await i18n.init();

Switching Locale at Runtime

await i18n.setLocale("no");
// Loads Norwegian translations (from cache if available, else from server)
// Then re-renders as needed

Preloading

Warm up the cache for a locale without switching to it:

// Preload Norwegian in the background while user is on English
await i18n.preload("no");
// Later, setLocale("no") will be instant

React Integration

import { useState, useEffect } from "react";
import { FeaturelyI18n } from "featurely-i18n";

const i18n = new FeaturelyI18n({
  apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
  projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
  locale: "en",
});

function App() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    i18n.init().then(() => setReady(true));
  }, []);

  if (!ready) return null;

  return <h1>{i18n.t("home.title")}</h1>;
}

Next.js App Router

// lib/i18n.ts  (shared singleton)
import { FeaturelyI18n } from "featurely-i18n";

export const i18n = new FeaturelyI18n({
  apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
  projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
  locale: "en",
});

// app/layout.tsx
"use client";
import { useEffect, useState } from "react";
import { i18n } from "@/lib/i18n";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    i18n.init().then(() => setReady(true));
  }, []);

  return (
    <html>
      <body>{ready ? children : null}</body>
    </html>
  );
}

Vue

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { FeaturelyI18n } from "featurely-i18n";

const i18n = new FeaturelyI18n({
  apiKey: import.meta.env.VITE_FEATURELY_API_KEY,
  projectId: import.meta.env.VITE_FEATURELY_PROJECT_ID,
  locale: "en",
});

const ready = ref(false);
onMounted(async () => {
  await i18n.init();
  ready.value = true;
});
</script>

🔧 Configuration

I18nConfig

| Option | Type | Required | Default | Description | | ---------------- | --------- | -------- | ---------------------------- | ------------------------------------------- | | apiKey | string | ✅ | - | Your Featurely API key | | projectId | string | ✅ | - | Your Featurely project ID | | locale | string | ✅ | - | Initial locale to load (e.g. "en", "no") | | apiUrl | string | ❌ | "https://www.featurely.no" | Custom API endpoint | | fallbackLocale | string | ❌ | - | Locale used when a key is missing | | debug | boolean | ❌ | false | Log debug info to the console |

🎯 API Reference

init(): Promise<void>

Initialize the SDK. Loads cached translations immediately, then validates them against the server (ETag). Must be called once before using t().

await i18n.init();

t(key, fallback?, vars?): string

Translate a key with optional fallback text and variable interpolation.

i18n.t("nav.home")
// → "Home"

i18n.t("nav.missing", "Fallback text")
// → "Fallback text"  (key not found)

i18n.t("greet.user", undefined, { name: "Jane" })
// → "Hello, Jane!"  (from "Hello, {{name}}!")

setLocale(locale: string): Promise<void>

Switch to a different locale. Clears the current translations and loads the new locale.

await i18n.setLocale("no");

getLocale(): string

Returns the currently active locale.

i18n.getLocale(); // "en"

isReady(): boolean

Returns true once init() has completed.

if (i18n.isReady()) {
  render();
}

preload(locale: string): Promise<void>

Warm up the cache for a locale without switching to it.

await i18n.preload("de");

getKeys(): string[]

Returns all translation keys loaded for the active locale.

const keys = i18n.getKeys();
// → ["home.title", "home.subtitle", "nav.home", ...]

💾 Caching

Translations are stored in localStorage under prefixed keys:

_ft_i18n_{projectId}_translations:{locale}   → serialised translations object
_ft_i18n_{projectId}_etag:{locale}           → ETag hash for cache validation

On every init() call the SDK:

  1. Loads cached translations synchronously — t() works immediately
  2. Sends the stored ETag in an If-None-Match request header
  3. If the server responds 304 Not Modified — keeps the cache, no payload transferred
  4. If the server responds 200 — stores the new translations and ETag

This means repeat page loads are near-instant and bandwidth-free when translations haven't changed.

🌐 Managing Translations

All translation keys and their values are managed from your Featurely dashboard:

  1. Go to Dashboard → Translations
  2. Configure your supported locales and default locale
  3. Add translation keys (e.g. home.title, nav.login)
  4. Enter translations for each locale
  5. The SDK picks up changes on the next page load (or immediately via setLocale())

Key Format

Keys may only contain letters, digits, dots (.), underscores (_), and hyphens (-).

home.title
nav.login
errors.not_found
button.save-draft

Variable Placeholders

Wrap variable names in double curly braces in your translation strings:

Hello, {{name}}!
You have {{count}} unread messages.
Expires on {{date}}.

Then pass values to t():

i18n.t("greet", undefined, { name: "Jane", count: 5, date: "Dec 31" });

🐛 Debug Integration

When featurely-site-manager is also installed, featurely-i18n automatically registers itself with the debug overlay via window.__FEATURELY_DEBUG__. This lets you inspect translation state from the SDK tab of the debug overlay.

Enable the debug overlay from your Featurely dashboard → Maintenance → Debug Overlay.

🔒 Content Security Policy (CSP)

If your site uses a Content-Security-Policy header, add https://www.featurely.no to connect-src:

connect-src 'self' https://www.featurely.no;

📱 Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers

🔗 Links

💬 Support