这是我处理 i18n 的方法,包括在从数据库加载翻译时使用ngx-translate。
在翻译方面,我的后端和前端是分开的。翻译不在 Angular 构建或服务器包中提供,而是通过 HTTP REST 调用从底层 数据库 中获取信息。所有翻译都在启动时加载,带入 JSON 结构,然后可以传递到前端,由 ngx-translate 处理其余部分。这是从数据库成功加载翻译并使其可访问的简单事件顺序前端。
- 数据库中的安全翻译
- 在后端启动时加载翻译(或实现重新加载机制,可能通过 REST)
- 将翻译映射到键值对 JSON 对象
- 使 JSON 对象可通过 REST api 访问
- 前端通过这个 REST api 加载 JSON 对象
- 使用带有ngx-translate的角度JSON对象
好处
稍后我将详细解释它的外观,只是简要说明这种数据库休息方法带来的好处:
- 所有翻译都存储在一个地方(一个表)
- 可以避免缺少某种语言的翻译(NULL 检查)
- 可以避免双键分配(PRIMARY KEY)
- 可以在运行时更新翻译
- 翻译过程可以在项目之外进行(不需要更新项目结构中的文件)
让我们看看如何实现。
数据库
翻译通常由基于翻译文件的简单键值对组成,我从来没有真正喜欢过。因此,相反,我将我的翻译保存在一个表中,其中包含我拥有的每种语言的 key 列和 translation 列,例如 KEY | EN | FR | DE 之类的值,例如 @ 987654327@。该键表示与普通文件中相同的键,但不是每种语言的单独文件,而是每个翻译保存在单个列中。
到 JSON 对象的后端映射
我喜欢一次加载整个表格,以便同时为前端交付准备好每种语言。这通常可以在后端启动时完成一次,结果可以保存在内存中以避免多次数据库调用。该表必须为每个语言列分成键值对 JSON 对象。然后,每个生成的语言对象都包含数据库键作为它们的键和翻译作为它们的值。
var EN = {
...
"button.close": "close",
...
}
var FR = {
...
"button.close": "près",
...
}
var DE = {
...
"button.close": "schließen",
...
}
这只是一个数组到对象的映射,取决于服务器语言,它通常非常简单(如果需要,我可以分享我的 node.js 代码)。结果是 JSON 语言对象的列表,每个对象都以键值对的形式进行翻译,之后可以访问这些对象。
HTTP 休息调用
翻译现在的格式与普通翻译文件的格式(键值对)几乎相同,只是保存在内存中而不是文件中。通过对特定语言的简单 HTTP api 调用,您可以访问此列表,获取该语言的翻译对象并将其直接发送到前端。这是一个node.js express 示例。
translationRouter.route('/:lang').get(function (request, response) {
// load translation key-value-pair object for requested language
response.send(translationService.getTranslations(request.params.lang));
});
ngx 翻译
ngx-translate 的工作方式非常简单。翻译被加载到 Angular 应用程序中,翻译键在应用程序中指定,然后被提供的语言的翻译值动态替换。正如其他人所说,它支持加载翻译的不同方式,例如普通的旧翻译文件或自我实现的加载器,如 HTTP 加载器。这是一个简单的 HTTP 加载器,它通过 REST 调用加载翻译(见上文)。
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';
import '../rxjs-operators';
export class TranslationLoader implements TranslateLoader {
constructor(private http: HttpClient) { }
getTranslation(lang: string): Observable<any> {
return this.http.get("/api/translation/" + lang);
}
}
唯一的技巧就是将此加载器指定为主加载器,这可以在 app.module 中完成。这是一个使用上述 HTTP 加载器的示例(也适用于 AOT)。
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslationLoader(http);
}
...
@NgModule({
imports: [...,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (HttpLoaderFactory),
deps: [HttpClient]
}
}), ...]
})
ngx-translate 不是请求文件,而是使用指定的 TranslationLoader 来获取其键值对,这正是我们通过 rest 调用提供的。十分简单。对于这种情况,可以指定要加载的语言,加上一个备用语言以防找不到值。这是加载默认语言的翻译以及浏览器语言的翻译的示例。
// fallback language
this.translate.setDefaultLang('en');
// browser language
this.translate.use(this.translate.getBrowserLang());
ngx-translate 的文档非常好,有多种使用方式,例如通过服务、指令或管道,另外您还可以参数化翻译。
其他信息
重新加载翻译
如好处列表中所述,您还可以在运行时重新加载翻译,这在捆绑应用程序以进行交付时可能是一件更复杂的事情。您可以简单地为管理员提供一个 HTTP 休息调用,该调用执行与启动时完全相同的翻译加载过程。通过这种方式,翻译可以重新加载、重新映射并存储在内存中。较新的页面请求将自动使用重新加载的翻译对象。
现场语言变化
使用 ngx-translate 的一些方法允许即时转换切换(例如通过指令)。这种方式以角度加载不同的语言(通过简单的this.translate.use(lang) 调用)将立即切换显示的翻译,而无需重新加载页面或可见组件,这实际上非常简洁,但不幸的是不适用于所有使用方式。
ngx 翻译的限制
虽然 ngx-translate 真的很容易使用,但它也有局限性。其中之一是使用 ngx-translate 指令与大多数 angular material 指令结合使用,因为角度材料指令(例如按钮)将创建子树结构,并且 ngx-translate 仅翻译第一个子项上的键(至少我认为确实如此)。所以用起来很酷,但有时有点棘手。
我想就是这样。我目前正在使用这种方法,我对结果非常满意。启动它需要做一些工作,但一旦一切顺利,它就会非常有用。