A comprehensive TypeScript internationalization library with plugin-based component registration, enum translation support, template processing, and context management.
Version 1.1.0 introduces a revolutionary plugin-based architecture with component registration and rigid compile-time type safety:
- Component Registration System: Register translation components with their own string keys
- Language Plugin Support: Add new languages dynamically with validation
- Compile-Time Type Safety: TypeScript ensures all strings are complete for all languages
- Automatic Validation: Comprehensive validation with detailed error reporting
- Fallback System: Intelligent fallback to default languages with missing translation detection
- Multi-Instance Support: Named instances for different application contexts
- Type-Safe Translations: Full TypeScript support with generic types for strings and languages
- Plugin Architecture: Register components and languages dynamically with full type safety
- Configuration Validation: Automatic validation ensures all languages have complete string collections
- Localized Error Messages: Error messages can be translated using the engine's own translation system
- Enum Translation Registry: Translate enum values with complete type safety
- Template Processing: Advanced template system with
{{EnumName.EnumKey}}patterns and variable replacement - Context Management: Admin vs user translation contexts with automatic language switching
- Singleton Pattern: Efficient instance management with named instances
- Currency Formatting: Built-in currency formatting utilities with locale support
- Fallback System: Graceful degradation when translations are missing
- Extensible Configuration: Module augmentation support for layered library extension
- Backward Compatibility: Legacy I18nEngine remains fully supported
- Component Registry: Manage translation components with validation
- Language Registry: Dynamic language registration with metadata
- Type-Safe Registration: Compile-time guarantees for translation completeness
- Validation Reporting: Detailed missing translation reports
- Plugin System: Modular component architecture
npm install @digitaldefiance/i18n-libimport { I18nEngine, I18nConfig, createContext, CurrencyCode, Timezone } from '@digitaldefiance/i18n-lib';
// Define your enums
enum MyStrings {
Welcome = 'welcome',
UserGreetingTemplate = 'userGreetingTemplate'
}
enum MyLanguages {
English = 'English',
Spanish = 'Spanish'
}
// Configure the engine
const config: I18nConfig<MyStrings, MyLanguages> = {
stringNames: Object.values(MyStrings),
strings: {
[MyLanguages.English]: {
[MyStrings.Welcome]: 'Welcome!',
[MyStrings.UserGreetingTemplate]: 'Hello, {name}!'
},
[MyLanguages.Spanish]: {
[MyStrings.Welcome]: '¡Bienvenido!',
[MyStrings.UserGreetingTemplate]: '¡Hola, {name}!'
}
},
defaultLanguage: MyLanguages.English,
defaultTranslationContext: 'user',
defaultCurrencyCode: new CurrencyCode('USD'),
languageCodes: {
[MyLanguages.English]: 'en',
[MyLanguages.Spanish]: 'es'
},
languages: Object.values(MyLanguages),
timezone: new Timezone('UTC'),
adminTimezone: new Timezone('UTC')
};
// Create engine
const i18n = new I18nEngine(config);
// Translate strings
const welcome = i18n.translate(MyStrings.Welcome);
// "Welcome!"
const greeting = i18n.translate(MyStrings.UserGreetingTemplate, { name: 'John' });
// "Hello, John!"
// Change language
i18n.context = { language: MyLanguages.Spanish };
const spanishGreeting = i18n.translate(MyStrings.UserGreetingTemplate, { name: 'Juan' });
// "¡Hola, Juan!"The new plugin-based architecture provides a component registration system with rigid compile-time type safety.
import {
createCoreI18nEngine,
CoreStringKey,
CoreLanguage,
ComponentDefinition,
ComponentRegistration
} from '@digitaldefiance/i18n-lib';
// Create engine with default languages and core strings
const i18n = createCoreI18nEngine('myapp');
// Use core translations
const welcomeMessage = i18n.translate('core', CoreStringKey.System_Welcome);
const errorMessage = i18n.translate('core', CoreStringKey.Error_ValidationFailed);
// Define your own component with type safety
enum MyComponentStringKey {
Welcome = 'welcome',
Goodbye = 'goodbye',
UserGreetingTemplate = 'userGreetingTemplate'
}
const MyComponent: ComponentDefinition<MyComponentStringKey> = {
id: 'my-component',
name: 'My Custom Component',
stringKeys: Object.values(MyComponentStringKey)
};
// Define translations for all supported languages
const myComponentStrings = {
[CoreLanguage.EnglishUS]: {
[MyComponentStringKey.Welcome]: 'Welcome to my component!',
[MyComponentStringKey.Goodbye]: 'Goodbye from my component!',
[MyComponentStringKey.UserGreetingTemplate]: 'Hello, {name}!'
},
[CoreLanguage.French]: {
[MyComponentStringKey.Welcome]: 'Bienvenue dans mon composant !',
[MyComponentStringKey.Goodbye]: 'Au revoir de mon composant !',
[MyComponentStringKey.UserGreetingTemplate]: 'Bonjour, {name} !'
},
[CoreLanguage.Spanish]: {
[MyComponentStringKey.Welcome]: '¡Bienvenido a mi componente!',
[MyComponentStringKey.Goodbye]: '¡Adiós desde mi componente!',
[MyComponentStringKey.UserGreetingTemplate]: '¡Hola, {name}!'
}
// TypeScript ensures all CoreLanguages are handled
};
// Register component (with validation)
const registration: ComponentRegistration<MyComponentStringKey, CoreLanguage> = {
component: MyComponent,
strings: myComponentStrings
};
const validationResult = i18n.registerComponent(registration);
if (!validationResult.isValid) {
console.warn('Missing translations:', validationResult.missingKeys);
}
// Use your component's translations
const welcome = i18n.translate('my-component', MyComponentStringKey.Welcome);
const greeting = i18n.translate('my-component', MyComponentStringKey.UserGreetingTemplate, {
name: 'John'
});
// Change language - affects all components
i18n.setLanguage(CoreLanguage.French);
const frenchWelcome = i18n.translate('my-component', MyComponentStringKey.Welcome);
// "Bienvenue dans mon composant !"- Compile-Time Type Safety: TypeScript ensures all string keys exist for all languages
- Component Isolation: Each component manages its own strings independently
- Flexible Language Support: Components can support different subsets of system languages
- Comprehensive Validation: Automatic detection of missing translations with detailed reporting
- Fallback System: Intelligent fallback to default language when translations are missing
- Dynamic Language Addition: Add new languages at runtime with automatic validation updates
- Extensibility: Easy to add new languages and components dynamically
The library includes several pre-built components:
Provides essential system strings in 8 languages:
- English (US/UK), French, Spanish, German, Chinese (Simplified), Japanese, Ukrainian
import { createCoreI18nEngine, CoreStringKey } from '@digitaldefiance/i18n-lib';
const i18n = createCoreI18nEngine();
const saveText = i18n.translate('core', CoreStringKey.Common_Save);
const errorMsg = i18n.translate('core', CoreStringKey.Error_ValidationFailed);Demonstrates user management strings:
import {
registerSuiteCoreComponent,
getUserTranslation,
UserStringKey
} from '@digitaldefiance/i18n-lib';
// Register user system with existing engine
registerSuiteCoreComponent(i18n);
// Use user system translations
const loginText = getUserTranslation(UserStringKey.Auth_Login);
const userNotFound = getUserTranslation(
UserStringKey.Error_UserNotFoundTemplate,
{ username: 'john_doe' }
);By default the plugin engine performs runtime validation and provides fallbacks. If you want compile-time enforcement that every language mapping contains every string key, use the helper in strict-types:
import { createCompleteComponentStrings } from '@digitaldefiance/i18n-lib';
enum MyStrings {
Welcome = 'welcome',
Farewell = 'farewell'
}
type AppLang = 'en' | 'fr';
// This will only compile if BOTH languages contain BOTH keys.
const myStrictStrings = createCompleteComponentStrings<MyStrings, AppLang>({
en: {
[MyStrings.Welcome]: 'Welcome',
[MyStrings.Farewell]: 'Goodbye'
},
fr: {
[MyStrings.Welcome]: 'Bienvenue',
[MyStrings.Farewell]: 'Au revoir'
}
});
// If any key is missing, TypeScript reports an error before runtime.The core library itself uses this helper for the core component (createCoreComponentStrings) to guarantee internal completeness. For partial / iterative authoring you can still start with normal objects and later switch to the strict helper when translations stabilize.
import { createLanguageDefinition } from '@digitaldefiance/i18n-lib';
// Add Italian support
const italian = createLanguageDefinition('it', 'Italiano', 'it');
i18n.registerLanguage(italian);
// Update existing components with Italian translations
i18n.updateComponentStrings('my-component', {
it: {
[MyComponentStringKey.Welcome]: 'Benvenuto nel mio componente!',
[MyComponentStringKey.Goodbye]: 'Arrivederci dal mio componente!',
[MyComponentStringKey.UserGreetingTemplate]: 'Ciao, {name}!'
}
});The plugin engine provides comprehensive validation to ensure translation completeness:
// Each component is validated against ALL system languages
enum MyStrings {
Welcome = 'welcome',
Goodbye = 'goodbye'
}
const myComponent: ComponentDefinition<MyStrings> = {
id: 'my-component',
name: 'My Component',
stringKeys: Object.values(MyStrings)
};
// System has EN, FR, ES languages - component must provide translations for all three
const registration: ComponentRegistration<MyStrings, CoreLanguage> = {
component: myComponent,
strings: {
[CoreLanguage.EnglishUS]: {
[MyStrings.Welcome]: 'Welcome',
[MyStrings.Goodbye]: 'Goodbye'
},
[CoreLanguage.French]: {
[MyStrings.Welcome]: 'Bienvenue',
[MyStrings.Goodbye]: 'Au revoir'
},
[CoreLanguage.Spanish]: {
[MyStrings.Welcome]: 'Bienvenido',
[MyStrings.Goodbye]: 'Adiós'
}
}
};
const result = i18n.registerComponent(registration);
if (!result.isValid) {
console.log('Missing translations:', result.missingKeys);
// Shows exactly which string keys are missing for which languages
}Components can support different subsets of system languages:
// Component A supports EN, FR, ES
const componentA = {
component: { id: 'comp-a', name: 'Component A', stringKeys: ['hello'] },
strings: {
en: { hello: 'Hello' },
fr: { hello: 'Bonjour' },
es: { hello: 'Hola' }
}
};
// Component B only supports EN and DE (added later)
const componentB = {
component: { id: 'comp-b', name: 'Component B', stringKeys: ['save'] },
strings: {
en: { save: 'Save' },
de: { save: 'Speichern' }
}
};
// Both components can coexist - missing translations use fallback
i18n.registerComponent(componentA); // ✓ Complete
i18n.registerComponent(componentB); // ⚠ Missing FR, ES - uses fallback
// Usage automatically handles fallbacks
i18n.translate('comp-b', 'save', {}, 'fr'); // Returns 'Save' (EN fallback)// Add new language to system
const germanLang = { id: 'de', name: 'German', code: 'de' };
i18n.registerLanguage(germanLang);
// New component registrations now require German translations
const newRegistration = {
component: { id: 'new-comp', name: 'New Component', stringKeys: ['test'] },
strings: {
en: { test: 'Test' },
fr: { test: 'Test' },
es: { test: 'Prueba' }
// Missing 'de' - validation will flag this
}
};
const result = i18n.registerComponent(newRegistration);
console.log(result.missingKeys); // Shows missing German translations// Comprehensive validation
const globalValidation = i18n.validateAllComponents();
if (!globalValidation.isValid) {
console.error('Validation errors:', globalValidation.errors);
console.warn('Warnings:', globalValidation.warnings);
}
// Handle registration errors
try {
i18n.registerComponent(incompleteRegistration);
} catch (error) {
if (error instanceof RegistryError) {
console.error(`Registry error: ${error.type}`, error.metadata);
}
}
// Strict validation mode (rejects incomplete registrations)
const strictEngine = new PluginI18nEngine(languages, {
validation: {
requireCompleteStrings: true,
allowPartialRegistration: false,
fallbackLanguageId: 'en'
}
});// Create separate instances for different contexts
const adminI18n = PluginI18nEngine.createInstance('admin', languages);
const userI18n = PluginI18nEngine.createInstance('user', languages);
// Register different components for each
adminI18n.registerComponent(adminComponentRegistration);
userI18n.registerComponent(userComponentRegistration);For complete documentation on the plugin architecture, see PLUGIN_ARCHITECTURE.md.
enum Status {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending'
}
// Register enum translations (requires complete translations)
i18n.registerEnum(Status, {
[MyLanguages.English]: {
[Status.Active]: 'Active',
[Status.Inactive]: 'Inactive',
[Status.Pending]: 'Pending'
},
[MyLanguages.Spanish]: {
[Status.Active]: 'Activo',
[Status.Inactive]: 'Inactivo',
[Status.Pending]: 'Pendiente'
}
}, 'Status');
// Translate enum values
const statusText = i18n.translateEnum(Status, Status.Active, MyLanguages.Spanish);
// "Activo"enum AppStrings {
WelcomeMessage = 'welcomeMessage',
UserGreetingTemplate = 'userGreetingTemplate'
}
const config: I18nConfig<AppStrings, MyLanguages> = {
// ... other config
enumName: 'AppStrings',
enumObj: AppStrings,
strings: {
[MyLanguages.English]: {
[AppStrings.WelcomeMessage]: 'Welcome to our app!',
[AppStrings.UserGreetingTemplate]: 'Hello, {name}!'
}
}
};
const i18n = new I18nEngine(config);
// Use template processor
const message = i18n.t('{{AppStrings.WelcomeMessage}} {{AppStrings.UserGreetingTemplate}}',
MyLanguages.English,
{ name: 'John' }
);
// "Welcome to our app! Hello, John!"import { createContext, setLanguage, setAdminLanguage, setContext } from '@digitaldefiance/i18n-lib';
// Create and manage context
const context = createContext(MyLanguages.English, 'user');
// Set different languages for user and admin contexts
setLanguage(context, MyLanguages.Spanish);
setAdminLanguage(context, MyLanguages.English);
// Switch between contexts
setContext(context, 'admin'); // Uses admin language
setContext(context, 'user'); // Uses user language
// Apply context to engine
i18n.context = context;import { ContextManager } from '@digitaldefiance/i18n-lib';
interface AppContext {
language: string;
theme: string;
}
const manager = new ContextManager<AppContext>();
// Add listeners for context changes
manager.addListener((property, oldValue, newValue) => {
console.log(`${property} changed from ${oldValue} to ${newValue}`);
});
// Create reactive context
const context = { language: 'en', theme: 'dark' };
const reactiveContext = manager.createProxy(context);
// Changes are automatically detected
reactiveContext.language = 'es'; // Triggers listenerimport { getCurrencyFormat } from '@digitaldefiance/i18n-lib';
// Get currency formatting information
const usdFormat = getCurrencyFormat('en-US', 'USD');
// { symbol: '$', position: 'prefix', groupSeparator: ',', decimalSeparator: '.' }
const eurFormat = getCurrencyFormat('de-DE', 'EUR');
// { symbol: '€', position: 'postfix', groupSeparator: '.', decimalSeparator: ',' }// Create named instances
const mainI18n = new I18nEngine(config, 'main');
const adminI18n = new I18nEngine(adminConfig, 'admin');
// Get instances by key
const instance = I18nEngine.getInstance('main');
// Clean up instances (useful for testing)
I18nEngine.clearInstances();
I18nEngine.removeInstance('main');Constructor
new PluginI18nEngine<TLanguages>(languages, config?)- Create new plugin enginePluginI18nEngine.createInstance<TLanguages>(key, languages, config?)- Create named instancePluginI18nEngine.getInstance<TLanguages>(key?)- Get existing instance
Component Management
registerComponent<TStringKeys>(registration)- Register component with translationsupdateComponentStrings<TStringKeys>(componentId, strings)- Update existing component stringsgetComponents()- Get all registered componentshasComponent(componentId)- Check if component exists
Translation Methods
translate<TStringKeys>(componentId, stringKey, variables?, language?)- Translate component stringsafeTranslate(componentId, stringKey, variables?, language?)- Safe translate with fallbackgetTranslationDetails<TStringKeys>(componentId, stringKey, variables?, language?)- Get detailed translation response
Language Management
registerLanguage(language)- Register new languageregisterLanguages(languages)- Register multiple languagesgetLanguages()- Get all registered languageshasLanguage(language)- Check if language existssetLanguage(language)- Set current languagegetLanguageByCode(code)- Get language by ISO code
Validation
validateAllComponents()- Validate all registered componentsgetLanguageRegistry()- Access language registry directlygetComponentRegistry()- Access component registry directly
createCoreI18nEngine(instanceKey?)- Create engine with core componentsgetCoreTranslation(stringKey, variables?, language?, instanceKey?)- Get core translationsafeCoreTranslation(stringKey, variables?, language?, instanceKey?)- Safe core translation
interface ComponentDefinition<TStringKeys extends string> {
readonly id: string;
readonly name: string;
readonly stringKeys: readonly TStringKeys[];
}
interface ComponentRegistration<TStringKeys extends string, TLanguages extends string> {
readonly component: ComponentDefinition<TStringKeys>;
readonly strings: PartialComponentLanguageStrings<TStringKeys, TLanguages>;
}
interface LanguageDefinition {
readonly id: string;
readonly name: string;
readonly code: string;
readonly isDefault?: boolean;
}new I18nEngine<TStringKey, TLanguage>(config, key?)- Create new engine instance
translate(key, vars?, language?, fallbackLanguage?)- Translate string with optional variablestranslateEnum(enumObj, value, language)- Translate enum valuet(templateString, language?, ...vars)- Process template string with{{EnumName.EnumKey}}patterns
registerEnum(enumObj, translations, enumName)- Register enum translations
getLanguageCode(language)- Get language code for languagegetLanguageFromCode(code)- Get language from codegetAllLanguageCodes()- Get all language codesgetAvailableLanguages()- Get available languagesisLanguageAvailable(language)- Check if language is available
get context()- Get current contextset context(context)- Set context properties
getInstance(key?)- Get instance by keyclearInstances()- Clear all instancesremoveInstance(key?)- Remove specific instance
createContext(defaultLanguage, defaultContext)- Create new contextsetLanguage(context, language)- Set user languagesetAdminLanguage(context, language)- Set admin languagesetContext(context, contextType)- Set context type
addListener(listener)- Add change listenerremoveListener(listener)- Remove change listenercreateProxy(context)- Create reactive context proxy
getCurrencyFormat(locale, currencyCode)- Get currency formatting info
createTranslations(translations)- Helper for creating typed translations
type EnumTranslation<T extends string | number> = {
[K in T]: string;
};
type EnumLanguageTranslation<T extends string | number, TLanguage extends string> = Partial<{
[L in TLanguage]: EnumTranslation<T>;
}>;
interface I18nConfig<TStringKey, TLanguage, TConstants?, TTranslationContext?> {
stringNames: TStringKey[];
strings: MasterStringsCollection<TStringKey, TLanguage>;
defaultLanguage: TLanguage;
defaultTranslationContext: TTranslationContext;
defaultCurrencyCode: CurrencyCode;
languageCodes: LanguageCodeCollection<TLanguage>;
languages: TLanguage[];
constants?: TConstants;
enumName?: string;
enumObj?: Record<string, TStringKey>;
timezone: Timezone;
adminTimezone: Timezone;
}The library includes comprehensive test coverage:
# Run tests
yarn test
# Run specific test suites
yarn test context-manager.spec.ts
yarn test enum-registry.spec.ts
yarn test i18n-engine.spec.tsFor proper test isolation when using the plugin-based architecture, use the cleanup utilities:
import { PluginI18nEngine, resetAllI18nEngines } from '@digitaldefiance/i18n-lib';
describe('My tests', () => {
beforeEach(() => {
// Clean up any existing instances before each test
PluginI18nEngine.clearAllInstances();
});
afterEach(() => {
// Or use the convenience function
resetAllI18nEngines();
});
// Or use specific cleanup methods
it('should manage instances', () => {
const engine1 = PluginI18nEngine.createInstance('app1', [englishLang]);
const engine2 = PluginI18nEngine.createInstance('app2', [frenchLang]);
// Check if instances exist
expect(PluginI18nEngine.hasInstance('app1')).toBe(true);
expect(PluginI18nEngine.hasInstance('app2')).toBe(true);
// Remove specific instance
PluginI18nEngine.removeInstance('app1');
expect(PluginI18nEngine.hasInstance('app1')).toBe(false);
// Clear all instances and component registrations
PluginI18nEngine.resetAll();
expect(PluginI18nEngine.hasInstance('app2')).toBe(false);
});
});PluginI18nEngine.clearAllInstances()- Remove all engine instancesPluginI18nEngine.removeInstance(key?)- Remove specific instance by keyPluginI18nEngine.hasInstance(key?)- Check if instance existsPluginI18nEngine.resetAll()- Clear instances and component registrationsresetAllI18nEngines()- Convenience function that callsresetAll()
The library supports layered extension across multiple libraries using TypeScript module augmentation:
// In your base library
import { DefaultStringKey, DefaultLanguage } from '@digitaldefiance/i18n-lib';
enum MyLibStringKey {
Feature_Save = 'feature_save',
Feature_Load = 'feature_load',
}
// Extend the global configuration
declare global {
namespace I18n {
interface Config {
StringKey: DefaultStringKey | MyLibStringKey;
}
}
}// In a library that extends the base library
import { DefaultStringKey } from '@digitaldefiance/i18n-lib';
import { MyLibStringKey } from 'my-base-lib';
enum AdvancedStringKey {
Advanced_Export = 'advanced_export',
Advanced_Import = 'advanced_import',
}
// Extend with union types
declare global {
namespace I18n {
interface Config {
StringKey: DefaultStringKey | MyLibStringKey | AdvancedStringKey;
}
}
}// In your final application
import { StringKey, Language, getI18nEngine } from 'my-extended-lib';
// All string keys from all layers are now available
const engine = getI18nEngine();
const translation = engine.translate('advanced_export' as StringKey);Use the default configuration helper for quick setup:
import { getDefaultI18nEngine } from '@digitaldefiance/i18n-lib';
// Create engine with default configuration
const engine = getDefaultI18nEngine(
{ APP_NAME: 'MyApp' }, // constants
new Timezone('America/New_York'), // user timezone
new Timezone('UTC') // admin timezone
);The engine automatically validates configurations during construction to ensure completeness:
- All languages in
languageCodesmust have correspondingstringscollections - All
stringNamesmust be present in every language's string collection - The
defaultLanguagemust have a string collection
Validation errors can be localized by including error message keys in your configuration:
enum MyStrings {
Welcome = 'welcome',
// Error message keys
Error_MissingStringCollectionTemplate = 'error_missing_string_collection_template',
Error_MissingTranslationTemplate = 'error_missing_translation_template',
Error_DefaultLanguageNoCollectionTemplate = 'error_default_language_no_collection_template'
}
const config: I18nConfig<MyStrings, MyLanguages> = {
stringNames: Object.values(MyStrings),
strings: {
[MyLanguages.English]: {
[MyStrings.Welcome]: 'Welcome!',
[MyStrings.Error_MissingStringCollectionTemplate]: 'Missing translations for language: {language}',
[MyStrings.Error_MissingTranslationTemplate]: 'Key \'{key}\' not found in {language}',
[MyStrings.Error_DefaultLanguageNoCollectionTemplate]: 'Default language \'{language}\' has no translations'
}
},
// ... rest of config
};If localized error messages aren't provided, the engine falls back to English templates:
Missing string collection for language: {language}Missing translation for key '{key}' in language '{language}'Default language '{language}' has no string collection
- Use Component Registration: Organize translations into logical components with
PluginI18nEngine - Define String Key Enums: Always use TypeScript enums for string keys to ensure compile-time type safety
- Complete Component Translations: Provide translations for all supported languages in each component
- Validate Registrations: Check validation results and handle missing translations appropriately
- Use Core Components: Start with
createCoreI18nEngine()for built-in system strings - Fallback Strategy: Configure appropriate fallback languages for missing translations
- Component Isolation: Keep related strings together in the same component
- Template Variables: Use template strings with variables for dynamic content
- Multi-Instance Architecture: Use named instances for different application contexts (admin, user, etc.)
- Complete Translations: EnumTranslation requires all enum values to be translated
- Type Safety: Use TypeScript enums for string keys and languages
- Context Separation: Use different contexts for admin and user interfaces
- Instance Management: Use named instances for different parts of your application
- Error Handling: Handle missing translations gracefully with fallback languages
- Layered Extension: Use union types when extending configurations across libraries
- Default Configuration: Use
getDefaultI18nEngine()for standard setups
When migrating from legacy to plugin architecture:
// Legacy approach
const legacy = new I18nEngine(config);
const text = legacy.translate(MyStrings.Welcome);
// New plugin approach
const modern = createCoreI18nEngine();
modern.registerComponent(myComponentRegistration);
const text = modern.translate('my-component', MyStrings.Welcome);Both systems can coexist in the same application during migration.
MIT
- Sat Oct 11 2025 19:25:00 GMT-0700 (Pacific Daylight Time)
- Added cleanup mechanisms for other modules to deregister, etc.
- Sat Oct 11 2025 17:47:00 GMT-0700 (Pacific Daylight Time)
- Improved type checking for completeness of component translations during registration
- Updated README/Migration guide for clarity.
- Sat Oct 11 2025 16:49:00 GMT-0700 (Pacific Daylight Time)
- Introduced plugin-based architecture with component registration and compile-time type safety
- Added
PluginI18nEnginewith comprehensive validation and fallback system - Maintained full support for legacy
I18nEngine - Added pre-built core and user system components
- Enhanced template processing and context management features
- Improved documentation and examples for both architectures
- Wed Sep 24 2025 15:20:07 GMT-0700 (Pacific Daylight Time)
- Initial release of the TypeScript internationalization library with enum translation, template processing, context management, and currency formatting.