Skip to content

Instantly share code, notes, and snippets.

@lucassus
Created February 2, 2018 11:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucassus/dd82d89d5c491bb76c95c5798b1cf00d to your computer and use it in GitHub Desktop.
Save lucassus/dd82d89d5c491bb76c95c5798b1cf00d to your computer and use it in GitHub Desktop.
const { includes, last, lowerCase, padStart } = require("lodash");
const babylon = require("babylon");
const generator = require("babel-generator").default;
const t = require("babel-types");
const traverse = require("babel-traverse").default;
const fs = require("fs");
const path = require("path");
const prettier = require("prettier");
// Usage: t-extractor [options] [filename]
const argv = require("minimist")(process.argv.slice(2), {
default: {
write: false,
keyPrefix: "extracted"
}
});
console.info(argv);
const fileName = last(argv._);
if (!fileName) {
console.error("Please provide a file");
process.exit(1);
}
const code = fs.readFileSync(path.join(__dirname, "..", fileName)).toString();
const ast = babylon.parse(code, {
sourceType: "module",
plugins: ["jsx", "classProperties", "objectRestSpread"]
});
const translations = {};
function* keyGenerator() {
let n = 0;
while (true) {
n = n + 1;
yield `extracted_${padStart(n, 3, "0")}`;
}
}
const keyIterator = keyGenerator();
const pushTranslation = text => {
const key = [
argv.keyPrefix,
keyIterator.next().value,
lowerCase(text)
.replace(/\s/gi, "_")
.substr(0, 32)
].join(":");
translations[key] = text.trim();
return { key };
};
const tExpression = key =>
t.callExpression(t.identifier("t"), [t.stringLiteral(key)]);
traverse(ast, {
// Do nothing with translated strings `t("something")`
CallExpression(path) {
if (path.node.callee.name === "t") {
path.skip();
}
},
// Do nothing with import statements
ImportDeclaration(path) {
path.skip();
},
// TODO(lucassus): template literals are quite completed :/
TemplateLiteral(path) {
path.skip();
},
StringLiteral(path) {
const { key } = pushTranslation(path.node.value);
if (t.isJSXAttribute(path.parent)) {
path.replaceWith(t.jSXExpressionContainer(tExpression(key)));
}
if (t.isStringLiteral(path)) {
// Do not translate property's keys
if (t.isObjectProperty(path.parent) && path.parent.key === path.node) {
return;
}
path.replaceWith(tExpression(key));
}
},
JSXElement(path) {
const tags = ["svg"];
if (includes(tags, path.node.openingElement.name.name)) {
path.skip();
}
},
// Do not translate JSX `className` attributes
JSXAttribute(path) {
const attrs = ["className", "id", "type"];
if (includes(attrs, path.node.name.name)) {
path.skip();
}
},
// Strings inside JSX tags
JSXText(path) {
const { value } = path.node;
// Skip empty nodes
if (value.trim() === "") {
return;
}
const { key } = pushTranslation(value);
path.replaceWith(t.jSXExpressionContainer(tExpression(key)));
}
});
const generatedCode = prettier.format(generator(ast).code);
if (argv.write) {
fs.writeFileSync(fileName, generatedCode);
} else {
console.info(generatedCode);
}
console.info(translations);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment