feat(xmb/xtb): support dtd
This commit is contained in:
parent
44093905e2
commit
e34a04d2ad
|
@ -16,6 +16,27 @@ describe('template i18n extraction output', () => {
|
|||
|
||||
it('should extract i18n messages', () => {
|
||||
const EXPECTED = `<? xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE messagebundle [
|
||||
<!ELEMENT messagebundle (msg)*>
|
||||
<!ATTLIST messagebundle class CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT msg (#PCDATA|ph|source)*>
|
||||
<!ATTLIST msg id CDATA #IMPLIED>
|
||||
<!ATTLIST msg seq CDATA #IMPLIED>
|
||||
<!ATTLIST msg name CDATA #IMPLIED>
|
||||
<!ATTLIST msg desc CDATA #IMPLIED>
|
||||
<!ATTLIST msg meaning CDATA #IMPLIED>
|
||||
<!ATTLIST msg obsolete (obsolete) #IMPLIED>
|
||||
<!ATTLIST msg xml:space (default|preserve) "default">
|
||||
<!ATTLIST msg is_hidden CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT source (#PCDATA)>
|
||||
|
||||
<!ELEMENT ph (#PCDATA|ex)*>
|
||||
<!ATTLIST ph name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="5a2858f1" desc="desc" meaning="meaning">translate me</msg>
|
||||
</messagebundle>`;
|
||||
|
|
|
@ -18,11 +18,29 @@ const _MESSAGE_TAG = 'msg';
|
|||
const _PLACEHOLDER_TAG = 'ph';
|
||||
const _EXEMPLE_TAG = 'ex';
|
||||
|
||||
const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
||||
<!ATTLIST messagebundle class CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT msg (#PCDATA|ph|source)*>
|
||||
<!ATTLIST msg id CDATA #IMPLIED>
|
||||
<!ATTLIST msg seq CDATA #IMPLIED>
|
||||
<!ATTLIST msg name CDATA #IMPLIED>
|
||||
<!ATTLIST msg desc CDATA #IMPLIED>
|
||||
<!ATTLIST msg meaning CDATA #IMPLIED>
|
||||
<!ATTLIST msg obsolete (obsolete) #IMPLIED>
|
||||
<!ATTLIST msg xml:space (default|preserve) "default">
|
||||
<!ATTLIST msg is_hidden CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT source (#PCDATA)>
|
||||
|
||||
<!ELEMENT ph (#PCDATA|ex)*>
|
||||
<!ATTLIST ph name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT ex (#PCDATA)>`;
|
||||
|
||||
export class Xmb implements Serializer {
|
||||
// TODO(vicb): DOCTYPE
|
||||
write(messageMap: {[k: string]: i18n.Message}): string {
|
||||
const visitor = new _Visitor();
|
||||
const declaration = new xml.Declaration({version: '1.0', encoding: 'UTF-8'});
|
||||
let rootNode = new xml.Tag(_MESSAGES_TAG);
|
||||
rootNode.children.push(new xml.Text('\n'));
|
||||
|
||||
|
@ -44,7 +62,9 @@ export class Xmb implements Serializer {
|
|||
});
|
||||
|
||||
return xml.serialize([
|
||||
declaration,
|
||||
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}),
|
||||
new xml.Text('\n'),
|
||||
new xml.Doctype(_MESSAGES_TAG, _DOCTYPE),
|
||||
new xml.Text('\n'),
|
||||
rootNode,
|
||||
]);
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface IVisitor {
|
|||
visitTag(tag: Tag): any;
|
||||
visitText(text: Text): any;
|
||||
visitDeclaration(decl: Declaration): any;
|
||||
visitDoctype(doctype: Doctype): any;
|
||||
}
|
||||
|
||||
class _Visitor implements IVisitor {
|
||||
|
@ -36,6 +37,10 @@ class _Visitor implements IVisitor {
|
|||
.join(' ');
|
||||
return strAttrs.length > 0 ? ' ' + strAttrs : '';
|
||||
}
|
||||
|
||||
visitDoctype(doctype: Doctype): any {
|
||||
return `<!DOCTYPE ${doctype.rootTag} [\n${doctype.dtd}\n]>`;
|
||||
}
|
||||
}
|
||||
|
||||
const _visitor = new _Visitor();
|
||||
|
@ -58,6 +63,12 @@ export class Declaration implements Node {
|
|||
visit(visitor: IVisitor): any { return visitor.visitDeclaration(this); }
|
||||
}
|
||||
|
||||
export class Doctype implements Node {
|
||||
constructor(public rootTag: string, public dtd: string){};
|
||||
|
||||
visit(visitor: IVisitor): any { return visitor.visitDoctype(this); }
|
||||
}
|
||||
|
||||
export class Tag implements Node {
|
||||
public attrs: {[k: string]: string} = {};
|
||||
|
||||
|
|
|
@ -23,6 +23,27 @@ export function main(): void {
|
|||
<p i18n>{ count, plural, =0 { { sex, gender, other {<p>deeply nested</p>}} }}</p>`;
|
||||
|
||||
const XMB = `<? xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE messagebundle [
|
||||
<!ELEMENT messagebundle (msg)*>
|
||||
<!ATTLIST messagebundle class CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT msg (#PCDATA|ph|source)*>
|
||||
<!ATTLIST msg id CDATA #IMPLIED>
|
||||
<!ATTLIST msg seq CDATA #IMPLIED>
|
||||
<!ATTLIST msg name CDATA #IMPLIED>
|
||||
<!ATTLIST msg desc CDATA #IMPLIED>
|
||||
<!ATTLIST msg meaning CDATA #IMPLIED>
|
||||
<!ATTLIST msg obsolete (obsolete) #IMPLIED>
|
||||
<!ATTLIST msg xml:space (default|preserve) "default">
|
||||
<!ATTLIST msg is_hidden CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT source (#PCDATA)>
|
||||
|
||||
<!ELEMENT ph (#PCDATA|ex)*>
|
||||
<!ATTLIST ph name CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="834fa53b">translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></msg>
|
||||
<msg id="7a2843db">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}}</msg>
|
||||
|
|
|
@ -32,9 +32,26 @@ export function main(): void {
|
|||
|
||||
|
||||
describe('load', () => {
|
||||
it('should load XTB files with a doctype', () => {
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*>
|
||||
<!ATTLIST translationbundle lang CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT translation (#PCDATA|ph)*>
|
||||
<!ATTLIST translation id CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT ph EMPTY>
|
||||
<!ATTLIST ph name CDATA #REQUIRED>
|
||||
]>
|
||||
<translationbundle>
|
||||
<translation id="foo">bar</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(XTB, {})).toEqual({foo: 'bar'});
|
||||
});
|
||||
|
||||
it('should load XTB files without placeholders', () => {
|
||||
const XTB = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="foo">bar</translation>
|
||||
</translationbundle>`;
|
||||
|
@ -43,8 +60,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should load XTB files with placeholders', () => {
|
||||
const XTB = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="foo">bar<ph name="PLACEHOLDER"/><ph name="PLACEHOLDER"/></translation>
|
||||
</translationbundle>`;
|
||||
|
@ -53,8 +69,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should load complex XTB files', () => {
|
||||
const XTB = `
|
||||
<? xml version="1.0" encoding="UTF-8" ?>
|
||||
const XTB = `<? xml version="1.0" encoding="UTF-8" ?>
|
||||
<translationbundle>
|
||||
<translation id="a">translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></translation>
|
||||
<translation id="b">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}}</translation>
|
||||
|
@ -98,8 +113,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should throw on nested <translation>', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
const XTB = `<translationbundle>
|
||||
<translation id="outer">
|
||||
<translation id="inner">
|
||||
</translation>
|
||||
|
@ -112,8 +126,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should throw when a <translation> has no id attribute', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
const XTB = `<translationbundle>
|
||||
<translation></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
|
@ -123,8 +136,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should throw when a placeholder has no name attribute', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
const XTB = `<translationbundle>
|
||||
<translation id="fail"><ph /></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
|
@ -134,8 +146,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should throw when a placeholder is not present in the source message', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
const XTB = `<translationbundle>
|
||||
<translation id="fail"><ph name="UNKNOWN"/></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
|
@ -146,8 +157,7 @@ export function main(): void {
|
|||
});
|
||||
|
||||
it('should throw when the translation results in invalid html', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
const XTB = `<translationbundle>
|
||||
<translation id="fail">foo<ph name="CLOSE_P"/>bar</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
|
|
Loading…
Reference in New Issue