You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用Angular 18内置i18n实现路由路径国际化?

Angular 18 内置i18n实现语言专属路由路径配置指南

一、两种实现方案选择

Angular内置i18n(@angular/localize)支持编译时多语言和运行时动态切换两种模式,对应不同的路由实现方式:

方案1:编译时多语言(推荐,性能最优)

这种模式下每个语言会被打包成独立的应用包,路由路径在构建阶段就完成翻译,适合不需要运行时切换语言的场景。

1.1 配置i18n与路由翻译键

  • 在angular.json中启用localize并配置语言:
    "projects": {
      "your-app": {
        "i18n": {
          "sourceLocale": "en-US",
          "locales": {
            "fr": "src/locale/messages.fr.xlf"
          }
        },
        "architect": {
          "build": {
            "options": {
              "localize": true
            },
            "configurations": {
              "en": { "localize": ["en-US"] },
              "fr": { "localize": ["fr"] }
            }
          }
        }
      }
    }
    
  • 在路由配置中使用$localize标记翻译键:
    import { Routes } from '@angular/router';
    
    export const routes: Routes = [
      {
        path: $localize`:@@ROUTE.LOGIN:login`,
        loadComponent: () => import('./login/login.component')
      },
      {
        path: $localize`:@@ROUTE.DASHBOARD:dashboard`,
        loadComponent: () => import('./dashboard/dashboard.component')
      },
      { path: '', redirectTo: $localize`:@@ROUTE.LOGIN:login`, pathMatch: 'full' }
    ];
    
  • 提取并翻译路由键:
    运行ng extract-i18n --output-path src/locale生成默认语言的翻译源messages.xlf,复制为messages.fr.xlf并添加路由翻译:
    <trans-unit id="ROUTE.LOGIN" datatype="html">
      <source>login</source>
      <target>se-connecter</target>
    </trans-unit>
    <trans-unit id="ROUTE.DASHBOARD" datatype="html">
      <source>dashboard</source>
      <target>tableau-de-bord</target>
    </trans-unit>
    

1.2 构建与部署

  • 分别构建各语言包:
    ng build --configuration=en --output-path dist/en
    ng build --configuration=fr --output-path dist/fr
    
  • 将构建产物部署到服务器的对应子目录(如/en和/fr),访问yourdomain/en/login或yourdomain/fr/se-connecter即可进入对应语言的路由。

方案2:运行时动态切换语言

如果需要在应用运行中切换语言并实时更新路由路径,需结合自定义路由序列化器和翻译服务实现。

