你能把ES6模板字符串简化成普通字符串吗?

9

我需要解决gettext无法识别ES6模板字符串的限制,我的想法是在编译阶段获取模板字符串的“非插值值”,以便代码中只有“普通”字符串。

基本上,我想要实现的是将这个转换为:

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
不,你不能“简化”模板字符串而不失去“智能”属性(包括字符串扩展)。请注意,(`A ${b=alert()}`)将执行alert(),而'A ${b=alert()}'则不会。 - Ismael Miguel
1
我知道。我不需要在gettext中使用智能功能,因为它无法理解模板字符串。(我已经更明确地表达了我的问题) - domokun
你是只询问字符串插值,还是也需要模板标签? - Bergi
你尝试过像 babel 这样的转译器吗? - Bergi
转换器不是可行的选择,因为翻译字符串被包装在一个函数中,该函数也会被转换。例如,t('translate me') 变成了类似于 function(par, 0)('translate me') 的东西,而 gettext 不理解这个。 - domokun
2个回答

8

很好的问题。我想到了四个解决方案:

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. 不建议使用的hack方法
另一个(不好的?)想法是首先将模板字符串写成常规字符串,然后在运行时将其视为模板字符串。我们定义了一个名为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,使用解析器或进行其他黑客行为,否则请进行粗暴的替换。


不必这么麻烦,你可以使用t => Function('return `' + t + '`')()。希望它能够正常工作。 - Ismael Miguel
不幸的是,这并不起作用,因为与eval不同,使用Function构造函数定义的函数在全局范围内,并且无法访问诸如adjective之类的变量。 - user663031
好观点。确实是非常好的观点。无论如何,肯定有更简单的方法来做到这一点。 - Ismael Miguel

0

目前,我正在开发一种基于es6模板字面量的本地化解决方案。您可以在这里查看它 - https://c-3po.js.org。该项目具有提取功能(基于babel插件)。您还可以使用它来构建本地化的js。以下是它的外观:

t`Hello ${name}`

这与我在这里所做的非常相似:https://github.com/dmatteo/crusca-cli - domokun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接