or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

http-client.mdi18n.mdindex.mdlocale.mdmenu.mdpipes.mdsettings.mdui-helpers.mdutilities.md
tile.json

i18n.mddocs/

Internationalization

Complete internationalization system with language switching, translation services, router guards for URL-based i18n, and reactive language management for Angular applications.

Capabilities

AlainI18NService

Core internationalization service interface for language management and text translation.

/**
 * Core internationalization service interface
 * Implement this interface to provide custom i18n functionality
 */
interface AlainI18NService {
  /** Observable stream of language changes */
  readonly change: Observable<string>;
  /** Default language code */
  defaultLang: string;
  /** Current active language code */
  currentLang: string;
  
  /** Change active language and optionally provide language data */
  use(lang: string, data?: Record<string, unknown>): void;
  /** Get list of available languages */
  getLangs(): any[];
  /** Translate text using key path and optional parameters */
  fanyi(path: string, params?: unknown | unknown[]): string;
}

/** Injection token for i18n service */
const ALAIN_I18N_TOKEN: InjectionToken<AlainI18NService>;

Usage Examples:

import { Injectable, inject } from "@angular/core";
import { Observable, BehaviorSubject } from "rxjs";
import { AlainI18NService, ALAIN_I18N_TOKEN } from "@delon/theme";

// Custom i18n service implementation
@Injectable()
export class CustomI18nService implements AlainI18NService {
  private _change = new BehaviorSubject<string>('en');
  private _currentLang = 'en';
  private _defaultLang = 'en';
  private translations: Record<string, Record<string, string>> = {
    en: {
      'app.title': 'My Application',
      'user.greeting': 'Hello, {{name}}!',
      'nav.dashboard': 'Dashboard',
      'nav.users': 'Users'
    },
    zh: {
      'app.title': '我的应用程序',
      'user.greeting': '你好,{{name}}!',
      'nav.dashboard': '仪表板',
      'nav.users': '用户'
    }
  };
  
  get change(): Observable<string> {
    return this._change.asObservable();
  }
  
  get defaultLang(): string {
    return this._defaultLang;
  }
  
  set defaultLang(lang: string) {
    this._defaultLang = lang;
  }
  
  get currentLang(): string {
    return this._currentLang;
  }
  
  use(lang: string, data?: Record<string, unknown>): void {
    if (data) {
      this.translations[lang] = { ...this.translations[lang], ...data };
    }
    
    this._currentLang = lang;
    this._change.next(lang);
  }
  
  getLangs(): any[] {
    return Object.keys(this.translations);
  }
  
  fanyi(path: string, params?: unknown | unknown[]): string {
    const langData = this.translations[this._currentLang] || this.translations[this._defaultLang];
    let text = langData[path] || path;
    
    // Handle parameter substitution
    if (params) {
      if (Array.isArray(params)) {
        params.forEach((param, index) => {
          text = text.replace(`{{${index}}}`, String(param));
        });
      } else if (typeof params === 'object') {
        Object.keys(params).forEach(key => {
          text = text.replace(`{{${key}}}`, String(params[key]));
        });
      }
    }
    
    return text;
  }
}

// Component using i18n service
@Component({
  selector: "app-header",
  template: `
    <header>
      <h1>{{ i18n.fanyi('app.title') }}</h1>
      <nav>
        <a>{{ i18n.fanyi('nav.dashboard') }}</a>
        <a>{{ i18n.fanyi('nav.users') }}</a>
      </nav>
      <div class="language-switcher">
        <select (change)="switchLanguage($event)">
          <option *ngFor="let lang of i18n.getLangs()" [value]="lang">
            {{ lang }}
          </option>
        </select>
      </div>
      <div class="user-greeting">
        {{ i18n.fanyi('user.greeting', { name: userName }) }}
      </div>
    </header>
  `
})
export class AppHeaderComponent {
  i18n = inject(ALAIN_I18N_TOKEN);
  userName = 'John Doe';
  
