JSPM

@logistically/i18n

1.0.2
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 8
  • Score
    100M100P100Q51131F
  • License MIT

Enterprise-grade internationalization (i18n) library for NestJS microservices with RTL support, tree shaking, and performance optimizations

Package Exports

  • @logistically/i18n
  • @logistically/i18n/decorators
  • @logistically/i18n/exceptions
  • @logistically/i18n/rtl

Readme

@logistically/i18n

Enterprise-grade internationalization (i18n) library for NestJS microservices with RTL support, tree shaking, and performance optimizations.

๐Ÿš€ Features

  • Multi-locale support - Support for unlimited locales
  • RTL language support - Full support for Arabic, Hebrew, Persian, Urdu, and other RTL languages
  • Parameter interpolation - Dynamic content in translations
  • Caching with TTL - Performance optimization
  • Statistics tracking - Monitor translation usage
  • Fallback strategies - Graceful handling of missing translations
  • Debug logging - Comprehensive logging for troubleshooting
  • Type safety - Full TypeScript support
  • Dependency injection - Seamless NestJS integration
  • Elegant exception handling - Translated exceptions with clean syntax
  • Enhanced decorators - Multi-source locale extraction (JWT, cookies, headers, query params)

๐Ÿ“ฆ Installation

npm install @logistically/i18n

๐Ÿ› ๏ธ CLI Tool

This library works seamlessly with the @logistically/i18n-cli tool for extracting, generating, and managing translations.

CLI Installation

# Install CLI globally
npm install -g @logistically/i18n-cli

# Or install locally
npm install --save-dev @logistically/i18n-cli

Complete Workflow

# 1. Extract translatable strings from your codebase
i18n extract --patterns "*.ts,*.js" --output translation-keys.json

# 2. Generate translation files for multiple languages
i18n generate --languages en,fr,es,ar --output src/translations

# 3. Replace hardcoded strings with translation keys
i18n replace --dry-run  # Preview changes
i18n replace            # Apply changes

# 4. Use the library in your NestJS services

CLI Features

  • ๐Ÿ” Smart Extraction - Automatically extract translatable strings
  • ๐Ÿ—๏ธ File Generation - Generate translation files for multiple languages
  • ๐Ÿ”„ String Replacement - Replace hardcoded strings with translation keys
  • ๐Ÿ›ก๏ธ Security First - Input validation and output sanitization
  • ๐Ÿ“Š Performance Monitoring - Real-time metrics and progress tracking

๐Ÿšง Planned CLI Features

  • ๐Ÿ” GraphQL Schema Extraction - Extract translatable fields from GraphQL schemas
  • ๐Ÿท๏ธ GraphQL Directive Generation - Generate @i18n directives for GraphQL types
  • ๐Ÿ“‹ GraphQL Translation Validation - Validate i18n usage in GraphQL resolvers
  • ๐Ÿ”„ GraphQL Type Integration - Seamless integration with GraphQL code-first approach

For complete CLI documentation and usage examples, see the CLI tool's README.

๐ŸŒ Complete Translation Ecosystem

The @logistically/i18n library and @logistically/i18n-cli tool work together to provide a complete translation solution for enterprise applications.

๐ŸŽฏ Ecosystem Benefits

  • End-to-End Workflow - From extraction to deployment
  • Enterprise Security - Input validation and output sanitization
  • Performance Optimization - Concurrent processing and caching
  • RTL Support - Full support for Arabic, Hebrew, Persian, Urdu
  • Type Safety - Complete TypeScript support
  • Microservices Ready - Designed for distributed architectures

๐Ÿ“‹ Complete Workflow Example

# 1. Extract translatable strings from your microservices
i18n extract --patterns "*.ts,*.js" --output translation-keys.json

# 2. Generate translation files for all supported languages
i18n generate --languages en,fr,es,ar,he,fa,ur --output src/translations

# 3. Preview string replacements
i18n replace --dry-run --verbose

# 4. Apply replacements with backup
i18n replace --backup

# 5. Use the library in your NestJS services

๐Ÿ”ง Integration Example

// Before: Hardcoded strings
export class ProfileService {
  async getProfile(profileId: string) {
    const profile = await this.repository.findById(profileId);
    if (!profile) {
      throw new NotFoundException('Profile not found: ' + profileId);
    }
    return profile;
  }
}

// After: Using the ecosystem
import { TranslationService, TranslatedExceptions } from '@logistically/i18n';

export class ProfileService {
  constructor(private translationService: TranslationService) {}

