UNPKG

ngx-i18nsupport

Version:

Some tooling to be used with the Angular 2 i18n workflow

174 lines (161 loc) 6.43 kB
import {ITranslationMessagesFile, ITransUnit, NORMALIZATION_FORMAT_NGXTRANSLATE} from 'ngx-i18nsupport-lib'; import {FileUtil} from '../common/file-util'; import {isNullOrUndefined} from 'util'; import {NgxTranslateExtractionPattern} from './ngx-translate-extraction-pattern'; /** * Created by roobm on 15.03.2017. * A tool for extracting messages in ngx-translate format. * Generates a json-file to be used with ngx-translate. */ /** * The interface used for translations in ngx-translate. * A hash that contains either the translation or another hash. */ interface NgxTranslations { [id: string]: NgxTranslations | string; } /** * internal, * a message with id (a dot-separated string). */ interface NgxMessage { id: string; // dot separated name, e.g. "myapp.service1.message1" message: string; // the message, placeholder are in {{n}} syntax, e.g. "a test with value: {{0}} } export class NgxTranslateExtractor { public static DefaultExtractionPattern = '@@|ngx-translate'; private extractionPattern: NgxTranslateExtractionPattern; /** * Check, wether extractionPattern has valid syntax. * @param {string} extractionPatternString * @return {string} null, if pattern is ok, string describing the error, if it is not ok. */ public static checkPattern(extractionPatternString: string): string { try { new NgxTranslateExtractionPattern(extractionPatternString); } catch (error) { return error.message; } return null; } public static extract(messagesFile: ITranslationMessagesFile, extractionPattern: string, outputFile: string) { new NgxTranslateExtractor(messagesFile, extractionPattern).extractTo(outputFile); } constructor(private messagesFile: ITranslationMessagesFile, extractionPatternString: string) { this.extractionPattern = new NgxTranslateExtractionPattern(extractionPatternString); } /** * Extact messages and write them to a file. * @param outputFile */ public extractTo(outputFile: string) { let translations: NgxTranslations = this.toNgxTranslations(this.extract()); if (translations && Object.keys(translations).length > 0) { FileUtil.replaceContent(outputFile, JSON.stringify(translations, null, 4), 'UTF-8') } else { if (FileUtil.exists(outputFile)) { FileUtil.deleteFile(outputFile); } } } /** * Extract messages and convert them to ngx translations. * @return the translation objects. */ private extract(): NgxMessage[] { let result: NgxMessage[] = []; this.messagesFile.forEachTransUnit((tu: ITransUnit) => { let ngxId = this.ngxTranslateIdFromTU(tu); if (ngxId) { let messagetext = tu.targetContentNormalized().asDisplayString(NORMALIZATION_FORMAT_NGXTRANSLATE); result.push({id: ngxId, message: messagetext}); } }); return result; } /** * Check, wether this tu should be extracted for ngx-translate usage, and return its id for ngx-translate. * There are 2 possibilities: * 1. description is set to "ngx-translate" and meaning contains the id. * 2. id is explicitly set to a string. * @param tu * @return an ngx id or null, if this tu should not be extracted. */ private ngxTranslateIdFromTU(tu: ITransUnit): string { if (this.isExplicitlySetId(tu.id)) { if (this.extractionPattern.isExplicitIdMatched(tu.id)) { return tu.id; } else { return null; } } let description = tu.description(); if (description && this.extractionPattern.isDescriptionMatched(description)) { return tu.meaning(); } } /** * Test, wether ID was explicitly set (via i18n="@myid). * Just heuristic, an ID is explicitly, if it does not look like a generated one. * @param id * @return {boolean} */ private isExplicitlySetId(id: string): boolean { if (isNullOrUndefined(id)) { return false; } // generated IDs are either decimal or sha1 hex let reForGeneratedId = /^[0-9a-f]{11,}$/; return !reForGeneratedId.test(id); } /** * Convert list of relevant TUs to ngx translations object. * @param msgList */ private toNgxTranslations(msgList: NgxMessage[]): NgxTranslations { let translationObject: NgxTranslations = {}; msgList.forEach((msg: NgxMessage) => { this.putInTranslationObject(translationObject, msg); }); return translationObject; } /** * Put a new messages into the translation data object. * If you add, e.g. "{id: 'myapp.example', message: 'test'}", * the translation object will then contain an object myapp that has property example: * {myapp: { * example: 'test' * }} * @param translationObject * @param msg */ private putInTranslationObject(translationObject: NgxTranslations, msg: NgxMessage) { let firstPartOfId: string; let restOfId: string; let indexOfDot = msg.id.indexOf('.'); if (indexOfDot == 0 || indexOfDot == (msg.id.length - 1)) { throw new Error('bad nxg-translate id "' + msg.id + '"'); } if (indexOfDot < 0) { firstPartOfId = msg.id; restOfId = ''; } else { firstPartOfId = msg.id.substring(0, indexOfDot); restOfId = msg.id.substring(indexOfDot + 1); } let object = translationObject[firstPartOfId]; if (isNullOrUndefined(object)) { if (restOfId === '') { translationObject[firstPartOfId] = msg.message; return; } object = {}; translationObject[firstPartOfId] = object; } else { if (restOfId === '') { throw new Error('duplicate id praefix "' + msg.id + '"'); } } this.putInTranslationObject(<NgxTranslations> object, {id: restOfId, message: msg.message}); } }