  constructor() {
    // Listen to language changes
    this.i18n.change.subscribe(lang => {
      console.log('Language changed to:', lang);
      // Update page title, direction, etc.
      document.title = this.i18n.fanyi('app.title');
    });
  }
  
  switchLanguage(event: Event) {
    const lang = (event.target as HTMLSelectElement).value;
    this.i18n.use(lang);
  }
}

AlainI18nBaseService

Abstract base implementation providing common i18n functionality.

/**
 * Abstract base implementation of AlainI18NService
 * Extend this class for custom i18n implementations
 */
abstract class AlainI18nBaseService implements AlainI18NService {
  abstract readonly change: Observable<string>;
  abstract defaultLang: string;
  abstract currentLang: string;
  
  abstract use(lang: string, data?: Record<string, unknown>): void;
  abstract getLangs(): any[];
  abstract fanyi(path: string, params?: unknown | unknown[]): string;
}

Usage Examples:

import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";
import { AlainI18nBaseService } from "@delon/theme";

@Injectable()
export class MyI18nService extends AlainI18nBaseService {
  private _change = new BehaviorSubject<string>('en');
  private _currentLang = 'en';
  defaultLang = 'en';
  
  get change(): Observable<string> {
    return this._change.asObservable();
  }
  
  get currentLang(): string {
    return this._currentLang;
  }
  
  use(lang: string, data?: Record<string, unknown>): void {
    // Custom implementation
    this._currentLang = lang;
    this._change.next(lang);
  }
  
  getLangs(): any[] {
    return ['en', 'zh', 'es', 'fr'];
  }
  
  fanyi(path: string, params?: unknown | unknown[]): string {
    // Custom translation logic
    return `[${this._currentLang}] ${path}`;
  }
}

AlainI18NServiceFake

Simple fake implementation for testing and development.

/**
 * Simple fake implementation of AlainI18NService
 * Returns translation keys as-is, useful for development
 */
class AlainI18NServiceFake implements AlainI18NService {
  readonly change: Observable<string>;
  defaultLang: string;
  currentLang: string;
  
  use(lang: string, data?: Record<string, unknown>): void;
  getLangs(): any[];
  fanyi(path: string, params?: unknown | unknown[]): string;
}

Usage Examples:

import { AlainI18NServiceFake } from "@delon/theme";

// Use fake service for testing
const fakeI18n = new AlainI18NServiceFake();
fakeI18n.defaultLang = 'en';
fakeI18n.currentLang = 'en';

// Returns the key as-is
console.log(fakeI18n.fanyi('app.title')); // Output: "app.title"
console.log(fakeI18n.fanyi('user.greeting', { name: 'John' })); // Output: "user.greeting"

I18n Pipe

Pipe for translating text in templates using the i18n service.

/**
 * I18n pipe for template translation
 * Automatically updates when language changes
 */
class I18nPipe implements PipeTransform {
  /**
   * Transform translation key to localized text
   * @param key - Translation key path
   * @param params - Optional parameters for interpolation
   * @returns Translated text
   */
  transform(key: string, params?: unknown | unknown[]): string;
}

Usage Examples:

// Template usage
@Component({
  template: `
    <h1>{{ 'app.title' | i18n }}</h1>
    <p>{{ 'user.greeting' | i18n:{ name: userName } }}</p>
    <button>{{ 'button.save' | i18n }}</button>
    
    <!-- With array parameters -->
    <span>{{ 'message.items' | i18n:[itemCount, totalCount] }}</span>
    
    <!-- Nested in other pipes -->
    <div [title]="'tooltip.help' | i18n">
      {{ 'content.help' | i18n | uppercase }}
    </div>
  `
})
export class MyComponent {
  userName = 'Alice';
  itemCount = 5;
  totalCount = 20;
}

// Component usage
import { Pipe, PipeTransform, inject } from "@angular/core";
import { ALAIN_I18N_TOKEN } from "@delon/theme";

@Pipe({ name: 'customI18n', pure: false })
export class CustomI18nPipe implements PipeTransform {
  private i18n = inject(ALAIN_I18N_TOKEN);
  
