add JSX translation extraction

This commit is contained in:
Florian Dold 2016-11-16 04:02:02 +01:00
parent af11f34fb5
commit a56210dfc9
3 changed files with 159 additions and 16 deletions

View File

@ -21,7 +21,7 @@
* @author Florian Dold * @author Florian Dold
*/ */
/// <reference path="../lib/decl/node.d.ts" /> /// <reference path="../decl/node.d.ts" />
"use strict"; "use strict";
@ -46,6 +46,6 @@ export function processFile(sourceFile: ts.SourceFile) {
const fileNames = process.argv.slice(2); const fileNames = process.argv.slice(2);
fileNames.forEach(fileName => { fileNames.forEach(fileName => {
let sourceFile = ts.createSourceFile(fileName, readFileSync(fileName).toString(), ts.ScriptTarget.ES6, /*setParentNodes */ true); let sourceFile = ts.createSourceFile(fileName, readFileSync(fileName).toString(), ts.ScriptTarget.ES2016, /*setParentNodes */ true);
processFile(sourceFile); processFile(sourceFile);
}); });

View File

@ -1 +1,15 @@
let y = <div>a{b}c</div> let x = <i18n.Translate>foo
{bar} {" foo "} baz</i18n.Translate>
let y = (
<i18n.TranslateSwitch>
<i18n.TranslateSingular>
singular form
second line
</i18n.TranslateSingular>
<i18n.TranslatePlural>
plural form
</i18n.TranslatePlural>
</i18n.TranslateSwitch>
);

View File

@ -24,7 +24,7 @@
* @author Florian Dold * @author Florian Dold
*/ */
/// <reference path="../lib/decl/node.d.ts" /> /// <reference path="../decl/node.d.ts" />
"use strict"; "use strict";
@ -43,16 +43,6 @@ export function processFile(sourceFile: ts.SourceFile) {
let lastTokLine = 0; let lastTokLine = 0;
let preLastTokLine = 0; let preLastTokLine = 0;
function getHeadName(node: ts.Node): string {
switch (node.kind) {
case ts.SyntaxKind.Identifier:
return (<ts.Identifier>node).text;
case ts.SyntaxKind.CallExpression:
case ts.SyntaxKind.PropertyAccessExpression:
return getHeadName((<any>node).expression);
}
}
function getTemplate(node: ts.Node): string { function getTemplate(node: ts.Node): string {
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.FirstTemplateToken: case ts.SyntaxKind.FirstTemplateToken:
@ -62,7 +52,7 @@ export function processFile(sourceFile: ts.SourceFile) {
let textFragments = [te.head.text]; let textFragments = [te.head.text];
for (let tsp of te.templateSpans) { for (let tsp of te.templateSpans) {
textFragments.push(`%${(textFragments.length-1)/2+1}$s`); textFragments.push(`%${(textFragments.length-1)/2+1}$s`);
textFragments.push(tsp.literal.text); textFragments.push(tsp.literal.text.replace(/%/g, "%%"));
} }
return textFragments.join(''); return textFragments.join('');
default: default:
@ -178,10 +168,149 @@ export function processFile(sourceFile: ts.SourceFile) {
} }
} }
} }
interface JsxProcessingContext {
}
function getJsxElementPath(node: ts.Node) {
let path;
let process = (childNode) => {
switch (childNode.kind) {
case ts.SyntaxKind.JsxOpeningElement:
{
let e = childNode as ts.JsxOpeningElement;
return path = getPath(e.tagName);
}
default:
break;
}
};
ts.forEachChild(node, process);
return path;
}
function translateJsxExpression(node: ts.Node, h) {
switch (node.kind) {
case ts.SyntaxKind.StringLiteral:
{
let e = node as ts.StringLiteral;
return e.text;
}
default:
return `%${h[0]++}s`;
}
}
function trim(s) {
return s.replace(/^[ \n\t]*/, "").replace(/[ \n\t]*$/, "");
}
function getJsxContent(node: ts.Node) {
let fragments = [];
let holeNum = [1];
let process = (childNode) => {
switch (childNode.kind) {
case ts.SyntaxKind.JsxText:
{
let e = childNode as ts.JsxText;
let t = e.getText().split("\n").map(trim).join("\n");
fragments.push(t);
}
case ts.SyntaxKind.JsxOpeningElement:
break;
case ts.SyntaxKind.JsxExpression:
{
let e = childNode as ts.JsxExpression;
fragments.push(translateJsxExpression(e.expression, holeNum));
break;
}
case ts.SyntaxKind.JsxClosingElement:
break;
default:
console.error("unrecognized syntax in JSX Element", ts.SyntaxKind[childNode.kind]);
break;
}
};
ts.forEachChild(node, process);
return fragments.join("");
}
function getJsxSingular(node: ts.Node) {
let res;
let process = (childNode) => {
switch (childNode.kind) {
case ts.SyntaxKind.JsxElement:
{
let path = getJsxElementPath(childNode);
if (arrayEq(path, ["i18n", "TranslateSingular"])) {
res = getJsxContent(childNode);
}
}
default:
break;
}
};
ts.forEachChild(node, process);
return res;
}
function getJsxPlural(node: ts.Node) {
let res;
let process = (childNode) => {
switch (childNode.kind) {
case ts.SyntaxKind.JsxElement:
{
let path = getJsxElementPath(childNode);
if (arrayEq(path, ["i18n", "TranslatePlural"])) {
res = getJsxContent(childNode);
}
}
default:
break;
}
};
ts.forEachChild(node, process);
return res;
}
function processNode(node: ts.Node) { function processNode(node: ts.Node) {
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.JsxElement:
let path = getJsxElementPath(node);
if (arrayEq(path, ["i18n", "Translate"])) {
let content = getJsxContent(node);
let {line} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
let comment = getComment(node);
formatMsgComment(line, comment);
formatMsgLine("msgid", content);
console.log(`msgstr[0] ""`);
console.log();
return;
}
if (arrayEq(path, ["i18n", "TranslateSwitch"])) {
let {line} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
let comment = getComment(node);
formatMsgComment(line, comment);
let singularForm = getJsxSingular(node);
if (!singularForm) {
console.error("singular form missing");
process.exit(1);
}
let pluralForm = getJsxPlural(node);
if (!pluralForm) {
console.error("plural form missing");
process.exit(1);
}
formatMsgLine("msgid", singularForm);
formatMsgLine("msgid_plural", pluralForm);
console.log(`msgstr[0] ""`);
console.log(`msgstr[1] ""`);
console.log();
return;
}
break;
case ts.SyntaxKind.CallExpression: case ts.SyntaxKind.CallExpression:
{ {
// might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`) // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`)
@ -254,6 +383,6 @@ msgstr ""
console.log() console.log()
fileNames.forEach(fileName => { fileNames.forEach(fileName => {
let sourceFile = ts.createSourceFile(fileName, readFileSync(fileName).toString(), ts.ScriptTarget.ES6, /*setParentNodes */ true); let sourceFile = ts.createSourceFile(fileName, readFileSync(fileName).toString(), ts.ScriptTarget.ES2016, /*setParentNodes */ true);
processFile(sourceFile); processFile(sourceFile);
}); });