Sitelet https://github.com/angular/angular/pull/39459/files
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ export class Xliff1TranslationSerializer implements TranslationSerializer {

private serializePlaceholder(xml: XmlFile, id: string, text: string|undefined): void {
const attrs: Record<string, string> = {id};
const ctype = getCtypeForPlaceholder(id);
if (ctype !== null) {
attrs.ctype = ctype;
}
if (text !== undefined) {
attrs['equiv-text'] = text;
}
Expand Down Expand Up @@ -148,3 +152,68 @@ export class Xliff1TranslationSerializer implements TranslationSerializer {
message.id;
}
}

/**
* Compute the value of the `ctype` attribute from the `placeholder` name.
*
* The placeholder can take the following forms:
*
* - `START_BOLD_TEXT`/`END_BOLD_TEXT`
* - `TAG_<ELEMENT_NAME>`
* - `START_TAG_<ELEMENT_NAME>`
* - `CLOSE_TAG_<ELEMENT_NAME>`
*
* In these cases the element name of the tag is extracted from the placeholder name and returned as
* `x-<element_name>`.
*
* Line breaks and images are special cases.
*/
function getCtypeForPlaceholder(placeholder: string): string|null {
const tag = placeholder.replace(/^(START_|CLOSE_)/, '');
switch (tag) {
case 'LINE_BREAK':
return 'lb';
case 'TAG_IMG':
return 'image';
default:
const element = tag.startsWith('TAG_') ?
tag.replace(/^TAG_(.+)/, (_, tagName: string) => tagName.toLowerCase()) :
TAG_MAP[tag];
if (element === undefined) {
return null;
}
return `x-${element}`;
}
}

const TAG_MAP: Record<string, string> = {
'LINK': 'a',
'BOLD_TEXT': 'b',
'EMPHASISED_TEXT': 'em',
'HEADING_LEVEL1': 'h1',
'HEADING_LEVEL2': 'h2',
'HEADING_LEVEL3': 'h3',
'HEADING_LEVEL4': 'h4',
'HEADING_LEVEL5': 'h5',
'HEADING_LEVEL6': 'h6',
'HORIZONTAL_RULE': 'hr',
'ITALIC_TEXT': 'i',
'LIST_ITEM': 'li',
'MEDIA_LINK': 'link',
'ORDERED_LIST': 'ol',
'PARAGRAPH': 'p',
'QUOTATION': 'q',
'STRIKETHROUGH_TEXT': 's',
'SMALL_TEXT': 'small',
'SUBSTRIPT': 'sub',
'SUPERSCRIPT': 'sup',
'TABLE_BODY': 'tbody',
'TABLE_CELL': 'td',
'TABLE_FOOTER': 'tfoot',
'TABLE_HEADER_CELL': 'th',
'TABLE_HEADER': 'thead',
'TABLE_ROW': 'tr',
'MONOSPACED_TEXT': 'tt',
'UNDERLINED_TEXT': 'u',
'UNORDERED_LIST': 'ul',
};
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ export class Xliff2TranslationSerializer implements TranslationSerializer {
equivStart: placeholderName,
equivEnd: closingPlaceholderName,
};
const type = getTypeForPlaceholder(placeholderName);
if (type !== null) {
attrs.type = type;
}
if (text !== undefined) {
attrs.dispStart = text;
}
Expand All @@ -129,8 +133,14 @@ export class Xliff2TranslationSerializer implements TranslationSerializer {
} else if (placeholderName.startsWith('CLOSE_')) {
xml.endTag('pc');
} else {
const attrs:
Record<string, string> = {id: `${this.currentPlaceholderId++}`, equiv: placeholderName};
const attrs: Record<string, string> = {
id: `${this.currentPlaceholderId++}`,
equiv: placeholderName,
};
const type = getTypeForPlaceholder(placeholderName);
if (type !== null) {
attrs.type = type;
}
if (text !== undefined) {
attrs.disp = text;
}
Expand Down Expand Up @@ -166,3 +176,29 @@ export class Xliff2TranslationSerializer implements TranslationSerializer {
message.id;
}
}

/**
* Compute the value of the `type` attribute from the `placeholder` name.
*
* If the tag is not known but starts with `TAG_`, `START_TAG_` or `CLOSE_TAG_` then the type is
* `other`. Certain formatting tags (e.g. bold, italic, etc) have type `fmt`. Line-breaks, images
* and links are special cases.
*/
function getTypeForPlaceholder(placeholder: string): string|null {
const tag = placeholder.replace(/^(START_|CLOSE_)/, '');
switch (tag) {
case 'BOLD_TEXT':
case 'EMPHASISED_TEXT':
case 'ITALIC_TEXT':
case 'LINE_BREAK':
case 'STRIKETHROUGH_TEXT':
case 'UNDERLINED_TEXT':
return 'fmt';
case 'TAG_IMG':
return 'image';
case 'LINK':
return 'link';
default:
return /^(START_|CLOSE_)/.test(placeholder) ? 'other' : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ runInEachFileSystem(() => {
` </context-group>`,
` </trans-unit>`,
` <trans-unit id="2932901491976224757" datatype="html">`,
` <source>pre<x id="START_TAG_SPAN" equiv-text="&apos;&lt;span&gt;&apos;"/>` +
`inner-pre<x id="START_BOLD_TEXT" equiv-text="&apos;&lt;b&gt;&apos;"/>bold<x id="CLOSE_BOLD_TEXT" equiv-text="&apos;&lt;/b&gt;&apos;"/>` +
`inner-post<x id="CLOSE_TAG_SPAN" equiv-text="&apos;&lt;/span&gt;&apos;"/>post</source>`,
` <source>pre<x id="START_TAG_SPAN" ctype="x-span" equiv-text="&apos;&lt;span&gt;&apos;"/>` +
`inner-pre<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="&apos;&lt;b&gt;&apos;"/>bold<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="&apos;&lt;/b&gt;&apos;"/>` +
`inner-post<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&apos;&lt;/span&gt;&apos;"/>post</source>`,
` <context-group purpose="location">`,
` <context context-type="sourcefile">test_files/test.js</context>`,
` <context context-type="linenumber">9,10</context>`,
Expand Down Expand Up @@ -279,8 +279,8 @@ runInEachFileSystem(() => {
` <note category="location">test_files/test.js:9,10</note>`,
` </notes>`,
` <segment>`,
` <source>pre<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" dispStart="&apos;&lt;span&gt;&apos;" dispEnd="&apos;&lt;/span&gt;&apos;">` +
`inner-pre<pc id="1" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" dispStart="&apos;&lt;b&gt;&apos;" dispEnd="&apos;&lt;/b&gt;&apos;">bold</pc>` +
` <source>pre<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other" dispStart="&apos;&lt;span&gt;&apos;" dispEnd="&apos;&lt;/span&gt;&apos;">` +
`inner-pre<pc id="1" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&apos;&lt;b&gt;&apos;" dispEnd="&apos;&lt;/b&gt;&apos;">bold</pc>` +
`inner-post</pc>post</source>`,
` </segment>`,
` </unit>`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,18 @@ runInEachFileSystem(() => {
` <source>a<x id="PH" equiv-text="placeholder + 1"/>b<x id="PH_1"/>c</source>`,
` </trans-unit>`,
` <trans-unit id="67890" datatype="html">`,
` <source>a<x id="START_TAG_SPAN"/><x id="CLOSE_TAG_SPAN"/>c</source>`,
` <source>a<x id="START_TAG_SPAN" ctype="x-span"/><x id="CLOSE_TAG_SPAN" ctype="x-span"/>c</source>`,
` <note priority="1" from="description">some description</note>`,
` </trans-unit>`,
` <trans-unit id="38705" datatype="html">`,
` <source>a<x id="START_TAG_SPAN"/><x id="CLOSE_TAG_SPAN"/>c</source>`,
` <source>a<x id="START_TAG_SPAN" ctype="x-span"/><x id="CLOSE_TAG_SPAN" ctype="x-span"/>c</source>`,
` <context-group purpose="location">`,
` <context context-type="sourcefile">file.ts</context>`,
` <context context-type="linenumber">3,4</context>`,
` </context-group>`,
` </trans-unit>`,
` <trans-unit id="13579" datatype="html">`,
` <source><x id="START_BOLD_TEXT"/>b<x id="CLOSE_BOLD_TEXT"/></source>`,
` <source><x id="START_BOLD_TEXT" ctype="x-b"/>b<x id="CLOSE_BOLD_TEXT" ctype="x-b"/></source>`,
` </trans-unit>`,
` <trans-unit id="24680" datatype="html">`,
` <source>a</source>`,
Expand All @@ -128,7 +128,7 @@ runInEachFileSystem(() => {
` <source>pre-ICU {VAR_SELECT, select, a {a} b {<x id="INTERPOLATION"/>} c {pre <x id="INTERPOLATION_1"/> post}} post-ICU</source>`,
` </trans-unit>`,
` <trans-unit id="100001" datatype="html">`,
` <source>{VAR_PLURAL, plural, one {<x id="START_BOLD_TEXT"/>something bold<x id="CLOSE_BOLD_TEXT"/>} other {pre <x id="START_TAG_SPAN"/>middle<x id="CLOSE_TAG_SPAN"/> post}}</source>`,
` <source>{VAR_PLURAL, plural, one {<x id="START_BOLD_TEXT" ctype="x-b"/>something bold<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} other {pre <x id="START_TAG_SPAN" ctype="x-span"/>middle<x id="CLOSE_TAG_SPAN" ctype="x-span"/> post}}</source>`,
` </trans-unit>`,
` </body>`,
` </file>`,
Expand Down Expand Up @@ -230,6 +230,64 @@ runInEachFileSystem(() => {
`</xliff>\n`,
].join('\n'));
});

it('should render the "ctype" for line breaks', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage('1', ['a', 'b'], ['LINE_BREAK'], {})]);
expect(output).toContain(
'<source>a<x id="LINE_BREAK" ctype="lb"/>b</source>',
);
});

it('should render the "ctype" for images', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage('2', ['a', 'b'], ['TAG_IMG'], {})]);
expect(output).toContain(
'<source>a<x id="TAG_IMG" ctype="image"/>b</source>',
);
});

it('should render the "ctype" for bold elements', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('3', ['a', 'b', 'c'], ['START_BOLD_TEXT', 'CLOSE_BOLD_TEXT'], {})]);
expect(output).toContain(
'<source>a<x id="START_BOLD_TEXT" ctype="x-b"/>b<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>c</source>',
);
});