  async getProfile(profileId: string, locale: string = 'en') {
    const profile = await this.repository.findById(profileId);
    if (!profile) {
      throw TranslatedExceptions.notFound('PROFILE.NOT_FOUND', {
        locale,
        params: { profileId }
      });
    }
    return profile;
  }
}

๐Ÿš€ Enterprise Features

Feature Library CLI Combined
RTL Support โœ… Full support โœ… Generation โœ… Complete workflow
Performance โœ… Caching, Tree shaking โœ… Concurrent processing โœ… Optimized end-to-end
Security โœ… Input validation โœ… Sanitization โœ… Enterprise-grade
Type Safety โœ… TypeScript โœ… TypeScript โœ… Complete coverage
Microservices โœ… DI, Decorators โœ… Multi-service โœ… Distributed ready

๐Ÿ“Š Performance Comparison

# Traditional approach
# โŒ Manual extraction: 2-3 hours
# โŒ Manual generation: 1-2 hours  
# โŒ Manual replacement: 4-6 hours
# โŒ Total: 7-11 hours

# With @logistically ecosystem
# โœ… CLI extraction: 5-10 minutes
# โœ… CLI generation: 2-5 minutes
# โœ… CLI replacement: 10-30 minutes
# โœ… Total: 17-45 minutes (95% faster!)

For complete CLI documentation and usage examples, see the CLI tool's README.

๐ŸŽฏ Quick Start

1. Setup Module

import { Module } from '@nestjs/common';
import { TranslationModule } from '@logistically/i18n';

@Module({
  imports: [
    TranslationModule.forRoot({
      serviceName: 'profile-service',
      defaultLocale: 'en',
      supportedLocales: ['en', 'fr', 'es', 'de', 'ar', 'he', 'fa', 'ur'],
      translationsPath: 'src/translations',
      debug: false,
      fallbackStrategy: 'default',
      cache: { enabled: true, ttl: 3600 }
    })
  ]
})
export class AppModule {}

2. Create Translation Files

// src/translations/en.json
{
  "PROFILE.NOT_FOUND": "Profile not found: ${profileId}",
  "PROFILE.INVALID_TYPE": "Invalid profile type: ${profileType}",
  "VALIDATION.MAX_FILES": "Cannot upload more than ${maxFiles} files"
}

// src/translations/fr.json
{
  "PROFILE.NOT_FOUND": "Profil introuvable: ${profileId}",
  "PROFILE.INVALID_TYPE": "Type de profil invalide: ${profileType}",
  "VALIDATION.MAX_FILES": "Impossible de tรฉlรฉcharger plus de ${maxFiles} fichiers"
}

// src/translations/ar.json
{
  "PROFILE.NOT_FOUND": "ุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ู…ูˆุฌูˆุฏ: ${profileId}",
  "PROFILE.INVALID_TYPE": "ู†ูˆุน ุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ุตุญูŠุญ: ${profileType}",
  "VALIDATION.MAX_FILES": "ู„ุง ูŠู…ูƒู† ุฑูุน ุฃูƒุซุฑ ู…ู† ${maxFiles} ู…ู„ู"
}

3. Use in Services

import { Injectable } from '@nestjs/common';
import { TranslationService, TranslatedExceptions } from '@logistically/i18n';

@Injectable()
export class ProfileService {
  constructor(private translationService: TranslationService) {}

  async getProfile(profileId: string, locale: string = 'en') {
    const profile = await this.profileRepository.findById(profileId);
    
    if (!profile) {
      // ๐ŸŽฏ Elegant translated exception
      throw TranslatedExceptions.notFound('PROFILE.NOT_FOUND', {
        locale,
        params: { profileId }
      });
    }
    
    return profile;
  }

  // Quick translation
  getWelcomeMessage(locale: string, userName: string) {
    return this.translationService.translate('WELCOME.MESSAGE', locale, {
      userName
    });
  }
}

4. Use Enhanced Decorators

The library provides powerful decorators for extracting locale from multiple sources:

import { Controller, Get, Param } from '@nestjs/common';
import { 
  Locale, 
  LocaleFromJWT, 
  LocaleFromCookies, 
  LocaleFromHeaders, 
  LocaleFromQuery,
  TranslationParams 
} from '@logistically/i18n';

