refactor(ivy): i18n - share `MessageSerializer` across `TranslationParsers` (#33444)
Each of the XML based `TranslationParsers` was providing its own `MessageSerializer`, but they are all very similar. So these have been consolidated into a single more generic `MessageSerializer. As a result of this, the extra layers of folders in the project seemed unnecessary, so they have been flattened. PR Close #33444
This commit is contained in:
parent
e483acaa17
commit
c2f13a1e3a
|
@ -15,9 +15,9 @@ import {getOutputPathFn, OutputPathFn} from './output_path';
|
||||||
import {SourceFileTranslationHandler} from './source_files/source_file_translation_handler';
|
import {SourceFileTranslationHandler} from './source_files/source_file_translation_handler';
|
||||||
import {MissingTranslationStrategy} from './source_files/source_file_utils';
|
import {MissingTranslationStrategy} from './source_files/source_file_utils';
|
||||||
import {TranslationLoader} from './translation_files/translation_loader';
|
import {TranslationLoader} from './translation_files/translation_loader';
|
||||||
import {SimpleJsonTranslationParser} from './translation_files/translation_parsers/simple_json/simple_json_translation_parser';
|
import {SimpleJsonTranslationParser} from './translation_files/translation_parsers/simple_json_translation_parser';
|
||||||
import {Xliff1TranslationParser} from './translation_files/translation_parsers/xliff1/xliff1_translation_parser';
|
import {Xliff1TranslationParser} from './translation_files/translation_parsers/xliff1_translation_parser';
|
||||||
import {Xliff2TranslationParser} from './translation_files/translation_parsers/xliff2/xliff2_translation_parser';
|
import {Xliff2TranslationParser} from './translation_files/translation_parsers/xliff2_translation_parser';
|
||||||
import {Translator} from './translator';
|
import {Translator} from './translator';
|
||||||
import {Diagnostics} from '../diagnostics';
|
import {Diagnostics} from '../diagnostics';
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,28 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {Element, Expansion, ExpansionCase, Node, Text, visitAll} from '@angular/compiler';
|
import {Element, Expansion, ExpansionCase, Node, Text, visitAll} from '@angular/compiler';
|
||||||
import {MessageRenderer} from '../../../message_renderers/message_renderer';
|
|
||||||
import {BaseVisitor} from '../base_visitor';
|
import {BaseVisitor} from '../base_visitor';
|
||||||
import {TranslationParseError} from '../translation_parse_error';
|
import {TranslationParseError} from '../translation_parsers/translation_parse_error';
|
||||||
import {getAttrOrThrow, getAttribute} from '../translation_utils';
|
import {getAttrOrThrow, getAttribute} from '../translation_parsers/translation_utils';
|
||||||
|
|
||||||
const INLINE_ELEMENTS = ['cp', 'sc', 'ec', 'mrk', 'sm', 'em'];
|
import {MessageRenderer} from './message_renderer';
|
||||||
|
|
||||||
export class Xliff2MessageSerializer<T> extends BaseVisitor {
|
interface MessageSerializerConfig {
|
||||||
constructor(private renderer: MessageRenderer<T>) { super(); }
|
inlineElements: string[];
|
||||||
|
placeholder?: {elementName: string; nameAttribute: string; bodyAttribute?: string;};
|
||||||
|
placeholderContainer?: {elementName: string; startAttribute: string; endAttribute: string;};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This visitor will walk over a set of XML nodes, which represent an i18n message, and serialize
|
||||||
|
* them into a message object of type `T`.
|
||||||
|
* The type of the serialized message is controlled by the
|
||||||
|
*/
|
||||||
|
export class MessageSerializer<T> extends BaseVisitor {
|
||||||
|
constructor(private renderer: MessageRenderer<T>, private config: MessageSerializerConfig) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
serialize(nodes: Node[]): T {
|
serialize(nodes: Node[]): T {
|
||||||
this.renderer.startRender();
|
this.renderer.startRender();
|
||||||
|
@ -24,13 +37,18 @@ export class Xliff2MessageSerializer<T> extends BaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
visitElement(element: Element): void {
|
visitElement(element: Element): void {
|
||||||
if (element.name === 'ph') {
|
if (this.config.placeholder && element.name === this.config.placeholder.elementName) {
|
||||||
this.visitPlaceholder(getAttrOrThrow(element, 'equiv'), getAttribute(element, 'disp'));
|
const name = getAttrOrThrow(element, this.config.placeholder.nameAttribute);
|
||||||
} else if (element.name === 'pc') {
|
const body = this.config.placeholder.bodyAttribute &&
|
||||||
this.visitPlaceholderContainer(
|
getAttribute(element, this.config.placeholder.bodyAttribute);
|
||||||
getAttrOrThrow(element, 'equivStart'), element.children,
|
this.visitPlaceholder(name, body);
|
||||||
getAttrOrThrow(element, 'equivEnd'));
|
} else if (
|
||||||
} else if (INLINE_ELEMENTS.indexOf(element.name) !== -1) {
|
this.config.placeholderContainer &&
|
||||||
|
element.name === this.config.placeholderContainer.elementName) {
|
||||||
|
const start = getAttrOrThrow(element, this.config.placeholderContainer.startAttribute);
|
||||||
|
const end = getAttrOrThrow(element, this.config.placeholderContainer.endAttribute);
|
||||||
|
this.visitPlaceholderContainer(start, element.children, end);
|
||||||
|
} else if (this.config.inlineElements.indexOf(element.name) !== -1) {
|
||||||
visitAll(this, element.children);
|
visitAll(this, element.children);
|
||||||
} else {
|
} else {
|
||||||
throw new TranslationParseError(element.sourceSpan, `Invalid element found in message.`);
|
throw new TranslationParseError(element.sourceSpan, `Invalid element found in message.`);
|
||||||
|
@ -58,11 +76,11 @@ export class Xliff2MessageSerializer<T> extends BaseVisitor {
|
||||||
const length = nodes.length;
|
const length = nodes.length;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
while (index < length) {
|
while (index < length) {
|
||||||
if (!isPlaceholderContainer(nodes[index])) {
|
if (!this.isPlaceholderContainer(nodes[index])) {
|
||||||
const startOfContainedNodes = index;
|
const startOfContainedNodes = index;
|
||||||
while (index < length - 1) {
|
while (index < length - 1) {
|
||||||
index++;
|
index++;
|
||||||
if (isPlaceholderContainer(nodes[index])) {
|
if (this.isPlaceholderContainer(nodes[index])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,8 +107,8 @@ export class Xliff2MessageSerializer<T> extends BaseVisitor {
|
||||||
this.visitContainedNodes(children);
|
this.visitContainedNodes(children);
|
||||||
this.renderer.closePlaceholder(closeName);
|
this.renderer.closePlaceholder(closeName);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function isPlaceholderContainer(node: Node): boolean {
|
private isPlaceholderContainer(node: Node): boolean {
|
||||||
return node instanceof Element && node.name === 'pc';
|
return node instanceof Element && node.name === this.config.placeholderContainer !.elementName;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
import {ɵMessageId, ɵParsedTranslation, ɵparseTranslation} from '@angular/localize';
|
import {ɵMessageId, ɵParsedTranslation, ɵparseTranslation} from '@angular/localize';
|
||||||
import {extname} from 'path';
|
import {extname} from 'path';
|
||||||
import {ParsedTranslationBundle, TranslationParser} from '../translation_parser';
|
import {ParsedTranslationBundle, TranslationParser} from './translation_parser';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A translation parser that can parse JSON that has the form:
|
* A translation parser that can parse JSON that has the form:
|
|
@ -1,56 +0,0 @@
|
||||||
/**
|
|
||||||
* @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 {Element, Expansion, ExpansionCase, Node, Text, visitAll} from '@angular/compiler';
|
|
||||||
import {MessageRenderer} from '../../../message_renderers/message_renderer';
|
|
||||||
import {BaseVisitor} from '../base_visitor';
|
|
||||||
import {TranslationParseError} from '../translation_parse_error';
|
|
||||||
import {getAttrOrThrow} from '../translation_utils';
|
|
||||||
|
|
||||||
const INLINE_ELEMENTS = ['g', 'bx', 'ex', 'bpt', 'ept', 'ph', 'it', 'mrk'];
|
|
||||||
|
|
||||||
export class Xliff1MessageSerializer<T> extends BaseVisitor {
|
|
||||||
constructor(private renderer: MessageRenderer<T>) { super(); }
|
|
||||||
|
|
||||||
serialize(nodes: Node[]): T {
|
|
||||||
this.renderer.startRender();
|
|
||||||
visitAll(this, nodes);
|
|
||||||
this.renderer.endRender();
|
|
||||||
return this.renderer.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitElement(element: Element): void {
|
|
||||||
if (element.name === 'x') {
|
|
||||||
this.visitPlaceholder(getAttrOrThrow(element, 'id'), '');
|
|
||||||
} else if (INLINE_ELEMENTS.indexOf(element.name) !== -1) {
|
|
||||||
visitAll(this, element.children);
|
|
||||||
} else {
|
|
||||||
throw new TranslationParseError(element.sourceSpan, `Invalid element found in message.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitText(text: Text): void { this.renderer.text(text.value); }
|
|
||||||
|
|
||||||
visitExpansion(expansion: Expansion): void {
|
|
||||||
this.renderer.startIcu();
|
|
||||||
this.renderer.text(`${expansion.switchValue}, ${expansion.type},`);
|
|
||||||
visitAll(this, expansion.cases);
|
|
||||||
this.renderer.endIcu();
|
|
||||||
}
|
|
||||||
|
|
||||||
visitExpansionCase(expansionCase: ExpansionCase): void {
|
|
||||||
this.renderer.text(` ${expansionCase.value} {`);
|
|
||||||
this.renderer.startContainer();
|
|
||||||
visitAll(this, expansionCase.expression);
|
|
||||||
this.renderer.closeContainer();
|
|
||||||
this.renderer.text(`}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPlaceholder(name: string, body: string|undefined): void {
|
|
||||||
this.renderer.placeholder(name, body);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,12 +8,14 @@
|
||||||
import {Element, Node, XmlParser, visitAll} from '@angular/compiler';
|
import {Element, Node, XmlParser, visitAll} from '@angular/compiler';
|
||||||
import {ɵMessageId, ɵParsedTranslation} from '@angular/localize';
|
import {ɵMessageId, ɵParsedTranslation} from '@angular/localize';
|
||||||
import {extname} from 'path';
|
import {extname} from 'path';
|
||||||
import {TargetMessageRenderer} from '../../../message_renderers/target_message_renderer';
|
|
||||||
import {BaseVisitor} from '../base_visitor';
|
import {BaseVisitor} from '../base_visitor';
|
||||||
import {TranslationParseError} from '../translation_parse_error';
|
import {MessageSerializer} from '../message_serialization/message_serializer';
|
||||||
import {ParsedTranslationBundle, TranslationParser} from '../translation_parser';
|
import {TargetMessageRenderer} from '../message_serialization/target_message_renderer';
|
||||||
import {getAttrOrThrow, getAttribute, parseInnerRange} from '../translation_utils';
|
|
||||||
import {Xliff1MessageSerializer} from './xliff1_message_serializer';
|
import {TranslationParseError} from './translation_parse_error';
|
||||||
|
import {ParsedTranslationBundle, TranslationParser} from './translation_parser';
|
||||||
|
import {getAttrOrThrow, getAttribute, parseInnerRange} from './translation_utils';
|
||||||
|
|
||||||
const XLIFF_1_2_NS_REGEX = /xmlns="urn:oasis:names:tc:xliff:document:1.2"/;
|
const XLIFF_1_2_NS_REGEX = /xmlns="urn:oasis:names:tc:xliff:document:1.2"/;
|
||||||
|
|
||||||
|
@ -90,7 +92,10 @@ class XliffTranslationVisitor extends BaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeTargetMessage(source: Element): ɵParsedTranslation {
|
function serializeTargetMessage(source: Element): ɵParsedTranslation {
|
||||||
const serializer = new Xliff1MessageSerializer(new TargetMessageRenderer());
|
const serializer = new MessageSerializer(new TargetMessageRenderer(), {
|
||||||
|
inlineElements: ['g', 'bx', 'ex', 'bpt', 'ept', 'ph', 'it', 'mrk'],
|
||||||
|
placeholder: {elementName: 'x', nameAttribute: 'id'}
|
||||||
|
});
|
||||||
return serializer.serialize(parseInnerRange(source));
|
return serializer.serialize(parseInnerRange(source));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
import {Element, Node, XmlParser, visitAll} from '@angular/compiler';
|
import {Element, Node, XmlParser, visitAll} from '@angular/compiler';
|
||||||
import {ɵMessageId, ɵParsedTranslation} from '@angular/localize';
|
import {ɵMessageId, ɵParsedTranslation} from '@angular/localize';
|
||||||
import {extname} from 'path';
|
import {extname} from 'path';
|
||||||
import {TargetMessageRenderer} from '../../../message_renderers/target_message_renderer';
|
|
||||||
import {BaseVisitor} from '../base_visitor';
|
import {BaseVisitor} from '../base_visitor';
|
||||||
import {TranslationParseError} from '../translation_parse_error';
|
import {MessageSerializer} from '../message_serialization/message_serializer';
|
||||||
import {ParsedTranslationBundle, TranslationParser} from '../translation_parser';
|
import {TargetMessageRenderer} from '../message_serialization/target_message_renderer';
|
||||||
import {getAttrOrThrow, getAttribute, parseInnerRange} from '../translation_utils';
|
|
||||||
import {Xliff2MessageSerializer} from './xliff2_message_serializer';
|
import {TranslationParseError} from './translation_parse_error';
|
||||||
|
import {ParsedTranslationBundle, TranslationParser} from './translation_parser';
|
||||||
|
import {getAttrOrThrow, getAttribute, parseInnerRange} from './translation_utils';
|
||||||
|
|
||||||
const XLIFF_2_0_NS_REGEX = /xmlns="urn:oasis:names:tc:xliff:document:2.0"/;
|
const XLIFF_2_0_NS_REGEX = /xmlns="urn:oasis:names:tc:xliff:document:2.0"/;
|
||||||
|
|
||||||
|
@ -105,7 +107,12 @@ function assertTranslationUnit(segment: Element, context: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeTargetMessage(source: Element): ɵParsedTranslation {
|
function serializeTargetMessage(source: Element): ɵParsedTranslation {
|
||||||
const serializer = new Xliff2MessageSerializer(new TargetMessageRenderer());
|
const serializer = new MessageSerializer(new TargetMessageRenderer(), {
|
||||||
|
inlineElements: ['cp', 'sc', 'ec', 'mrk', 'sm', 'em'],
|
||||||
|
placeholder: {elementName: 'ph', nameAttribute: 'equiv', bodyAttribute: 'disp'},
|
||||||
|
placeholderContainer:
|
||||||
|
{elementName: 'pc', startAttribute: 'equivStart', endAttribute: 'equivEnd'}
|
||||||
|
});
|
||||||
return serializer.serialize(parseInnerRange(source));
|
return serializer.serialize(parseInnerRange(source));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵmakeTemplateObject} from '@angular/localize';
|
import {ɵmakeTemplateObject} from '@angular/localize';
|
||||||
import {SimpleJsonTranslationParser} from '../../../../../src/translate/translation_files/translation_parsers/simple_json/simple_json_translation_parser';
|
import {SimpleJsonTranslationParser} from '../../../../src/translate/translation_files/translation_parsers/simple_json_translation_parser';
|
||||||
|
|
||||||
describe('SimpleJsonTranslationParser', () => {
|
describe('SimpleJsonTranslationParser', () => {
|
||||||
describe('canParse()', () => {
|
describe('canParse()', () => {
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵcomputeMsgId, ɵmakeParsedTranslation} from '@angular/localize';
|
import {ɵcomputeMsgId, ɵmakeParsedTranslation} from '@angular/localize';
|
||||||
import {Xliff1TranslationParser} from '../../../../../src/translate/translation_files/translation_parsers/xliff1/xliff1_translation_parser';
|
import {Xliff1TranslationParser} from '../../../../src/translate/translation_files/translation_parsers/xliff1_translation_parser';
|
||||||
|
|
||||||
describe('Xliff1TranslationParser', () => {
|
describe('Xliff1TranslationParser', () => {
|
||||||
describe('canParse()', () => {
|
describe('canParse()', () => {
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵcomputeMsgId, ɵmakeParsedTranslation} from '@angular/localize';
|
import {ɵcomputeMsgId, ɵmakeParsedTranslation} from '@angular/localize';
|
||||||
import {Xliff2TranslationParser} from '../../../../../src/translate/translation_files/translation_parsers/xliff2/xliff2_translation_parser';
|
import {Xliff2TranslationParser} from '../../../../src/translate/translation_files/translation_parsers/xliff2_translation_parser';
|
||||||
|
|
||||||
describe('Xliff2TranslationParser', () => {
|
describe('Xliff2TranslationParser', () => {
|
||||||
describe('canParse()', () => {
|
describe('canParse()', () => {
|
Loading…
Reference in New Issue