打字稿中的Angular 6和i18n

时间:2018-09-19 14:46:30

标签: angular typescript internationalization

我看到angular6为其组件实现了i18n,并且通过使用i18n可以将html国际化,但是您可以对打字稿做同样的事情吗?我有两个特定领域

一个有趣的图表:-能够举个例子

exampleData = {
 valueBox: {
                text: '<span style="font-size: 32px">%pie-total-value</span> <br/> Example',
                placement: 'center',
                fontWeight: 'normal'
            },
     }

非常感谢您的时间和答复。

4 个答案:

答案 0 :(得分:3)

在Angular 9中,您可以像这样使用全局$ localize函数:

func loadImageFromDiskWith(fileName: String) -> UIImage? {
  let documentDirectory = FileManager.SearchPathDirectory.documentDirectory

    let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
    let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)

    if let dirPath = paths.first {
        let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
        let image = UIImage(contentsOfFile: imageUrl.path)
        return image
    }
    return nil
}

请注意:

  1. 在Angular 9 CLI中,不使用xi18n提取这些消息,您 必须手动完成
  2. 如果您没有经过$localize migration,请运行$localize`String to translate`

答案 1 :(得分:2)

直到现在(@angular/language-service v7.2)都无法通过库的API来完成。

以下是我的解决方法(感谢his good post on GitHubthank @BrunoBruzzano for the link的fredrikredflag):


src/app/i18n.service.ts

import {Injectable} from "@angular/core";
import {Xliff2} from '@angular/compiler';
// You can also import {Xliff} or {Xtb} from "@angular/compiler"

declare const require;
const content = require('raw-loader!../i18n/messages.fa.xlf');

@Injectable({
    providedIn: 'root'
})
export class I18nService {
    private readonly xliff: any = new Xliff2().load(content, '');

    get(key: string): string {
        return this.xliff.i18nNodesByMsgId[key][0].value;
    }
}

i18n伪组件(仅用于messages.xlf文件中的自动生成翻译)

  1. src/app/i18n/i18n.component.ts (无关紧要。只需要存在即可)。

    import {Component} from '@angular/core';
    @Component({templateUrl: './i18n.component.html'})
    export class I18nComponent {}
    
  2. src/app/i18n/i18n.component.html 不要忘记使用ID!

    <p i18n="@@newVersionAlert">New version available. Load New Version?</p>
    

别忘了在I18nComponent中声明@NgModule


用法(在运行ng xi18n ...并翻译之后):

在您的组件中

...
import {I18nService} from './i18n.service';

...
    constructor(private i18nService: I18nService, ...) { ... }

    sampleUsage() {
        confirm(this.t('newVersionAlert'));
    }

    /**
     * translate
     */
    private t(i18nId: string) {
        return this.i18nService.get(i18nId);
    }
...

在构建之前翻译i18n.service.ts的实用程序脚本

