test(ivy): add i18n integration tests via TestBed (#27852)
PR Close #27852
This commit is contained in:
parent
ac5f5ed0a6
commit
4613864fc8
|
@ -389,7 +389,7 @@ describe('i18n support in the view compiler', () => {
|
|||
it('should correctly bind to context in nested template', () => {
|
||||
const input = `
|
||||
<div *ngFor="let outer of items">
|
||||
<div i18n-title="m|d" title="different scope {{ outer | uppercase }}">
|
||||
<div i18n-title="m|d" title="different scope {{ outer | uppercase }}"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -518,7 +518,7 @@ describe('i18n support in the view compiler', () => {
|
|||
it('should correctly bind to context in nested template', () => {
|
||||
const input = `
|
||||
<div *ngFor="let outer of items">
|
||||
<div i18n-title="m|d" title="different scope {{ outer | uppercase }}">
|
||||
<div i18n-title="m|d" title="different scope {{ outer | uppercase }}"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1347,7 +1347,7 @@ describe('i18n support in the view compiler', () => {
|
|||
verify(input, output);
|
||||
});
|
||||
|
||||
it('should be able to be child elements inside i18n block', () => {
|
||||
it('should be able to act as child elements inside i18n block', () => {
|
||||
const input = `
|
||||
<div i18n>
|
||||
<ng-template>Template content: {{ valueA | uppercase }}</ng-template>
|
||||
|
|
|
@ -504,7 +504,7 @@ function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode |
|
|||
* @publicAPI
|
||||
*/
|
||||
export function i18nPostprocess(
|
||||
message: string, replacements: {[key: string]: (string | string[])}): string {
|
||||
message: string, replacements: {[key: string]: (string | string[])} = {}): string {
|
||||
//
|
||||
// Step 1: resolve all multi-value cases (like [<5B>*1:1<><31>#2:1<>|<7C>#4:1<>|<7C>5<EFBFBD>])
|
||||
//
|
||||
|
|
|
@ -0,0 +1,480 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Directive, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {fixmeIvy, onlyInIvy, polyfillGoogGetMsg} from '@angular/private/testing';
|
||||
|
||||
@Directive({
|
||||
selector: '[tplRef]',
|
||||
})
|
||||
class DirectiveWithTplRef {
|
||||
constructor(public vcRef: ViewContainerRef, public tplRef: TemplateRef<{}>) {}
|
||||
ngOnInit() { this.vcRef.createEmbeddedView(this.tplRef, {}); }
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
class MyComp {
|
||||
name = 'John';
|
||||
items = ['1', '2', '3'];
|
||||
visible = true;
|
||||
age = 20;
|
||||
count = 2;
|
||||
otherLabel = 'other label';
|
||||
}
|
||||
|
||||
const TRANSLATIONS: any = {
|
||||
'one': 'un',
|
||||
'two': 'deux',
|
||||
'more than two': 'plus que deux',
|
||||
'ten': 'dix',
|
||||
'twenty': 'vingt',
|
||||
'other': 'autres',
|
||||
'Hello': 'Bonjour',
|
||||
'Hello {$interpolation}': 'Bonjour {$interpolation}',
|
||||
'Bye': 'Au revoir',
|
||||
'Item {$interpolation}': 'Article {$interpolation}',
|
||||
'\'Single quotes\' and "Double quotes"': '\'Guillemets simples\' et "Guillemets doubles"',
|
||||
'My logo': 'Mon logo',
|
||||
'{$startTagSpan}My logo{$tagImg}{$closeTagSpan}':
|
||||
'{$startTagSpan}Mon logo{$tagImg}{$closeTagSpan}',
|
||||
'{$startTagNgTemplate} Hello {$closeTagNgTemplate}{$startTagNgContainer} Bye {$closeTagNgContainer}':
|
||||
'{$startTagNgTemplate} Bonjour {$closeTagNgTemplate}{$startTagNgContainer} Au revoir {$closeTagNgContainer}',
|
||||
'{$startTagNgTemplate}{$startTagSpan}Hello{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan_1}Hello{$closeTagSpan}{$closeTagNgContainer}':
|
||||
'{$startTagNgTemplate}{$startTagSpan}Bonjour{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan_1}Bonjour{$closeTagSpan}{$closeTagNgContainer}',
|
||||
'{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}':
|
||||
'{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autres}}',
|
||||
'{VAR_SELECT, select, 10 {10 - {$startBoldText}ten{$closeBoldText}} 20 {20 - {$startItalicText}twenty{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}other{$closeUnderlinedText}{$closeTagDiv}}}':
|
||||
'{VAR_SELECT, select, 10 {10 - {$startBoldText}dix{$closeBoldText}} 20 {20 - {$startItalicText}vingt{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}autres{$closeUnderlinedText}{$closeTagDiv}}}',
|
||||
'{VAR_SELECT_2, select, 10 {ten - {VAR_SELECT, select, 1 {one} 2 {two} other {more than two}}} 20 {twenty - {VAR_SELECT_1, select, 1 {one} 2 {two} other {more than two}}} other {other}}':
|
||||
'{VAR_SELECT_2, select, 10 {dix - {VAR_SELECT, select, 1 {un} 2 {deux} other {plus que deux}}} 20 {vingt - {VAR_SELECT_1, select, 1 {un} 2 {deux} other {plus que deux}}} other {autres}}'
|
||||
};
|
||||
|
||||
const getFixtureWithOverrides = (overrides = {}) => {
|
||||
TestBed.overrideComponent(MyComp, {set: overrides});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
return fixture;
|
||||
};
|
||||
|
||||
onlyInIvy('Ivy i18n logic').describe('i18n', function() {
|
||||
|
||||
beforeEach(() => {
|
||||
polyfillGoogGetMsg(TRANSLATIONS);
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithTplRef]});
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
it('should translate static attributes', () => {
|
||||
const title = 'Hello';
|
||||
const template = `<div i18n-title="m|d" title="${title}"></div>`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.title).toBe('Bonjour');
|
||||
});
|
||||
|
||||
it('should support interpolation', () => {
|
||||
const title = 'Hello {{ name }}';
|
||||
const template = `<div i18n-title="m|d" title="${title}"></div>`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.title).toBe('Bonjour John');
|
||||
});
|
||||
|
||||
it('should support interpolation with custom interpolation config', () => {
|
||||
const title = 'Hello {% name %}';
|
||||
const template = `<div i18n-title="m|d" title="${title}"></div>`;
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const fixture = getFixtureWithOverrides({template, interpolation});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.title).toBe('Bonjour John');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-903: i18n attributes in nested templates throws at runtime')
|
||||
.it('should correctly bind to context in nested template', () => {
|
||||
const title = 'Item {{ id }}';
|
||||
const template = `
|
||||
<div *ngFor='let id of items'>
|
||||
<div i18n-title='m|d' title='${title}'></div>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
for (let i = 0; i < element.children.length; i++) {
|
||||
const child = element.children[i];
|
||||
expect((child as any).innerHTML).toBe(`<div title="Article ${i + 1}"></div>`);
|
||||
}
|
||||
});
|
||||
|
||||
fixmeIvy('FW-904: i18n attributes placed on i18n root node don\'t work')
|
||||
.it('should work correctly when placed on i18n root node', () => {
|
||||
const title = 'Hello {{ name }}';
|
||||
const content = 'Hello';
|
||||
const template = `
|
||||
<div i18n i18n-title="m|d" title="${title}">${content}</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.title).toBe('Bonjour John');
|
||||
expect(element).toHaveText('Bonjour');
|
||||
});
|
||||
|
||||
it('should add i18n attributes on self-closing tags', () => {
|
||||
const title = 'Hello {{ name }}';
|
||||
const template = `<img src="logo.png" i18n-title title="${title}">`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.title).toBe('Bonjour John');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested nodes', () => {
|
||||
it('should handle static content', () => {
|
||||
const content = 'Hello';
|
||||
const template = `<div i18n>${content}</div>`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('Bonjour');
|
||||
});
|
||||
|
||||
it('should support interpolation', () => {
|
||||
const content = 'Hello {{ name }}';
|
||||
const template = `<div i18n>${content}</div>`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('Bonjour John');
|
||||
});
|
||||
|
||||
it('should support interpolation with custom interpolation config', () => {
|
||||
const content = 'Hello {% name %}';
|
||||
const template = `<div i18n>${content}</div>`;
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const fixture = getFixtureWithOverrides({template, interpolation});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('Bonjour John');
|
||||
});
|
||||
|
||||
it('should properly escape quotes in content', () => {
|
||||
const content = `'Single quotes' and "Double quotes"`;
|
||||
const template = `<div i18n>${content}</div>`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('\'Guillemets simples\' et "Guillemets doubles"');
|
||||
});
|
||||
|
||||
it('should correctly bind to context in nested template', () => {
|
||||
const content = 'Item {{ id }}';
|
||||
const template = `
|
||||
<div *ngFor='let id of items'>
|
||||
<div i18n>${content}</div>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
for (let i = 0; i < element.children.length; i++) {
|
||||
const child = element.children[i];
|
||||
expect(child).toHaveText(`Article ${i + 1}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle i18n attributes inside i18n section', () => {
|
||||
const title = 'Hello {{ name }}';
|
||||
const template = `
|
||||
<div i18n>
|
||||
<div i18n-title="m|d" title="${title}"></div>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
const content = `<div title="Bonjour John"></div>`;
|
||||
expect(element.innerHTML).toBe(content);
|
||||
});
|
||||
|
||||
it('should handle i18n blocks in nested templates', () => {
|
||||
const content = 'Hello {{ name }}';
|
||||
const template = `
|
||||
<div *ngIf="visible">
|
||||
<div i18n>${content}</div>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.children[0]).toHaveText('Bonjour John');
|
||||
});
|
||||
|
||||
it('should ignore i18n attributes on self-closing tags', () => {
|
||||
const template = '<img src="logo.png" i18n>';
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element.innerHTML).toBe(template.replace(' i18n', ''));
|
||||
});
|
||||
|
||||
it('should handle i18n attribute with directives', () => {
|
||||
const content = 'Hello {{ name }}';
|
||||
const template = `
|
||||
<div *ngIf="visible" i18n>${content}</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('Bonjour John');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ng-container and ng-template support', () => {
|
||||
it('should handle single translation message within ng-container', () => {
|
||||
const content = 'Hello {{ name }}';
|
||||
const template = `
|
||||
<ng-container i18n>${content}</ng-container>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('Bonjour John');
|
||||
});
|
||||
|
||||
it('should handle single translation message within ng-template', () => {
|
||||
const content = 'Hello {{ name }}';
|
||||
const template = `
|
||||
<ng-template i18n tplRef>${content}</ng-template>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element).toHaveText('Bonjour John');
|
||||
});
|
||||
|
||||
it('should be able to act as child elements inside i18n block (plain text content)', () => {
|
||||
const hello = 'Hello';
|
||||
const bye = 'Bye';
|
||||
const template = `
|
||||
<div i18n>
|
||||
<ng-template tplRef>
|
||||
${hello}
|
||||
</ng-template>
|
||||
<ng-container>
|
||||
${bye}
|
||||
</ng-container>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Bonjour Au revoir');
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-910: Invalid placeholder structure generated when using <ng-template> with content that contains tags')
|
||||
.it('should be able to act as child elements inside i18n block (text + tags)', () => {
|
||||
const content = 'Hello';
|
||||
const template = `
|
||||
<div i18n>
|
||||
<ng-template tplRef>
|
||||
<span>${content}</span>
|
||||
</ng-template>
|
||||
<ng-container>
|
||||
<span>${content}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
const spans = element.getElementsByTagName('span');
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const child = spans[i];
|
||||
expect((child as any).innerHTML).toBe('Bonjour');
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle self-closing tags as content', () => {
|
||||
const label = 'My logo';
|
||||
const content = `${label}<img src="logo.png" title="Logo">`;
|
||||
const template = `
|
||||
<ng-container i18n>
|
||||
<span>${content}</span>
|
||||
</ng-container>
|
||||
<ng-template i18n tplRef>
|
||||
<span>${content}</span>
|
||||
</ng-template>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
const spans = element.getElementsByTagName('span');
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const child = spans[i];
|
||||
expect(child).toHaveText('Mon logo');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ICU logic', () => {
|
||||
it('should handle single ICUs', () => {
|
||||
const template = `
|
||||
<div i18n>{age, select, 10 {ten} 20 {twenty} other {other}}</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element).toHaveText('vingt');
|
||||
});
|
||||
|
||||
it('should support ICUs generated outside of i18n blocks', () => {
|
||||
const template = `
|
||||
<div>{age, select, 10 {ten} 20 {twenty} other {other}}</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element).toHaveText('vingt');
|
||||
});
|
||||
|
||||
it('should support interpolation', () => {
|
||||
const template = `
|
||||
<div i18n>{age, select, 10 {ten} other {{{ otherLabel }}}}</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element).toHaveText(fixture.componentInstance.otherLabel);
|
||||
});
|
||||
|
||||
it('should support interpolation with custom interpolation config', () => {
|
||||
const template = `
|
||||
<div i18n>{age, select, 10 {ten} other {{% otherLabel %}}}</div>
|
||||
`;
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const fixture = getFixtureWithOverrides({template, interpolation});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element).toHaveText(fixture.componentInstance.otherLabel);
|
||||
});
|
||||
|
||||
it('should handle ICUs with HTML tags inside', () => {
|
||||
const template = `
|
||||
<div i18n>
|
||||
{age, select, 10 {10 - <b>ten</b>} 20 {20 - <i>twenty</i>} other {<div class="other"><u>other</u></div>}}
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
const italicTags = element.getElementsByTagName('i');
|
||||
expect(italicTags.length).toBe(1);
|
||||
expect(italicTags[0].innerHTML).toBe('vingt');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-905: Multiple ICUs in one i18n block are not processed')
|
||||
.it('should handle multiple ICUs in one block', () => {
|
||||
const template = `
|
||||
<div i18n>
|
||||
{age, select, 10 {ten} 20 {twenty} other {other}} -
|
||||
{count, select, 1 {one} 2 {two} other {more than two}}
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('vingt - deux');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-906: Multiple ICUs wrapped in HTML tags in one i18n block throw an error')
|
||||
.it('should handle multiple ICUs in one i18n block wrapped in HTML elements', () => {
|
||||
const template = `
|
||||
<div i18n>
|
||||
<span>
|
||||
{age, select, 10 {ten} 20 {twenty} other {other}}
|
||||
</span>
|
||||
<span>
|
||||
{count, select, 1 {one} 2 {two} other {more than two}}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
const spans = element.getElementsByTagName('span');
|
||||
expect(spans.length).toBe(2);
|
||||
expect(spans[0].innerHTML).toBe('vingt');
|
||||
expect(spans[1].innerHTML).toBe('deux');
|
||||
});
|
||||
|
||||
it('should handle ICUs inside a template in i18n block', () => {
|
||||
const template = `
|
||||
<div i18n>
|
||||
<span *ngIf="visible">
|
||||
{age, select, 10 {ten} 20 {twenty} other {other}}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
const spans = element.getElementsByTagName('span');
|
||||
expect(spans.length).toBe(1);
|
||||
expect(spans[0]).toHaveText('vingt');
|
||||
});
|
||||
|
||||
it('should handle nested icus', () => {
|
||||
const template = `
|
||||
<div i18n>
|
||||
{age, select,
|
||||
10 {ten - {count, select, 1 {one} 2 {two} other {more than two}}}
|
||||
20 {twenty - {count, select, 1 {one} 2 {two} other {more than two}}}
|
||||
other {other}}
|
||||
</div>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement.firstChild;
|
||||
expect(element).toHaveText('vingt - deux');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-908: ICUs inside <ng-container>s throw an error at runtime')
|
||||
.it('should handle ICUs inside <ng-container>', () => {
|
||||
const template = `
|
||||
<ng-container i18n>
|
||||
{age, select, 10 {ten} 20 {twenty} other {other}}
|
||||
</ng-container>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element.innerHTML).toBe('vingt');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-909: ICUs inside <ng-template>s throw errors at runtime')
|
||||
.it('should handle ICUs inside <ng-template>', () => {
|
||||
const template = `
|
||||
<ng-template i18n tplRef>
|
||||
{age, select, 10 {ten} 20 {twenty} other {other}}
|
||||
</ng-template>
|
||||
`;
|
||||
const fixture = getFixtureWithOverrides({template});
|
||||
|
||||
const element = fixture.nativeElement;
|
||||
expect(element.innerHTML).toBe('vingt');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -16,13 +16,12 @@
|
|||
export function polyfillGoogGetMsg(translations: {[key: string]: string} = {}): void {
|
||||
const glob = (global as any);
|
||||
glob.goog = glob.goog || {};
|
||||
glob.goog.getMsg =
|
||||
glob.goog.getMsg || function(input: string, placeholders: {[key: string]: string} = {}) {
|
||||
if (typeof translations[input] !== 'undefined') { // to account for empty string
|
||||
input = translations[input];
|
||||
}
|
||||
return Object.keys(placeholders).length ?
|
||||
input.replace(/\{\$(.*?)\}/g, (match, key) => placeholders[key] || '') :
|
||||
input;
|
||||
};
|
||||
}
|
||||
glob.goog.getMsg = function(input: string, placeholders: {[key: string]: string} = {}) {
|
||||
if (typeof translations[input] !== 'undefined') { // to account for empty string
|
||||
input = translations[input];
|
||||
}
|
||||
return Object.keys(placeholders).length ?
|
||||
input.replace(/\{\$(.*?)\}/g, (match, key) => placeholders[key] || '') :
|
||||
input;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue