【问题标题】:How could you implement a database-approach to Angular internationalization?您如何实现 Angular 国际化的数据库方法?
【发布时间】:2018-05-16 07:33:53
【问题描述】:

我在这里阅读了 Angular i8n 指南:https://angular.io/guide/i18n

我喜欢这些概念,在文件中使用标记和提示。

我不喜欢文本资源被困在一种奇怪的文件格式中,这种格式很容易翻译一次,但很难由外部维护。

是否有一种相当直接的方式来使用 Angular 支持 i8n 的方式,但将静态文本文件替换为对数据库的调用——甚至是数据库生成的 json 文件之类的东西?

【问题讨论】:

    标签: angular internationalization


    【解决方案1】:

    在我的项目中,我们将字符串文字(需要国际化)以x.y.z="Hello World" 等格式存储在单独的文件中。我们开发了一个脚本来将此字符串转换为 JSON 对象为

    x { y { z: { default: "Hello World" } } }

    然后我们使用“ng2-translate”库在 Angular 4 项目中使用这个 JSON 文件进行国际化。

    【讨论】:

      【解决方案2】:

      我可以建议您使用ngx-translate(之前称为ng2-translate)库,它的API 比Angular 内置的国际化更好...这样您可以从静态json 文件加载翻译(可以是由后端生成)或例如添加toml-loader 并存储您的翻译,如下例所示(在文件assets/i18n/en.toml 中):

      [homepage]
      title = "Homepage title"
      contact = "Contact"
      
      [auth]
      login = "Log in"
      logout = "Log out"
      
      [settings]
      settings = "Settings"
      body = "<b>HTML</b> is fine here"
      

      并以这种方式使用它:

      <h2>{{'settings.settings' | translate}}</h2>
      <p [innerHTML]="'settings.body' | translate"></p>
      {{'auth.logout' | translate}}
      

      要设置它,您基本上需要几行代码:

      import * as translation_en from 'toml-loader!../assets/i18n/en.toml';
      
      @Injectable()
      export class AppService {
          constructor(private translateService: TranslateService) {
              translateService.setDefaultLang('en');
              translateService.setTranslation('en', translation_en);
              translateService.use('en');
          }
      }
      

      希望对你有帮助!

      【讨论】:

        【解决方案3】:

        我建议你使用angular-l10n 库。这是用于角度定位的 i18n 的开源替代方案。它使用 JSON 格式并支持从服务加载。这里也是文档的链接。 angular-l10n configuration 在页面搜索“加载翻译数据”,可以找到从 Web API 加载的信息。

        【讨论】:

          【解决方案4】:

          请记住,ngx-translate 的创建者现在在 Angular i18n 核心团队中,并且作为 Angular 5.x 的一部分,正在努力使 i18n 变得更好。例如。翻译服务、AOT 中的运行时翻译切换等等。

          请看这里:https://github.com/angular/angular/issues/11405#issuecomment-343933617

          所以我建议坚持使用 Angular 开箱即用的 i18n。

          对于我公司的网站,我们使用 Text United 进行翻译,效果很好。 我们唯一的问题是默认情况下 HTML 标记最终出现在翻译工具中。我们的解决方案是:

          • 使用 XTB
          • 在 Text United 中,使用自定义 XML 解析器。排除 ph 元素。

          Text United 提供付费选项,可以聘请翻译人员以任何语言为您完成工作。当然你也可以自己做。 每次您只需上传源语言,它就会匹配已翻译的项目。

          【讨论】:

          • 我想主要要求是避免“奇怪”的文件格式(而 XTB 是很奇怪)。内置 i18n 的路线图是乐观的,但目前有哪些选择?内置 i18n 仍然不支持语言环境更改,并且在功能方面不如 ngx-translate。
          • 不知道为什么 XTB / XMB 很奇怪是个问题,只是为工作选择合适的工具。当您将 DOCX 作为 Zip 文件打开然后检查其中的文件时,DOCX 也很奇怪......同意 i18n 需要改进,但我更喜欢坚持使用开箱即用而不是可能会得到的 3rd 方组件过时了。
          • 问题是问题被忽略了,我不喜欢文本资源卡在奇怪的文件格式,这就是它所要求的。工具选择也是一个问题。我猜你推荐了一个付费工具来处理那些可以用免费工具(如 PO 格式)或严肃的 JSON 地图来处理的东西,这解释了这里出了什么问题。甚至 XLIFF 格式似乎更可行,但它仍然缺乏适当支持它的工具。
          【解决方案5】:

          您可以使用ngx-translate,这是 Angular 2+ 中国际化的标准库

          您可以导入库并创建一组包含翻译的 json 文件并将其放在 assets 文件夹中。

          然后你可以在 HTML 中引用它。比如说。

          en.json 有,

          "guest.first-name": "first Name",

          其中第一个是键,第二个是要显示的值。您可以在 html 中引用为,

          <input  [label]="'guest.first-name' | translate" type="text" name="form_name" [(ngModel)]="firstName" required="required" ></input>
          

          【讨论】:

            【解决方案6】:

            这是我处理 i18n 的方法,包括在从数据库加载翻译时使用ngx-translate。

            在翻译方面,我的后端和前端是分开的。翻译不在 Angular 构建或服务器包中提供,而是通过 HTTP REST 调用从底层 数据库 中获取信息。所有翻译都在启动时加载,带入 JSON 结构,然后可以传递到前端,由 ngx-translate 处理其余部分。这是从数据库成功加载翻译并使其可访问的简单事件顺序前端。

            1. 数据库中的安全翻译
            2. 在后端启动时加载翻译(或实现重新加载机制,可能通过 REST)
            3. 将翻译映射到键值对 JSON 对象
            4. 使 JSON 对象可通过 REST api 访问
            5. 前端通过这个 REST api 加载 JSON 对象
            6. 使用带有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 仅翻译第一个子项上的键(至少我认为确实如此)。所以用起来很酷,但有时有点棘手。



            我想就是这样。我目前正在使用这种方法,我对结果非常满意。启动它需要做一些工作,但一旦一切顺利,它就会非常有用。

            【讨论】:

            • 是否可以对原生 Angular i18n 做同样的事情?
            • 说实话,我真的不知道。我在 1.5 年前就采用了这种方法,从那时起 Angular 世界发生了很多变化。我不知道本机 Angular i18n 的当前实现是如何工作的,但我很确定它与 ngx-translate 方法不同。也许可以包含其中的一部分,但我猜不是整个实现都适用于原生 Angular。
            【解决方案7】:

            您也可以考虑Transloco。它使用 JSON 存储格式并允许您加载 JSON 文件 from any source,因此您可以从数据库加载它并重新格式化它,然后再将其返回给服务。

            我不应该说这是一个本地化 (l10n) 解决方案,而不是一个完整的国际化 (i18n) 解决方案,但无论如何这可能就是您所寻找的。​​p>

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-08-22
              • 2014-03-08
              • 1970-01-01
              • 1970-01-01
              • 2016-05-03
              相关资源
              最近更新 更多