fix(ivy): template compiler should render correct $localize placeholder names (#32509)

The `goog.getMsg()` function requires placeholder names to be camelCased.

This is not the case for `$localize`. Here placeholder names need
match what is serialized to translation files.

Specifically such placeholder names keep their casing but have all characters
that are not in `a-z`, `A-Z`, `0-9` and `_` converted to `_`.

PR Close #32509
This commit is contained in:
Pete Bacon Darwin
2019-09-06 12:26:48 +01:00
committed by Matias Niemelä
parent 9166baf709
commit ea6a2e9f25
7 changed files with 1344 additions and 1279 deletions

View File

@ -9,7 +9,6 @@ import * as i18n from '../../../i18n/i18n_ast';
import * as o from '../../../output/output_ast';
import {serializeIcuNode} from './icu_serializer';
import {i18nMetaToDocStmt, metaFromI18nMessage} from './meta';
import {formatI18nPlaceholderName} from './util';
export function createLocalizeStatements(
@ -37,7 +36,7 @@ class MessagePiece {
}
class LiteralPiece extends MessagePiece {}
class PlaceholderPiece extends MessagePiece {
constructor(name: string) { super(formatI18nPlaceholderName(name)); }
constructor(name: string) { super(formatI18nPlaceholderName(name, /* useCamelCase */ false)); }
}
/**

View File

@ -2021,13 +2021,15 @@ export function getTranslationDeclStmts(
message: i18n.Message, variable: o.ReadVarExpr, closureVar: o.ReadVarExpr,
params: {[name: string]: o.Expression} = {},
transformFn?: (raw: o.ReadVarExpr) => o.Expression): o.Statement[] {
const formattedParams = i18nFormatPlaceholderNames(params, /* useCamelCase */ true);
const statements: o.Statement[] = [
declareI18nVariable(variable),
o.ifStmt(
o.variable(NG_I18N_CLOSURE_MODE),
createGoogleGetMsgStatements(variable, message, closureVar, formattedParams),
createLocalizeStatements(variable, message, formattedParams)),
createGoogleGetMsgStatements(
variable, message, closureVar,
i18nFormatPlaceholderNames(params, /* useCamelCase */ true)),
createLocalizeStatements(
variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
];
if (transformFn) {

View File

@ -232,6 +232,11 @@ describe('serializeI18nMessageForGetMsg', () => {
.toEqual('Some text {$interpolation} and {$interpolation_1}');
});
it('should serialize interpolation with named placeholder for `GetMsg()`', () => {
expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}'))
.toEqual('{$placeholderName}');
});
it('should serialize content with HTML tags for `GetMsg()`', () => {
expect(serialize('A <span>B<div>C</div></span> D'))
.toEqual('A {$startTagSpan}B{$startTagDiv}C{$closeTagDiv}{$closeTagSpan} D');
@ -277,14 +282,14 @@ describe('serializeI18nMessageForLocalize', () => {
it('should serialize text with interpolation for `$localize()`', () => {
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({
messageParts: ['Some text ', ' and ', ' done'],
placeHolders: ['interpolation', 'interpolation_1']
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
});
});
it('should serialize text with interpolation at start for `$localize()`', () => {
expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({
messageParts: ['', ' and ', ' done'],
placeHolders: ['interpolation', 'interpolation_1']
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
});
});
@ -292,21 +297,27 @@ describe('serializeI18nMessageForLocalize', () => {
it('should serialize text with interpolation at end for `$localize()`', () => {
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({
messageParts: ['Some text ', ' and ', ''],
placeHolders: ['interpolation', 'interpolation_1']
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
});
});
it('should serialize only interpolation for `$localize()`', () => {
expect(serialize('{{ valueB + valueC }}'))
.toEqual({messageParts: ['', ''], placeHolders: ['interpolation']});
.toEqual({messageParts: ['', ''], placeHolders: ['INTERPOLATION']});
});
it('should serialize interpolation with named placeholder for `$localize()`', () => {
expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}'))
.toEqual({messageParts: ['', ''], placeHolders: ['PLACEHOLDER_NAME']});
});
it('should serialize content with HTML tags for `$localize()`', () => {
expect(serialize('A <span>B<div>C</div></span> D')).toEqual({
messageParts: ['A ', 'B', 'C', '', ' D'],
placeHolders: ['startTagSpan', 'startTagDiv', 'closeTagDiv', 'closeTagSpan']
placeHolders: ['START_TAG_SPAN', 'START_TAG_DIV', 'CLOSE_TAG_DIV', 'CLOSE_TAG_SPAN']
});
});
@ -346,7 +357,7 @@ describe('serializeI18nMessageForLocalize', () => {
'{gender, select, male {male} female {female} other {other}}<div>{gender, select, male {male} female {female} other {other}}</div>'))
.toEqual({
messageParts: ['', '', '', '', ''],
placeHolders: ['icu', 'startTagDiv', 'icu', 'closeTagDiv']
placeHolders: ['ICU', 'START_TAG_DIV', 'ICU', 'CLOSE_TAG_DIV']
});
});
});