@Controller('profiles')
export class ProfileController {
  @Get(':id')
  async getProfile(
    @Param('id') id: string,
    @Locale() locale: string,                    // Multi-source (JWT > Cookies > Headers > Query)
    @LocaleFromJWT() jwtLocale: string | null,  // JWT token only
    @LocaleFromCookies() cookieLocale: string | null,  // Cookies only
    @LocaleFromHeaders() headerLocale: string | null,  // Headers only
    @LocaleFromQuery() queryLocale: string | null,     // Query params only
    @TranslationParams() params: any
  ) {
    return { 
      message: 'Profile loaded', 
      detectedLocale: locale,
      sources: { jwtLocale, cookieLocale, headerLocale, queryLocale },
      params 
    };
  }
}

Locale Sources Priority (for @Locale() decorator):

  1. JWT Token - Authorization: Bearer <token> (highest priority)
  2. Cookies - locale, language, lang cookies
  3. Headers - Accept-Language, X-Locale, Accept-Locale
  4. Query Parameters - ?locale=, ?language=, ?lang=
  5. Default - Falls back to 'en'

JWT Token Format:

{
  "sub": "user123",
  "locale": "fr",        // Preferred field name
  "language": "es",      // Alternative field name
  "lang": "de"          // Alternative field name
}

๐ŸŽจ Advanced Usage

Custom Interpolation

TranslationModule.forRoot({
  serviceName: 'my-service',
  interpolation: {
    prefix: '{{',
    suffix: '}}'
  }
});

// Usage: "Hello {{name}}!"

Fallback Strategies

// Return the key if translation not found
fallbackStrategy: 'key'

// Throw an error if translation not found
fallbackStrategy: 'throw'

// Return key with warning (default)
fallbackStrategy: 'default'

Caching Configuration

cache: {
  enabled: true,
  ttl: 3600 // 1 hour
}

Statistics Configuration

// Enable all statistics (default)
statistics: {
  enabled: true,
  trackKeyUsage: true,
  trackLocaleUsage: true
}

// Disable all statistics for maximum performance
statistics: {
  enabled: false
}

// Enable only basic statistics
statistics: {
  enabled: true,
  trackKeyUsage: false,
  trackLocaleUsage: false
}

Statistics Tracking

const stats = translationService.getStats();
console.log(stats);
// {
//   totalRequests: 1000,
//   successfulTranslations: 950,
//   failedTranslations: 50,
//   cacheHits: 800,
//   cacheMisses: 200,
//   localeUsage: { en: 600, fr: 400 },
//   keyUsage: { 'PROFILE.NOT_FOUND': 100 }
// }

๐Ÿ› ๏ธ Exception Handling

Elegant Exception Syntax

import { TranslatedExceptions, T, Ex } from '@logistically/i18n';

// Full syntax
throw TranslatedExceptions.notFound('PROFILE.NOT_FOUND', {
  locale: 'fr',
  params: { profileId: '123' }
});

// Short aliases
throw T.notFound('PROFILE.NOT_FOUND', { locale: 'fr', params: { profileId: '123' } });
throw Ex.badRequest('VALIDATION.ERROR', { locale: 'en', params: { field: 'email' } });

// Custom HTTP status
throw TranslatedExceptions.http('CUSTOM.ERROR', 422, {
  locale: 'en',
  params: { reason: 'validation_failed' }
});

Quick Translation Helper

// Quick translation without service injection
const message = T.t('PROFILE.NOT_FOUND', 'fr', { profileId: '123' });

๐ŸŒ RTL (Right-to-Left) Language Support

The library provides comprehensive RTL support for Arabic, Hebrew, Persian, Urdu, and other RTL languages.

RTL Configuration

TranslationModule.forRoot({
  serviceName: 'my-service',
  rtl: {
    enabled: true,              // Enable RTL support
    autoDetect: true,           // Auto-detect RTL text
    wrapWithMarkers: false,     // Wrap text with directional markers
    includeDirectionalInfo: true // Include RTL info in metadata
  }
});

RTL Translation Methods

// Basic RTL translation
const result = translationService.translateWithRTL('PROFILE.NOT_FOUND', 'ar', { profileId: '123' });
console.log(result);
// {
//   text: "ุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ู…ูˆุฌูˆุฏ: 123",
//   rtl: { isRTL: true, direction: "rtl" }
// }

// Translation with directional markers
const markedText = translationService.translateWithDirectionalMarkers('PROFILE.NOT_FOUND', 'ar');
// Returns: "\u200Fุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ู…ูˆุฌูˆุฏ: 123"

// Check if locale is RTL
const isRTL = translationService.isRTLLocale('ar'); // true
const isRTL = translationService.isRTLLocale('he'); // true
const isRTL = translationService.isRTLLocale('en'); // false

// Get RTL information
const rtlInfo = translationService.getRTLInfo('ar');
// {
//   isRTL: true,
//   direction: "rtl",
//   script: "Arab",
//   name: "Arabic"
// }