(此要求:require('raw-loader!../i18n/messages.fa.xlf')需要进行翻译以匹配所需的语言环境。

PreBuild/prebuild.ts

import {Xliff2} from "@angular/compiler";  
// You can also import {Xliff} or {Xtb} from "@angular/compiler" depending of your case.

const fs = require('fs');  
const path = require('path');  

const localeId = process.argv[2];  

if (localeId === undefined) throw new Error(`No language specified.\nUsage: node ${path.basename(__filename)} <locale-id${'>'}`);  

const content = fs.readFileSync(`src/i18n/messages.${localeId}.xlf`, 'utf8');  
const xliff = new Xliff2().load(content, '');

fs.writeFileSync(i18nServiceFilePath,  
  fs.readFileSync(i18nServiceFilePath, 'utf8')  
    .replace(/(raw-loader!\.\.\/i18n\/messages\.)\w{2}(\.xlf)/, `$1${xliff.locale}$2`)  
);

PreBuild/tsconfig.json

{
    "compilerOptions": {
        "outDir": "./build",
        "lib": [
            "es2018",
            "dom"
        ],
        "module": "commonjs",
        "moduleResolution": "node",
        "target": "es6",
        "typeRoots": [
            "../node_modules/@types"
        ]
    },
    "files": [
        "prebuild.ts"
    ]
}

package.json

...
"scripts": {
    "compile-pre-build": "tsc -p PreBuild/tsconfig.json --pretty",
    "pre-build": "node PreBuild/build/prebuild.js",
    ...
...

用法:

(一次npm run compile-pre-build之后:)

npm run pre-build -- fa

npm run pre-build -- en

这将编辑i18n.service.ts

答案 2 :(得分:0)

您可以使用Transloco库执行此操作:https://ngneat.github.io/transloco/

然后像这样在Typescript文件中获取翻译:

this.translocoService.translate('hello');

答案 3 :(得分:-1)

您可以扩展“ ng serve | build”过程,以便在.ts中完成i18n翻译的“ AOT补全”

  • 主要思想是在.ts中使用jour生成的.xlf文件(以及@@ translationId之类的ID)
    import { Component } from '@angular/core';

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent {
      title = 'i18n-ts-demo-ng';
      title2 = '@@my.test.header';
    }
  • 并将其翻译为构建过程

    1. 添加“ ng add ngx-build-plus”
    2. 创建插件
//create file i18n-plugin.ts in root
import { I18NTransformer } from './i18n';
import { AngularCompilerPlugin } from '@ngtools/webpack';

function findAngularCompilerPlugin(webpackCfg): AngularCompilerPlugin | null {
  return webpackCfg.plugins.find(plugin =>  plugin instanceof AngularCompilerPlugin);
}

// The AngularCompilerPlugin has nog public API to add transformations, user private API _transformers instead.
function addTransformerToAngularCompilerPlugin(acp, transformer): void {
  acp._transformers = [transformer, ...acp._transformers];
}

export default {
  pre() {
    // This hook is not used in our example
  },

  // This hook is used to manipulate the webpack configuration
  config(cfg) {
    // Find the AngularCompilerPlugin in the webpack configuration
    const angularCompilerPlugin = findAngularCompilerPlugin(cfg);

    if (!angularCompilerPlugin) {
      console.error('Could not inject the typescript transformer: Webpack AngularCompilerPlugin not found');
      return;
    }

    addTransformerToAngularCompilerPlugin(angularCompilerPlugin, I18NTransformer);
    return cfg;
  },

  post() {
    // This hook is not used in our example
  }
};
//create file i18n.ts in root
import * as ts from 'typescript';

// TODO move to config
const RequireAlli18NKeys = false; // if true onda all 18n keys must be found othervse error is thrown;

// Read translations
import { Xliff, Node } from '@angular/compiler';
const fs = require('fs');
const path = require('path');

let localeId: string; // hr || en ...

let i18nLocale = 0; // 0 - parameter not found | 1 - parameter is fount so next is locale string (hr, ...)

// parse parameters
process.argv.forEach(pParam => {

  console.log('param:' + pParam);
  // get Locale is using: ng serve ...
  if (pParam.startsWith('--configuration=')) {
    localeId = pParam.replace('--configuration=', '');
    console.log('Locale:' + localeId);
  }

  // Has to be before code down
  if (i18nLocale === 1) {
    i18nLocale = 2;
    localeId = pParam;
    console.log('Locale:' + localeId);
  }

  // Get locale if using: ng build --prod --i18n-locale en ...
  if (pParam.startsWith('--i18n-locale')) {
    i18nLocale = 1;
    localeId = pParam.replace('--config--i18n-locale ', '')
  }
});

// Load translation
// tslint:disable-next-line:max-line-length
if (localeId === undefined) { throw new Error(`No language specified.\nUsage: ng serve --configuration=hr --aot --plugin ~dist/out-tsc/i18n-plugin.js`); }
const content = fs.readFileSync(`src/translate/messages.${localeId}.xlf`, 'utf8');
const xliff = new Xliff().load(content, '');

export const I18NTransformer = <T extends ts.Node>(context: ts.TransformationContext) => {
  return (rootNode: ts.SourceFile) => {
    function visit(node: ts.Node): ts.Node {
      if (
        rootNode.fileName.includes('node_modules')
        || !rootNode.fileName.includes('.ts')
        // || ts.isToken(node)
      ) {
        return ts.visitEachChild(node, visit, context);
      }

      if (ts.isStringLiteral(node)) {
        // teplace @@ with translation
        if (node.text.includes('@@')) {
          // take key for translatioc
          const tSourceKey = node.text;
          const tI18NKey = node.text.replace('@@', '');
          // find key
          const tTranslation: any = xliff.i18nNodesByMsgId[tI18NKey];
          if (tTranslation) {
            // let t1 = tTranslation[0];

            // let tLocaleStr = t1.toString(); //tTranslation[0].value;
            const tLocaleStr = tTranslation[0].value;
            console.log(ConsoleColor.BgCyan, 'i18n key: ', ConsoleColor.Reset, tI18NKey + '=> translation   : ' + tLocaleStr);
            const tNew2 = node.text.replace(tSourceKey, tLocaleStr);
            return ts.createStringLiteral(tNew2);
          }
          const tMessage = 'ERROR! No translation for key: ' + tI18NKey + ', source:' + rootNode.fileName;
          console.log(ConsoleColor.BgRed, tMessage, ConsoleColor.Reset);
          if (RequireAlli18NKeys) {
            throw new Error(tMessage);
          }
        }
      }

      return ts.visitEachChild(node, visit, context);
    }
    return ts.visitNode(rootNode, visit);
  };
};

class ConsoleColor {
  static Reset = '\x1b[0m';
  static Bright = '\x1b[1m';
  static Dim = '\x1b[2m';
  static Underscore = '\x1b[4m';
  static Blink = '\x1b[5m';
  static Reverse = '\x1b[7m';
  static Hidden = '\x1b[8m';

  static FgBlack = '\x1b[30m';
  static FgRed = '\x1b[31m';
  static FgGreen = '\x1b[32m';
  static FgYellow = '\x1b[33m';
  static FgBlue = '\x1b[34m';
  static FgMagenta = '\x1b[35m';
  static FgCyan = '\x1b[36m';
  static FgWhite = '\x1b[37m';

  static BgBlack = '\x1b[40m';
  static BgRed = '\x1b[41m';
  static BgGreen = '\x1b[42m';
  static BgYellow = '\x1b[43m';
  static BgBlue = '\x1b[44m';
  static BgMagenta = '\x1b[45m';
  static BgCyan = '\x1b[46m';
  static BgWhite = '\x1b[47m';
}
  • 在终端开始位置:tsc --skipLibCheck --module umd -w

  • ng serve --configuration = hr --aot --plugin〜dist / out-tsc / i18n-plugin.js

完整示例在https://github.com/Emanuel3003/i18n-ts-demo-ng

致谢