  transform(key: string, params?: any): string {
    return this.i18n.fanyi(key, params);
  }
}

I18n Router Guards

Router guards for URL-based internationalization with automatic language detection and redirection.

/**
 * Router guard for URL-based internationalization
 * Automatically redirects to language-prefixed routes
 */
function alainI18nCanActivate(
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
): boolean | UrlTree;

/**
 * Router guard for child routes with i18n support
 * Handles language switching in nested route structures
 */
function alainI18nCanActivateChild(
  childRoute: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
): boolean | UrlTree;

Usage Examples:

import { Routes } from "@angular/router";
import { alainI18nCanActivate, alainI18nCanActivateChild } from "@delon/theme";

// Routes with i18n guards
const routes: Routes = [
  {
    path: '',
    canActivate: [alainI18nCanActivate],
    children: [
      {
        path: 'dashboard',
        component: DashboardComponent
      },
      {
        path: 'users',
        canActivateChild: [alainI18nCanActivateChild],
        children: [
          { path: 'list', component: UserListComponent },
          { path: 'detail/:id', component: UserDetailComponent }
        ]
      }
    ]
  },
  {
    path: ':lang',
    canActivate: [alainI18nCanActivate],
    children: [
      { path: 'dashboard', component: DashboardComponent },
      { path: 'users', component: UsersComponent }
    ]
  }
];

// Language-aware routing service
@Injectable()
export class I18nRoutingService {
  constructor(
    private router: Router,
    private i18n: AlainI18NService
  ) {}
  
  navigateWithLang(path: string, lang?: string) {
    const currentLang = lang || this.i18n.currentLang;
    this.router.navigate([`/${currentLang}${path}`]);
  }
  
  getCurrentPath(): string {
    const url = this.router.url;
    // Remove language prefix
    return url.replace(/^\/[a-z]{2}(-[A-Z]{2})?/, '');
  }
  
  switchLanguage(newLang: string) {
    const currentPath = this.getCurrentPath();
    this.i18n.use(newLang);
    this.navigateWithLang(currentPath, newLang);
  }
}

I18n Provider Setup

Configuration and provider setup for i18n services.

/**
 * Provide i18n service in application
 * Configure with provideAlain options
 */
interface AlainProvideOptions {
  /** Custom i18n service class */
  i18nClass?: Type<any>;
  /** Default language configuration */
  defaultLang?: AlainProvideLang;
}

interface AlainProvideLang {
  /** Language abbreviation (e.g., 'en', 'zh') */
  abbr: string;
  /** Angular locale data */
  ng: any;
  /** ng-zorro-antd locale data */
  zorro: any;
  /** Date locale data */
  date: any;
  /** Delon locale data */
  delon: any;
}

Usage Examples:

import { bootstrapApplication } from "@angular/platform-browser";
import { provideAlain } from "@delon/theme";
import { CustomI18nService } from "./i18n.service";
import enUS from "@angular/common/locales/en";
import { en_US } from "ng-zorro-antd/i18n";
import { enUS as dateEnUS } from "date-fns/locale";
import { en_US as delonEnUS } from "@delon/theme/locale";

bootstrapApplication(AppComponent, {
  providers: [
    provideAlain({
      i18nClass: CustomI18nService,
      defaultLang: {
        abbr: 'en',
        ng: enUS,
        zorro: en_US,
        date: dateEnUS,
        delon: delonEnUS
      }
    })
  ]
});

// Module-based setup
@NgModule({
  providers: [
    {
      provide: ALAIN_I18N_TOKEN,
      useClass: CustomI18nService,
      multi: false
    }
  ]
})
export class AppModule {}

// Custom provider factory
export function createI18nService(): AlainI18NService {
  const service = new CustomI18nService();
  service.defaultLang = 'en';
  return service;
}

const I18N_PROVIDER: Provider = {
  provide: ALAIN_I18N_TOKEN,
  useFactory: createI18nService
};