// Get text direction for mixed content
const direction = translationService.getTextDirection('Hello ู…ุฑุญุจุง'); // "auto"
const direction = translationService.getTextDirection('ู…ุฑุญุจุง'); // "rtl"
const direction = translationService.getTextDirection('Hello'); // "ltr"

RTL Utilities

import { isRTL, getRTLInfo, getRTLLocales, containsRTLText } from '@logistically/i18n';

// Check if locale is RTL
isRTL('ar'); // true
isRTL('he'); // true
isRTL('en'); // false

// Get RTL information
getRTLInfo('ar-SA'); // { isRTL: true, direction: 'rtl', script: 'Arab', name: 'Arabic (Saudi Arabia)' }

// Get all RTL locales
const rtlLocales = getRTLLocales();
// ['ar', 'ar-SA', 'ar-EG', 'he', 'he-IL', 'fa', 'fa-IR', ...]

// Check if text contains RTL characters
containsRTLText('ู…ุฑุญุจุง ุจุงู„ุนุงู„ู…'); // true
containsRTLText('Hello World'); // false
containsRTLText('Hello ู…ุฑุญุจุง'); // true

Supported RTL Languages

  • Arabic (ar, ar-SA, ar-EG, etc.)
  • Hebrew (he, he-IL)
  • Persian/Farsi (fa, fa-IR, fa-AF)
  • Urdu (ur, ur-PK, ur-IN)
  • Kurdish (ku, ku-IQ, ku-IR)
  • Pashto (ps, ps-AF, ps-PK)
  • Sindhi (sd, sd-PK, sd-IN)
  • Uyghur (ug, ug-CN)
  • Yiddish (yi, yi-US, yi-IL)
  • And many more...

RTL Metadata in Responses

const metadata = translationService.getTranslationMetadata('PROFILE.NOT_FOUND', 'ar');
console.log(metadata);
// {
//   key: "PROFILE.NOT_FOUND",
//   originalText: "ุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ู…ูˆุฌูˆุฏ: ${profileId}",
//   translatedText: "ุงู„ู…ู„ู ุงู„ุดุฎุตูŠ ุบูŠุฑ ู…ูˆุฌูˆุฏ: 123",
//   locale: "ar",
//   fallbackUsed: false,
//   timestamp: Date,
//   rtl: {
//     isRTL: true,
//     direction: "rtl",
//     script: "Arab",
//     languageName: "Arabic"
//   }
// }

๐Ÿ”ง Configuration Options

Option Type Default Description
serviceName string - Service name for key prefixing
defaultLocale string 'en' Default locale
supportedLocales string[] ['en', 'fr', 'es', 'de', 'ar'] Supported locales
translationsPath string 'src/translations' Path to translation files
debug boolean false Enable debug logging
interpolation.prefix string '${' Interpolation prefix
interpolation.suffix string '}' Interpolation suffix
fallbackStrategy 'key' | 'default' | 'throw' 'default' Fallback strategy
cache.enabled boolean true Enable caching
cache.ttl number 3600 Cache TTL in seconds
statistics.enabled boolean true Enable statistics tracking
statistics.trackKeyUsage boolean true Track key usage statistics
statistics.trackLocaleUsage boolean true Track locale usage statistics
rtl.enabled boolean true Enable RTL language support
rtl.autoDetect boolean true Auto-detect RTL text content
rtl.wrapWithMarkers boolean false Wrap text with directional markers
rtl.includeDirectionalInfo boolean true Include RTL info in metadata

๐Ÿ“Š Performance

  • Caching: Built-in caching with configurable TTL
  • Lazy Loading: Translations loaded on-demand
  • Memory Efficient: No static dependencies
  • Statistics: Monitor performance and usage (configurable)
  • Zero Overhead: Statistics can be disabled for maximum performance

๐Ÿงช Testing

import { Test } from '@nestjs/testing';
import { TranslationModule } from '@logistically/i18n';

describe('ProfileService', () => {
  let translationService: TranslationService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      imports: [
        TranslationModule.forRoot({
          serviceName: 'test-service',
          debug: true
        })
      ]
    }).compile();

    translationService = module.get<TranslationService>(TranslationService);
  });

  it('should translate correctly', () => {
    const result = translationService.translate('TEST.KEY', 'en', { name: 'John' });
    expect(result).toBe('Hello John!');
  });
});

๐Ÿ“š Documentation

๐Ÿ“– Guides

๐Ÿค Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

๐Ÿ“„ License

MIT License - see LICENSE file for details.