grackle
v1.0.0
Published
Translate framework with ICU support
Maintainers
Readme
Grackle
A lightweight internationalization (i18n) translation framework for Node.js with full ICU MessageFormat support.
Features
- ICU MessageFormat - Full support for pluralization, gender, and complex message formatting
- Namespace Support - Organize translations by feature/module
- Multiple Instances - Create isolated translation contexts
- Fallback Strategy - Graceful fallback when translations are missing
- Zero Dependencies - Only depends on
intl-messageformat - Vanilla JavaScript - Works with ES5, no build step required
- TypeScript Support - Includes
.d.tstype definitions
Installation
npm install grackle
# or
yarn add grackleQuick Start
const tr = require("grackle");
// Learn translations
tr.learn({
en: {
hello: "Hello world",
NUM_PHOTOS:
"{numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}",
},
"zh-CN": {
hello: "你好世界",
NUM_PHOTOS: "{numPhotos, plural, =0 {没有照片} other {# 张照片}}",
},
});
// Set locale
tr.setLocale("en");
// Translate
tr("hello"); // 'Hello world'
tr("NUM_PHOTOS", { numPhotos: 1 }); // 'one photo'API Reference
tr.learn(langs, namespace?)
Register translation dictionaries.
tr.learn({
en: { OK: "OK" },
"zh-CN": { OK: "确定" },
});Parameters:
langs(LangGroup) - Object mapping locale codes to message dictionariesnamespace(string, optional) - Namespace name to store translations
tr(message, namespace?, values?, formats?)
Translate a message to the current locale.
tr("OK"); // 'OK'
tr("OK", "login"); // 'Login' (from 'login' namespace)
tr("NUM_PHOTOS", { numPhotos: 5 }); // '5 photos' (ICU format)Parameters:
message(string | string[]) - Message key or array of keys for fallbacknamespace(string, optional) - Namespace to searchvalues(Object, optional) - Values for ICU message interpolationformats(Object, optional) - Custom format overrides
Returns: Translated string or original message key if not found
tr.setLocale(locale)
Set the current locale for translations.
tr.setLocale("zh-CN");
tr("hello"); // '你好世界'Parameters:
locale(string) - Locale code (e.g., 'en', 'zh-CN', 'fr-FR')
tr.getLocale()
Get the current locale.
tr.setLocale("en");
tr.getLocale(); // 'en'Returns: Current locale code string
tr.locale(locale)
Create a scoped translation function for a specific locale.
const enTranslate = tr.locale("en");
const zhTranslate = tr.locale("zh-CN");
enTranslate("hello"); // 'Hello world'
zhTranslate("hello"); // '你好世界'Parameters:
locale(string) - Locale code to bind to
Returns: Translation function that always uses the specified locale
tr.create()
Create a new independent Grackle instance.
const app1 = tr.create();
const app2 = tr.create();
app1.learn({ en: { HELLO: "Hello from app1" } });
app2.learn({ en: { HELLO: "Hello from app2" } });
app1.setLocale("en");
app2.setLocale("en");
app1("HELLO"); // 'Hello from app1'
app2("HELLO"); // 'Hello from app2'Returns: New Grackle instance with isolated state
Advanced Usage
Namespaces
Organize translations by feature or module:
// Learn default translations
tr.learn({
en: {
OK: "OK",
CANCEL: "Cancel",
},
});
// Learn login module translations
tr.learn(
{
en: {
OK: "Login",
CANCEL: "Close",
},
},
"login",
);
// Learn payment module translations
tr.learn(
{
en: {
OK: "Pay",
CANCEL: "Abort",
},
},
"payment",
);
tr.setLocale("en");
tr("OK"); // 'OK' (default)
tr("OK", "login"); // 'Login' (login namespace)
tr("OK", "payment"); // 'Pay' (payment namespace)
tr("OK", "settings"); // 'OK' (fallback to default)Namespace fallback priority:
- Specified namespace
- Default namespace (
_defaults) - Original message key (if not found)
ICU Message Format
Grackle supports the full ICU MessageFormat syntax for complex message formatting:
Pluralization:
tr.learn({
en: {
NUM_PHOTOS:
"{numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}",
},
});
tr("NUM_PHOTOS", { numPhotos: 0 }); // 'no photos'
tr("NUM_PHOTOS", { numPhotos: 1 }); // 'one photo'
tr("NUM_PHOTOS", { numPhotos: 5 }); // '5 photos'Date/Number Formatting:
tr.learn({
en: {
BALANCE: "Your balance: {balance, number, currency}",
},
});
tr("BALANCE", { balance: 1234.56 }); // 'Your balance: $1,234.56'Selection:
tr.learn({
en: {
GREETING: "{gender, select, male {He} female {She} other {They}}",
},
});
tr("GREETING", { gender: "male" }); // 'He'
tr("GREETING", { gender: "female" }); // 'She'For more ICU syntax details, see the ICU MessageFormat Guide.
Message Key Fallback
Use an array of message keys to try multiple keys in order:
tr.learn({
en: {
PAY_OK: "Pay now",
OK: "OK",
},
});
tr(["PAY_OK", "OK"]); // 'Pay now' (first match)
tr(["LOGIN_OK", "OK"]); // 'OK' (fallback to 'OK')
// With namespaces
tr(["PAY_OK", "OK"], "login"); // 'Pay now' (searches all namespaces)Fallback behavior:
- Iterate through keys, return first match
- If no match found, return the last key in array
Multiple Instances
Create isolated translation contexts for different parts of your application:
// Default instance
const mainApp = require("grackle");
mainApp.learn({ en: { HELLO: "Main App" } });
mainApp.setLocale("en");
// Admin panel instance
const admin = mainApp.create();
admin.learn({ en: { HELLO: "Admin Panel" } });
admin.setLocale("en");
// User portal instance
const user = mainApp.create();
user.learn({ en: { HELLO: "User Portal" } });
user.setLocale("en");
mainApp("HELLO"); // 'Main App'
admin("HELLO"); // 'Admin Panel'
user("HELLO"); // 'User Portal'Each instance maintains its own:
- Translation dictionaries
- Namespace mappings
- Current locale
TypeScript Usage
import tr from "grackle";
tr.learn({
en: {
hello: "Hello world",
},
"zh-CN": {
hello: "你好世界",
},
});
tr.setLocale("en");
const message: string = tr("hello");Full TypeScript definitions are included in index.d.ts.
Contributing
License
MIT License - see LICENSE for details.
