需求背景
随着项目的不断迭代和功能的持续扩展,国际化文件的体积显著增加,不仅影响了加载性能,还加大了后期维护的复杂性。为了提升系统的性能与可维护性,建议采用模块化拆分的方式,将国际化文件按功能模块独立管理。这样做不仅可以有效减少每次加载的文件体积,提升系统响应速度,还能使得各个模块的国际化维护更加独立,降低跨模块修改带来的影响,提升开发效率。接下来,我们将详细介绍这种拆分方案的具体实现方法。
配置流程
HttpLoaderFactory
:为 AoT 编译提供工厂函数,生成TranslateHttpLoader
用于加载国际化文件
/**
* AoT(Ahead-of-Time 编译)需要导出一个工厂函数来生成 TranslateHttpLoader 实例。
* 该函数会在应用启动时由 Angular 调用。
*
* @param http HttpClient实例,用于发起HTTP请求
* @returns TranslateHttpLoader实例
*/
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
// 创建并返回 TranslateHttpLoader,用于加载国际化文件
return new TranslateHttpLoader(http, '../assets/i18n/', '');
}
@NgModule({
declarations: [AppComponent],
imports: [
// 国际化配置
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
]
})
initLoadTranslations
:在应用初始化时调用,返回一个 Promise,确保在所有翻译文件加载完
/**
* initLoadTranslations 函数用于在应用初始化时加载指定语言的全部国际化文件。
*
* @param translate TranslateService实例,用于操作翻译内容
* @param http HttpClient实例,用于发起HTTP请求
* @returns 一个函数,该函数返回Promise,当所有语言文件加载完成后,Promise resolve
*/
export function initLoadTranslations(translate: TranslateService, http: HttpClient): () => Promise<void> {
return (): Promise<void> => {
// 返回一个 Promise,确保在所有语言文件加载完成后再继续应用初始化
return new Promise<void>((resolve, reject) => {
// 当前语言,这里默认设为中文(可以根据实际需求修改)
const language: string = LanguageEnum.ZH_CN;
// 调用 loadTranslations 函数加载国际化文件
loadTranslations(language, translate, http, resolve);
});
};
}
@NgModule({
providers: [
{
provide: APP_INITIALIZER,
useFactory: initLoadTranslations,
deps: [TranslateService, HttpClient],
multi: true
}
]
})
getTranslateUrls
:生成当前语言的所有国际化文件的 URL,并带有时间戳以避免缓存
/**
* 获取国际化文件的 URL 地址,避免缓存。
*
* @param language 语言代码,例如 'zh_CN' 或 'en_US'
* @returns 包含当前语言的多个国际化文件的URL数组
*/
export function getTranslateUrls(language: string): Array<string> {
// 当前时间戳,作为 URL 参数,避免浏览器缓存
const timestamp: number = new Date().getTime();
// 返回包含所有需要加载的国际化文件URL
return [
`./assets/i18n/${language}/main.json?t=${timestamp}`
// 其余文件
];
}
getTranslatePromises
:遍历 URL 列表,发起 HTTP 请求获取翻译文件,使用TranslateService
将内容添加至应用中
/**
* 获取请求每个国际化文件的Promise实例,加载后会将其添加到TranslateService中。
*
* @param language 语言代码
* @param translationUrls 语言文件的 URL 数组
* @param translate TranslateService实例
* @param http HttpClient实例
* @returns Promise数组,每个Promise代表一个文件加载完成
*/
export function getTranslatePromises(language: string, translationUrls: Array<string>, translate: TranslateService, http: HttpClient): Array<Promise<void>> {
// 遍历每个URL并发起HTTP请求,加载后将翻译内容设置到TranslateService中
const loadLangs: Promise<void>[] = translationUrls.map((url: string) =>
// 发起HTTP请求获取JSON格式的翻译数据
http.get(url).toPromise().then((translations: any) => {
// 加载完成后将翻译数据加入TranslateService,第三个参数为true表示追加而不是覆盖
translate.setTranslation(language, translations, true);
})
);
// 返回每个请求对应的Promise
return loadLangs;
}
loadTranslations
:实际处理加载过程,等待所有文件加载完成后设置应用语言
/**
* 加载语言文件,并在加载完成后调用resolve函数。
*
* @param language 语言代码,例如 'zh_CN' 或 'en_US'
* @param translate TranslateService实例,用于设置和切换语言
* @param http HttpClient实例,用于发起HTTP请求
* @param resolve 可选的回调函数,当所有语言文件加载完成后调用
*/
export function loadTranslations(language: string, translate: TranslateService, http: HttpClient, resolve?: any): void {
// 获取当前语言的所有国际化文件的 URL 列表
const translationUrls: string[] = getTranslateUrls(language);
// 为每个 URL 创建 Promise,用于处理文件的加载
const loadPromises: Promise<void>[] = getTranslatePromises(language, translationUrls, translate, http);
// 使用 Promise.all 等待所有文件加载完成
Promise.all(loadPromises).then(() => {
// 当所有文件加载完成后,设置应用当前语言
translate.use(language);
// 调用传入的 resolve 函数(如果存在)
resolve && resolve();
}).catch((err: any) => {
// 如果加载过程中出现错误,输出错误信息
console.error('Error loading translations', err);
// 此处可以进一步添加错误处理逻辑
});
}
方案总结
通过本文的介绍,我们详细探讨了如何基于 Angular 进行国际化文件的模块化拆分。随着项目规模的扩大,合理地将国际化文件按功能模块进行拆分,不仅可以提升应用的加载性能,还能有效降低后期的维护成本,确保各个模块的国际化工作更加清晰、独立。同时,这种方案为项目的可扩展性和灵活性提供了更大的支持。在实际应用中,开发者可以根据项目的需求,灵活调整拆分方案,进一步提升项目的开发效率与用户体验。
完整配置代码
- 国际化公共方法文件,集成了在app.module.ts里面需要使用的useFactory。
import { HttpClient } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
// 支持的语言枚举,定义了中文和英文
export enum LanguageEnum {
ZH_CN = 'zh_CN',
EN_US = 'en_US'
}
/**
* AoT(Ahead-of-Time 编译)需要导出一个工厂函数来生成 TranslateHttpLoader 实例。
* 该函数会在应用启动时由 Angular 调用。
*
* @param http HttpClient实例,用于发起HTTP请求
* @returns TranslateHttpLoader实例
*/
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
// 创建并返回 TranslateHttpLoader,用于加载国际化文件
return new TranslateHttpLoader(http, '../assets/i18n/', '');
}
/**
* initLoadTranslations 函数用于在应用初始化时加载指定语言的全部国际化文件。
*
* @param translate TranslateService实例,用于操作翻译内容
* @param http HttpClient实例,用于发起HTTP请求
* @returns 一个函数,该函数返回Promise,当所有语言文件加载完成后,Promise resolve
*/
export function initLoadTranslations(translate: TranslateService, http: HttpClient): () => Promise<void> {
return (): Promise<void> => {
// 返回一个 Promise,确保在所有语言文件加载完成后再继续应用初始化
return new Promise<void>((resolve, reject) => {
// 当前语言,这里默认设为中文(可以根据实际需求修改)
const language: string = LanguageEnum.ZH_CN;
// 调用 loadTranslations 函数加载国际化文件
loadTranslations(language, translate, http, resolve);
});
};
}
/**
* 获取国际化文件的 URL 地址,避免缓存。
*
* @param language 语言代码,例如 'zh_CN' 或 'en_US'
* @returns 包含当前语言的多个国际化文件的URL数组
*/
export function getTranslateUrls(language: string): Array<string> {
// 当前时间戳,作为 URL 参数,避免浏览器缓存
const timestamp: number = new Date().getTime();
// 返回包含所有需要加载的国际化文件URL
return [
`./assets/i18n/${language}/main.json?t=${timestamp}`
];
}
/**
* 获取请求每个国际化文件的Promise实例,加载后会将其添加到TranslateService中。
*
* @param language 语言代码
* @param translationUrls 语言文件的 URL 数组
* @param translate TranslateService实例
* @param http HttpClient实例
* @returns Promise数组,每个Promise代表一个文件加载完成
*/
export function getTranslatePromises(language: string, translationUrls: Array<string>, translate: TranslateService, http: HttpClient): Array<Promise<void>> {
// 遍历每个URL并发起HTTP请求,加载后将翻译内容设置到TranslateService中
const loadLangs: Promise<void>[] = translationUrls.map((url: string) =>
// 发起HTTP请求获取JSON格式的翻译数据
http.get(url).toPromise().then((translations: any) => {
// 加载完成后将翻译数据加入TranslateService,第三个参数为true表示追加而不是覆盖
translate.setTranslation(language, translations, true);
})
);
// 返回每个请求对应的Promise
return loadLangs;
}
/**
* 加载语言文件,并在加载完成后调用resolve函数。
*
* @param language 语言代码,例如 'zh_CN' 或 'en_US'
* @param translate TranslateService实例,用于设置和切换语言
* @param http HttpClient实例,用于发起HTTP请求
* @param resolve 可选的回调函数,当所有语言文件加载完成后调用
*/
export function loadTranslations(language: string, translate: TranslateService, http: HttpClient, resolve?: any): void {
// 获取当前语言的所有国际化文件的 URL 列表
const translationUrls: string[] = getTranslateUrls(language);
// 为每个 URL 创建 Promise,用于处理文件的加载
const loadPromises: Promise<void>[] = getTranslatePromises(language, translationUrls, translate, http);
// 使用 Promise.all 等待所有文件加载完成
Promise.all(loadPromises).then(() => {
// 当所有文件加载完成后,设置应用当前语言
translate.use(language);
// 调用传入的 resolve 函数(如果存在)
resolve && resolve();
}).catch((err: any) => {
// 如果加载过程中出现错误,输出错误信息
console.error('Error loading translations', err);
// 此处可以进一步添加错误处理逻辑
});
}
- app.module.ts文件。
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { HttpLoaderFactory, initLoadTranslations } from './utils/translate/use-factory';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
FormsModule,
HttpClientModule,
// 国际化配置
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [
{
// 由于请求国际化文件是异步的,有时系统初始化时会出现国际化文件未加载,渲染时找不到翻译文件,故在初始化时先加载国际化文件
provide: APP_INITIALIZER,
useFactory: initLoadTranslations,
deps: [TranslateService, HttpClient],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
- 国际化文件目录结构,目录名必须与使用语言的key对应,不然会加载不到翻译文件。
src
└── assets
└── i18n
├── en_US
│ └── main.json
└── zh_CN
└── main.json