it('should render the "ctype" for headings', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage(
'4', ['a', 'b', 'c'], ['START_HEADING_LEVEL1', 'CLOSE_HEADING_LEVEL1'], {})]);
expect(output).toContain(
'<source>a<x id="START_HEADING_LEVEL1" ctype="x-h1"/>b<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>c</source>',
);
});

it('should render the "ctype" for span elements', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('5', ['a', 'b', 'c'], ['START_TAG_SPAN', 'CLOSE_TAG_SPAN'], {})]);
expect(output).toContain(
'<source>a<x id="START_TAG_SPAN" ctype="x-span"/>b<x id="CLOSE_TAG_SPAN" ctype="x-span"/>c</source>',
);
});

it('should render the "ctype" for div elements', () => {
const serializer = new Xliff1TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('6', ['a', 'b', 'c'], ['START_TAG_DIV', 'CLOSE_TAG_DIV'], {})]);
expect(output).toContain(
'<source>a<x id="START_TAG_DIV" ctype="x-div"/>b<x id="CLOSE_TAG_DIV" ctype="x-div"/>c</source>',
);
});
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,20 @@ runInEachFileSystem(() => {
` <note category="description">some description</note>`,
` </notes>`,
` <segment>`,
` <source>a<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN"></pc>c</source>`,
` <source>a<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other"></pc>c</source>`,
` </segment>`,
` </unit>`,
` <unit id="location-only">`,
` <notes>`,
` <note category="location">file.ts:3,4</note>`,
` </notes>`,
` <segment>`,
` <source>a<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN"></pc>c</source>`,
` <source>a<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other"></pc>c</source>`,
` </segment>`,
` </unit>`,
` <unit id="13579">`,
` <segment>`,
` <source><pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT">b</pc></source>`,
` <source><pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt">b</pc></source>`,
` </segment>`,
` </unit>`,
` <unit id="24680">`,
Expand Down Expand Up @@ -151,7 +151,7 @@ runInEachFileSystem(() => {
` </unit>`,
` <unit id="100001">`,
` <segment>`,
` <source>{VAR_PLURAL, plural, one {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT">something bold</pc>} other {pre <pc id="1" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN">middle</pc> post}}</source>`,
` <source>{VAR_PLURAL, plural, one {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt">something bold</pc>} other {pre <pc id="1" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other">middle</pc> post}}</source>`,
` </segment>`,
` </unit>`,
` </file>`,
Expand Down Expand Up @@ -243,6 +243,64 @@ runInEachFileSystem(() => {
`</xliff>\n`,
].join('\n'));
});

it('should render the "type" for line breaks', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage('1', ['a', 'b'], ['LINE_BREAK'], {})]);
expect(output).toContain(
'<source>a<ph id="0" equiv="LINE_BREAK" type="fmt"/>b</source>',
);
});