2.1 配置基础路由与翻译服务

  • 定义带语言参数的路由结构:
    import { Routes } from '@angular/router';
    
    export const routes: Routes = [
      {
        path: ':lang',
        children: [
          { path: 'login', loadComponent: () => import('./login/login.component') },
          { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component') }
        ]
      },
      { path: '', redirectTo: '/en/login', pathMatch: 'full' }
    ];
    
  • 创建路由翻译服务,维护路由键与各语言的映射:
    import { Injectable } from '@angular/core';
    import { LOCALE_ID, Inject } from '@angular/core';
    
    @Injectable({ providedIn: 'root' })
    export class RouteTranslationService {
      private routeMap = {
        en: { login: 'login', dashboard: 'dashboard' },
        fr: { login: 'se-connecter', dashboard: 'tableau-de-bord' }
      };
    
      constructor(@Inject(LOCALE_ID) private currentLocale: string) {}
    
      // 获取当前语言的翻译路径
      getTranslatedPath(routeKey: string): string {
        return this.routeMap[this.currentLocale]?.[routeKey] || routeKey;
      }
    
      // 根据路径反向查找路由键
      getRouteKeyFromPath(path: string, lang: string): string {
        const langMap = this.routeMap[lang];
        return Object.keys(langMap).find(key => langMap[key] === path) || path;
      }
    }
    

2.2 自定义URL序列化器

通过重写UrlSerializer实现路径的动态翻译:

import { Injectable } from '@angular/core';
import { DefaultUrlSerializer, UrlTree } from '@angular/router';
import { RouteTranslationService } from './route-translation.service';
import { TranslateService } from '@ngx-translate/core';

@Injectable()
export class TranslatedUrlSerializer extends DefaultUrlSerializer {
  constructor(
    private routeTranslationService: RouteTranslationService,
    private translateService: TranslateService
  ) {
    super();
  }

  override parse(url: string): UrlTree {
    // 解析URL时将翻译路径转为原始路由键
    const segments = url.split('/').filter(seg => seg);
    if (segments.length < 2) return super.parse(url);

    const lang = segments[0];
    const translatedPath = segments[1];
    const routeKey = this.routeTranslationService.getRouteKeyFromPath(translatedPath, lang);
    
    const parsedUrl = `/${lang}/${routeKey}`;
    return super.parse(parsedUrl);
  }

  override serialize(tree: UrlTree): string {
    // 序列化URL时将原始路由键转为翻译路径
    const primarySegments = tree.root.children['primary']?.segments;
    if (!primarySegments || primarySegments.length < 2) return super.serialize(tree);

    const lang = primarySegments[0].path;
    const routeKey = primarySegments[1].path;
    const translatedPath = this.routeTranslationService.getTranslatedPath(routeKey);
    
    return `/${lang}/${translatedPath}`;
  }
}

2.3 注册序列化器与导航处理

  • 在app.config.ts中提供自定义序列化器:
    import { ApplicationConfig, provideRouter } from '@angular/core';
    import { routes } from './app.routes';
    import { UrlSerializer } from '@angular/router';
    import { TranslatedUrlSerializer } from './translated-url-serializer';
    import { provideHttpClient } from '@angular/common/http';
    import { provideTranslateCore } from '@ngx-translate/core';
    import { TranslateLoader } from '@ngx-translate/core';
    import { TranslateHttpLoader } from '@ngx-translate/http-loader';
    import { HttpClient } from '@angular/common/http';
    
    export function HttpLoaderFactory(http: HttpClient) {
      return new TranslateHttpLoader(http, './assets/i18n/', '.json');
    }
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideRouter(routes),
        provideHttpClient(),
        { provide: UrlSerializer, useClass: TranslatedUrlSerializer },
        provideTranslateCore(),
        {
          provide: TranslateLoader,
          useFactory: HttpLoaderFactory,
          deps: [HttpClient]
        }
      ]
    };
    
  • 在组件中处理语言切换与导航:
    import { Component } from '@angular/core';
    import { Router } from '@angular/router';
    import { RouteTranslationService } from './route-translation.service';
    import { TranslateService } from '@ngx-translate/core';
    
    @Component({
      selector: 'app-header',
      template: `
        <a [routerLink]="getLoginPath()">Login</a>
        <a [routerLink]="getDashboardPath()">Dashboard</a>
        <button (click)="switchLang('en')">English</button>
        <button (click)="switchLang('fr')">Français</button>
      `
    })
    export class HeaderComponent {
      constructor(
        private router: Router,
        private routeTranslationService: RouteTranslationService,
        private translateService: TranslateService
      ) {}
    
      getLoginPath() {
        const lang = this.translateService.currentLang;
        return `/${lang}/${this.routeTranslationService.getTranslatedPath('login')}`;
      }
    
      getDashboardPath() {
        const lang = this.translateService.currentLang;
        return `/${lang}/${this.routeTranslationService.getTranslatedPath('dashboard')}`;
      }
    
      switchLang(lang: string) {
        this.translateService.use(lang);
        // 切换语言后重定向到当前路由的翻译路径
        const currentSegments = this.router.url.split('/').filter(seg => seg);
        if (currentSegments.length < 2) return;
    
        const routeKey = this.routeTranslationService.getRouteKeyFromPath(currentSegments[1], this.translateService.currentLang);
        const newPath = this.routeTranslationService.getTranslatedPath(routeKey);
        this.router.navigate([`/${lang}/${newPath}`]);
      }
    }
    

二、最佳实践

  • 优先选择编译时多语言:编译时生成的包体积更小、性能更好,且无需运行时翻译逻辑,适合大多数无需动态切换语言的场景。
  • 统一管理路由翻译键:将所有路由翻译键集中放在xlf或json文件中,避免分散在代码里,便于维护和批量更新。
  • 验证语言参数合法性:在路由守卫(如CanActivate)中检查lang参数是否为支持的语言,非法参数自动重定向到默认语言路由。
  • 持久化用户语言选择:用localStorage或cookie保存用户选择的语言,应用初始化时自动加载对应语言和路由。
  • SEO优化:为每个语言路由添加hreflang标签,帮助搜索引擎识别多语言内容:
    <link rel="alternate" hreflang="en" href="https://yourdomain.com/en/login">
    <link rel="alternate" hreflang="fr" href="https://yourdomain.com/fr/se-connecter">
    
  • 避免过度嵌套路由:带语言参数的路由已增加一层路径,尽量保持路由结构扁平化,减少维护复杂度。
  • 全面测试多语言路由:针对每个支持的语言,测试路由跳转、刷新、反向解析等场景,确保无404或路径错误。

内容的提问来源于stack exchange,提问作者Gladitor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:30:53