diff --git a/index.js b/index.js
index 763972a..5562e47 100644
--- a/index.js
+++ b/index.js
@@ -5,8 +5,34 @@ module.exports = function(babel)
const t = babel.types;
const importAdded = new WeakSet();
const arg0 = new WeakSet();
+ const regexp = new WeakMap();
const ru = /[А-ЯЁа-яё]/;
const strings = {};
+ const getRegexp = function(state)
+ {
+ let re = regexp.get(state.opts);
+ if (!re)
+ {
+ re = new RegExp(state.opts.regexp || '[А-ЯЁа-яё]');
+ regexp.set(state.opts, re);
+ }
+ return re;
+ };
+ const splitWhite = function(str)
+ {
+ let l = /^\s+/.exec(str), r = /\s+$/.exec(str);
+ l = l ? l[0] : '';
+ r = r ? r[0] : '';
+ return [ l, str.substr(l.length, str.length-r.length-l.length), r ];
+ };
+ const withWhite = function(l, repl, r)
+ {
+ if (l)
+ repl = t.binaryExpression('+', t.stringLiteral(l), repl);
+ if (r)
+ repl = t.binaryExpression('+', repl, t.stringLiteral(r));
+ return repl;
+ };
const addString = function(path, str)
{
strings[path.hub.file.opts.filename][str] = str;
@@ -44,26 +70,98 @@ module.exports = function(babel)
}
if (!found)
delete strings[path.hub.file.opts.filename];
+ const arrays = { ...strings };
+ for (let k in arrays)
+ arrays[k] = Object.keys(arrays[k]);
fs.writeFileSync(
path.hub.file.opts.root+'/'+(state.opts['output'] || 'react-translate-output.json'),
- JSON.stringify(strings, null, 2)
+ JSON.stringify(arrays, null, 2)
);
},
},
- Literal(path)
+ // Convert concatenated string literals and expressions to localization calls with arguments
+ // I.e. 'Really delete '+item.name+'?' -> L('Really delete {1}?', item.name)
+ BinaryExpression(path, state)
{
+ if (path.node.operator === '+')
+ {
+ let added = [ path.node.left, path.node.right ];
+ for (let i = 0; i < added.length; i++)
+ {
+ if (t.isBinaryExpression(added[i]) && added[i].operator === '+')
+ {
+ added.splice(i, 1, added[i].left, added[i].right);
+ i--;
+ }
+ }
+ let ru = getRegexp(state);
+ let i18n = false;
+ for (let i = 0; i < added.length; i++)
+ {
+ if (t.isStringLiteral(added[i]) && ru.exec(added[i].value))
+ {
+ i18n = true;
+ break;
+ }
+ }
+ if (i18n)
+ {
+ let tpl = '', expressions = [];
+ let lit = true, i = 0;
+ while (i < added.length)
+ {
+ if (lit)
+ {
+ let text = '';
+ while (i < added.length && t.isStringLiteral(added[i]))
+ text += added[i++].value;
+ tpl += text;
+ }
+ else
+ {
+ let expr = null;
+ while (i < added.length && !t.isStringLiteral(added[i]))
+ expr = expr ? t.binaryExpression('+', expr, added[i++]) : added[i++];
+ expressions.push(expr);
+ tpl += '{'+expressions.length+'}';
+ }
+ lit = !lit;
+ }
+ let [ lwhite, text, rwhite ] = splitWhite(tpl);
+ addString(path, text);
+ text = t.stringLiteral(text);
+ // Stop the string literal from being visited again
+ arg0.add(text);
+ let repl = t.callExpression(t.identifier('L'), [ text, ...expressions ]);
+ repl = withWhite(lwhite, repl, rwhite);
+ path.replaceWith(repl);
+ }
+ }
+ },
+ // Convert simple string literals to localization calls
+ // I.e. "Hello" -> L("Hello")
+ Literal(path, state)
+ {
+ let ru = getRegexp(state);
if (ru.exec(path.node.value))
{
- addString(path, path.node.value);
- addImport(path);
const parent = path.findParent(() => true);
- if (parent.isJSXAttribute())
- path.replaceWith(t.jsxExpressionContainer(t.callExpression(t.identifier('L'), [ path.node ])));
- else if (!arg0.has(path.node) && !path.findParent(parent => arg0.has(parent.node)))
+ const isJSX = parent.isJSXAttribute();
+ if (isJSX || !arg0.has(path.node) && !path.findParent(parent => arg0.has(parent.node)))
{
- // Stop the original string from being visited again
- arg0.add(path.node);
- path.replaceWith(t.callExpression(t.identifier('L'), [ path.node ]));
+ const [ lwhite, text, rwhite ] = splitWhite(path.node.value);
+ addString(path, text);
+ addImport(path);
+ let repl = t.callExpression(t.identifier('L'), [ t.stringLiteral(text) ]);
+ repl = withWhite(lwhite, repl, rwhite);
+ if (isJSX)
+ path.replaceWith(t.jsxExpressionContainer(repl));
+ else
+ {
+ // Stop the original string from being visited again
+ arg0.add(repl);
+ path.replaceWith(repl);
+ }
}
}
},
@@ -75,38 +173,44 @@ module.exports = function(babel)
arg0.add(path.node.arguments[0]);
}
},
- JSXText(path)
+ // Convert simple JSX literals to localization calls
+ // I.e. Text -> {L("Text")}
+ JSXText(path, state)
{
+ let ru = getRegexp(state);
if (ru.exec(path.node.value))
{
+ const [ lwhite, text, rwhite ] = splitWhite(path.node.value);
addImport(path);
- const lwhite = /^\s+/.exec(path.node.value);
- const rwhite = /\s+$/.exec(path.node.value);
- const llen = lwhite ? lwhite[0].length : 0;
- const text = path.node.value.substr(llen, path.node.value.length - llen - (rwhite ? rwhite[0].length : 0));
addString(path, text);
const repl = [];
if (lwhite)
- repl.push(t.jsxText(lwhite[0]));
+ repl.push(t.jsxText(lwhite));
repl.push(t.jsxExpressionContainer(t.callExpression(t.identifier('L'), [ t.stringLiteral(text) ])));
if (rwhite)
- repl.push(t.jsxText(rwhite[0]));
+ repl.push(t.jsxText(rwhite));
path.replaceWithMultiple(repl);
}
},
- TemplateLiteral(path)
+ // Convert template literals to localization calls with arguments
+ // I.e. `Really delete ${item.name}?` -> L("Really delete {1}?", item.name)
+ TemplateLiteral(path, state)
{
+ let ru = getRegexp(state);
if (path.node.quasis.find(q => ru.exec(q.value.cooked)))
{
addImport(path);
- let text = path.node.quasis[0].value.cooked;
+ let tpl = path.node.quasis[0].value.cooked;
for (let i = 1; i < path.node.quasis.length; i++)
- text += '{'+i+'}'+path.node.quasis[i].value.cooked;
+ tpl += '{'+i+'}'+path.node.quasis[i].value.cooked;
+ let [ lwhite, text, rwhite ] = splitWhite(tpl);
addString(path, text);
- // Stop the string literal from being visited again
text = t.stringLiteral(text);
+ // Stop the string literal from being visited again
arg0.add(text);
- path.replaceWith(t.callExpression(t.identifier('L'), [ text, ...path.node.expressions ]));
+ let repl = t.callExpression(t.identifier('L'), [ text, ...path.node.expressions ]);
+ repl = withWhite(lwhite, repl, rwhite);
+ path.replaceWith(repl);
}
},
},