Last active
December 11, 2019 01:35
-
-
Save mohlendo/90c834f90bc084af2b869ca36a20712f to your computer and use it in GitHub Desktop.
Angular i18n translations outside a template - JIT only
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component } from '@angular/core' | |
@Component({ | |
selector: 'i18n', | |
moduleId: module.id, | |
template: `<span i18n="@@foobar">Hello {{placeholder}}!</span>` | |
}) | |
export class I18NComponent { | |
placeholder: any | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Inject, Injectable, Optional, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core' | |
import { HtmlParser, I18NHtmlParser, Xliff, Xliff2, Xmb } from '@angular/compiler' | |
/** | |
* Service that fills the gap between i18n templates and dynamic messages. | |
* Add new messages to the template of the {@see I18NComponent}. | |
* Only simple interpolations are supported no ICU stuff. | |
*/ | |
@Injectable() | |
export class I18NService { | |
private readonly parser: I18NHtmlParser | |
private readonly translations: { | |
locale: string; | |
i18nNodesByMsgId: { | |
[msgId: string]: any; | |
}; | |
} | |
// simple cache for the prepared patterns | |
private readonly patternCache: { [key: string]: string } = {} | |
constructor ( | |
@Optional() @Inject(TRANSLATIONS) source: string = null, | |
@Inject(TRANSLATIONS_FORMAT) format = 'xlf2' | |
) { | |
if (source) { | |
let serializer | |
switch (format) { | |
case 'xlf2': | |
serializer = new Xliff2() | |
break | |
case 'xmb': | |
serializer = new Xmb() | |
break | |
case 'xlf': | |
serializer = new Xliff() | |
break | |
} | |
this.translations = serializer.load(source, '') | |
this.parser = new I18NHtmlParser(new HtmlParser(), source) | |
} else { | |
this.translations = {locale: 'en', i18nNodesByMsgId: {}} | |
} | |
} | |
private static toParseString (nodes: any[]): string { | |
let interpolationIndex = 0 | |
return nodes.reduce((prev, node) => { | |
if (node.hasOwnProperty('name')) { | |
return `${prev}{{${interpolationIndex++}}}` | |
} | |
if (node.hasOwnProperty('value')) { | |
return `${prev}${node.value}` | |
} | |
}, '').trim() | |
} | |
private static interpolate (pattern: string, interpolation: any[]) { | |
// this is actually a static message | |
if (interpolation.length === 0) { | |
return pattern | |
} | |
let compiled = '' | |
compiled += pattern.replace(/{{(\w+)}}/g, (match, key) => { | |
if (interpolation[key] !== undefined) { | |
match = match.replace(`{{${key}}}`, interpolation[key]) | |
} | |
return match | |
}) | |
return compiled | |
} | |
/** | |
* Returns the translated message for the given key. | |
* The order of the interpolation must match the order in the message. Don't change it during translation. | |
* @param {string} key translation key | |
* @param {any[]} interpolation the list of interpolations | |
* @return {string} the resulting message | |
*/ | |
get (key: string, interpolation: any[] = []): string { | |
if (!key) { | |
throw new Error('key cannot be empty') | |
} | |
const nodes = this.translations.i18nNodesByMsgId[key] | |
if (!nodes) { | |
console.warn(`Missing translation for message "${key}"`) | |
return key | |
} | |
// check if we have a cache-entry | |
let pattern = this.patternCache[key] | |
if (!pattern) { | |
const parseTree = this.parser.parse(`<div i18n="@@${key}">${I18NService.toParseString(nodes)}</div>`, '') | |
pattern = parseTree.rootNodes[0]['children'][0].value | |
// cache the pattern | |
this.patternCache[key] = pattern | |
} | |
return I18NService.interpolate(pattern, interpolation) | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component } from '@angular/core' | |
@Component({ | |
selector: 'test', | |
moduleId: module.id | |
}) | |
export class I18NComponent { | |
constructor(readonly i18nService: I18NService) { | |
console.log(i18nService.get('foobar', ['World']) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment