@forgedevstack/lingo
v1.0.1
Published
Translation and localization library for ForgeStack. Manage languages, keys, and AI-powered translations.
Downloads
261
Maintainers
Readme
Lingo
Translation and localization library for ForgeStack. Manage languages, resolve keys, handle plurals, and switch locales — all with a clean API.
Use Lingo Portal to manage translations visually: create a project, add keys, and AI-translate to all languages in one click.
ForgeStack
Lingo is part of the ForgeStack ecosystem. The Lingo Portal (the web app where you manage projects and keys) is built with:
| Package | Purpose |
|--------|--------|
| @forgedevstack/bear | Full UI kit — buttons, inputs, cards, modals, typography, icons, alerts, tabs, etc. |
| @forgedevstack/forge-compass | Client-side routing — CompassProvider, Routes, useNavigate, useRoute. |
| @forgedevstack/grid-table | Data tables for listing and editing (e.g. keys, projects). |
You can use Lingo in any stack. If you build with React, Bear and Compass pair well for UI and navigation; grid-table is optional for table-heavy admin screens.
Features
- Key resolution — Dot-separated nested keys (
auth.login.title). - Interpolation —
Hello, {{name}}!→Hello, John!. - Plurals — CLDR plural categories (
zero,one,two,few,many,other) for 30+ languages. - RTL support — Automatic
dirandlangon the document and provider wrapper. - Local or remote — Bundle translations or fetch from Lingo Portal API.
- Caching — Persist selected locale in
localStorage. - Framework-agnostic core — React adapter included; core works anywhere.
- Tiny — Zero dependencies (React optional).
Install
npm install @forgedevstack/lingoReact is optional (peer dependency when using the React build):
npm install @forgedevstack/lingo reactQuick start
Local translations
import { createLingo, LingoProvider, useLingo } from '@forgedevstack/lingo';
const lingo = createLingo({
defaultLocale: 'en',
fallbackLocale: 'en',
locales: ['en', 'es', 'fr', 'ar'],
source: {
type: 'local',
translations: {
en: {
greeting: 'Hello, {{name}}!',
auth: { login: 'Sign In', register: 'Sign Up' },
items: { one: '{{count}} item', other: '{{count}} items' },
},
es: {
greeting: '¡Hola, {{name}}!',
auth: { login: 'Iniciar sesión', register: 'Registrarse' },
items: { one: '{{count}} elemento', other: '{{count}} elementos' },
},
},
},
});
function App() {
return (
<LingoProvider instance={lingo}>
<MyPage />
</LingoProvider>
);
}
function MyPage() {
const { t, locale, setLocale, direction } = useLingo();
return (
<div>
<h1>{t('greeting', { name: 'John' })}</h1>
<p>{t('auth.login')}</p>
<p>{t('items', { count: 5 })}</p>
<p>Direction: {direction}</p>
<button onClick={() => setLocale('es')}>Español</button>
<button onClick={() => setLocale('ar')}>العربية</button>
</div>
);
}Remote translations (Lingo Portal)
Configure Lingo to load translations from the Portal API (e.g. after creating a project and copying the API URL from the Export page):
import { createLingo } from '@forgedevstack/lingo';
const lingo = createLingo({
defaultLocale: 'en',
fallbackLocale: 'en',
locales: ['en', 'es', 'fr'],
source: {
type: 'remote',
projectId: 'proj_abc123',
endpoint: 'https://your-lingo-portal.com/api',
apiKey: 'your_optional_api_key', // if your API requires it
},
cache: true,
storageKey: 'lingo_locale',
});Translations are fetched when the user switches locale. The API returns a flat key → value map for the requested locale.
API reference
createLingo(config)
Creates a Lingo instance.
| Config | Type | Description |
|--------|------|-------------|
| defaultLocale | string | Locale to use on first load |
| fallbackLocale | string | Locale to fall back to when a key is missing |
| locales | string[] | Supported locale codes |
| source | TranslationSource | { type: 'local', translations } or { type: 'remote', projectId, endpoint?, apiKey? } |
| cache | boolean | Persist locale in localStorage (default: true) |
| storageKey | string | localStorage key (default: 'lingo_locale') |
| debug | boolean | Log missing keys (default: true) |
| onLocaleChange | (locale: string) => void | Callback on locale change |
| onMissingKey | (key: string, locale: string) => void | Callback when a translation is missing |
Instance methods
| Method | Description |
|--------|-------------|
| t(key, vars?) | Translate a key with optional interpolation vars |
| locale | Current active locale |
| setLocale(locale) | Change locale (async; may fetch remote) |
| locales | Array of supported locales |
| direction | 'ltr' or 'rtl' for the current locale |
| getLocaleInfo(locale) | Get locale metadata (name, nativeName, flag, direction) |
| isLoading | true while fetching remote translations |
| subscribe(listener) | Listen for state changes; returns unsubscribe fn |
| addTranslations(locale, map) | Add or merge translations at runtime |
React exports
| Export | Description |
|--------|-------------|
| <LingoProvider instance={lingo}> | Wraps app with Lingo context; sets dir and lang |
| useLingo() | Returns { t, locale, setLocale, locales, direction, isLoading, getLocaleInfo } |
| <T k="key" vars={{}} /> | Inline translation component |
| <Lingo k="key" vars={{}} /> | Same as <T>, with optional className and style |
| <LocaleSwitcher /> | Simple <select> dropdown for switching locales |
Interpolation
Use {{variableName}} in translation strings and pass vars as the second argument to t() or via the vars prop on <T> / <Lingo>:
// String: "Hello, {{name}}!"
t('greeting', { name: 'Jane' }); // → "Hello, Jane!"
// With <T>
<T k="greeting" vars={{ name: 'Jane' }} />Plurals
Define plural forms using CLDR categories in your translation map:
{
"items": {
"zero": "No items",
"one": "{{count}} item",
"other": "{{count}} items"
}
}Pass count in vars: t('items', { count: 3 }) → "3 items".
Supported plural rules: English, Spanish, French, Portuguese, German, Russian, Ukrainian, Polish, Czech, Arabic, Hebrew, Japanese, Korean, Chinese, and more.
RTL
When you switch to an RTL locale (ar, he, fa, ur), Lingo automatically:
- Sets
document.documentElement.dir = 'rtl' - Sets
document.documentElement.langto the locale - Wraps the provider children in a
<div dir="rtl">
Utilities
import {
flattenTranslations,
unflattenTranslations,
mergeTranslations,
getDirection,
} from '@forgedevstack/lingo';| Utility | Description |
|---------|-------------|
| flattenTranslations(map) | Nested object → flat dot-separated keys |
| unflattenTranslations(flat) | Flat key-value → nested map |
| mergeTranslations(a, b) | Deep merge (b overrides a) |
| getDirection(locale) | Returns 'ltr' or 'rtl' |
Locale catalog
LOCALE_CATALOG provides metadata for 30+ locales (name, nativeName, direction, flag):
import { LOCALE_CATALOG } from '@forgedevstack/lingo';
// e.g. { code: 'ar', name: 'Arabic', nativeName: 'العربية', direction: 'rtl', flag: '🇸🇦' }Package entry points
@forgedevstack/lingo— Core + React (createLingo, LingoProvider, useLingo, <T>, <Lingo>, LocaleSwitcher, utilities, types).@forgedevstack/lingo/react— Same React components and hooks; use this subpath if you want to tree-shake the core.
Version
1.0.0
