我必须解决识别 ES6 模板字符串的gettext
限制,我想过获取模板字符串的"非内插值"作为编译步骤,以便在代码中只有"普通"字符串。
基本上我想实现的是改变这个
const adjective = 'wonderful'
const something = `Look, I am a ${adjective} string`
console.log(something)
> "Look, I am a wonderful string"
进入这个
const adjective = 'wonderful'
const something = 'Look, I am a ${adjective} string'
console.log(something)
> "Look, I am a ${adjective} string"
实现这一点的一种残酷方法是使用 sed
,但它肯定不是更优雅的(也可能容易出错)
sed "s/`/'/g" FILENAME
有什么更好、更干净的想法浮现在脑海中吗?
好问题。我想到了四种解决方案:
1. 蛮力
正如您所建议的,在扫描可翻译字符串之前用引号暴力替换反引号并不是一个可怕的想法,只要您了解风险。例如,考虑:
"hello, this word is in `backticks`"
另一个边缘情况是
`${`I am nested`}`
此方法还将中断多行模板字符串。
2.修复xgettext
当然,"正确"的解决方案是编写一个处理模板字符串的xgettext
分支。然后你可以写
const something = _(`Look, I am a ${adjective} string`);
不幸的是,这可能比看起来更难。xgettext 中有一堆与字符串相关的硬连线逻辑。如果你要承担这个项目,很多人会感谢你。
3. 使用解析器
更健壮的替代方案是使用 JavaScript 解析器,如 Esprima。这些分析器公开了获取令牌(如模板字符串)的功能。正如您在 http://esprima.org/demo/parse.html 中看到的,要查找的相关令牌类型是 TemplateLiteral
.
4. 不明智的黑客攻击
另一个(不好的?)想法是将模板字符串编写为常规字符串,然后在运行时将它们视为模板字符串。我们定义一个函数eval_template
:
const template = _("Look, I am a ${adjective} string");
const something = eval_template(template, {adjective});
eval_template
将字符串转换为计算的模板。模板字符串中使用的局部范围中的任何变量都需要作为第二个参数中传递的对象的一部分提供给eval_template
(因为使用 Function
创建的函数在全局范围内并且无法访问局部变量,因此我们必须将它们传入)。它的实现方式如下:
function eval_template_(s, params) {
var keys = Object.keys(params);
var vals = keys.map(key => params[key]);
var f = Function(...keys, "return `" + s + "`");
return f(...vals);
}
当然,这有点尴尬。这种方法的唯一优点是不需要预扫描重写。
小点,但如果原始模板字符串是多行的,则不能直接将其重写为常规字符串。在这种情况下,您可以将其保留为反引号模板字符串,但转义$
为 $
,一切都会好起来的:
底线:除非你想重写xgettext
,使用解析器,或从事其他黑客行为,否则请进行暴力替换。
目前,我正在研究基于 es6 模板文字的本地化解决方案。你可以在这里查看它 - https://c-3po.js.org。该项目具有提取功能(基于 babel 插件)。你也可以使用它来构建本地化的js。下面是它的外观:
t`Hello ${name}`