it('should render the "type" for images', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage('2', ['a', 'b'], ['TAG_IMG'], {})]);
expect(output).toContain(
'<source>a<ph id="0" equiv="TAG_IMG" type="image"/>b</source>',
);
});

it('should render the "type" for bold elements', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('3', ['a', 'b', 'c'], ['START_BOLD_TEXT', 'CLOSE_BOLD_TEXT'], {})]);
expect(output).toContain(
'<source>a<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt">b</pc>c</source>',
);
});

it('should render the "type" for heading elements', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize([mockMessage(
'4', ['a', 'b', 'c'], ['START_HEADING_LEVEL1', 'CLOSE_HEADING_LEVEL1'], {})]);
expect(output).toContain(
'<source>a<pc id="0" equivStart="START_HEADING_LEVEL1" equivEnd="CLOSE_HEADING_LEVEL1" type="other">b</pc>c</source>',
);
});

it('should render the "type" for span elements', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('5', ['a', 'b', 'c'], ['START_TAG_SPAN', 'CLOSE_TAG_SPAN'], {})]);
expect(output).toContain(
'<source>a<pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other">b</pc>c</source>',
);
});

it('should render the "type" for div elements', () => {
const serializer = new Xliff2TranslationSerializer(
'xx', absoluteFrom('/project'), useLegacyIds, options);
const output = serializer.serialize(
[mockMessage('6', ['a', 'b', 'c'], ['START_TAG_DIV', 'CLOSE_TAG_DIV'], {})]);
expect(output).toContain(
'<source>a<pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other">b</pc>c</source>',
);
});
});
});
});
Expand Down