featurely-i18n
v1.0.0
Published
Translation management SDK for Featurely — load, cache, and interpolate translations with automatic cache invalidation
Maintainers
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
localStorageand 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-managerdebug 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 neededPreloading
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 instantReact 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 validationOn every init() call the SDK:
- Loads cached translations synchronously —
t()works immediately - Sends the stored ETag in an
If-None-Matchrequest header - If the server responds 304 Not Modified — keeps the cache, no payload transferred
- 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:
- Go to Dashboard → Translations
- Configure your supported locales and default locale
- Add translation keys (e.g.
home.title,nav.login) - Enter translations for each locale
- 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-draftVariable 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
- Open a GitHub issue
- Contact [email protected]
