i18n-tasks
i18n-tasks copied to clipboard
Patterns in JS
I'm using the i18n-js gem/package to use the translations also in JS and till now I don't really understand which patterns are recognized by the scanner, I was always getting unused translations.
There are several possibilities to specify the translation in JS (and interpolate strings too).
To avoid to interpolate strings like i18n.t("some.scope." + key)
or i18n.t(`some.scope.${key}`)
, I'm just using the scope option in the following way (see also here):
i18n.t(key, { scope: 'some.scope' });
I think this syntax is very similar to the one used in Ruby.
Could you clarify that aspect? I haven't found anything special about JS in the docs. I'm using the scope option when the key is dynamic, but also a simple string sometimes is not recognized. Few examples:
$(".modal-body", this.alertProductModal).html(I18n.t('configurations.check_background.your_product_doesnt_fit'));
$(".modal-footer", this.alertProductModal).append($("<a id='ignore_alert_background' class='conf-btn btn btn-outline-dark btn-secondary'>" + I18n.t('global.ignore') + "</a>"));
Thank you for your support!
@afdev82 Are these translations in a html file or a javascript file?
It's javascript
Then I think you would have to implement a CustomScanner to handle a case like this.
Ah ok,
in the Usage search section of the README I read:
i18n-tasks uses an AST scanner for .rb and .html.erb files, and a regexp scanner for all other files.
I thought that the javascript files were supported by the regexp scanner and it was not needed to implement a custom one. If I need to implement one for the javascript files it's also fine, could it be clarified in the README which files are supported out of the box? Thank you!
@afdev82 Ah, that makes sense to document. The existing one probably works for a lot of syntaxes, but I do not think it can handle the Javascript object as parameter.
Could it be worth to improve the existing one? I think many users could benefit from it. First I will try to have a look at the custom scanner to fix my issue, I think if I find a solution, maybe it could be integrated later.
@afdev82 Yes, that would probably be good 🙂 Could you write some test cases?
I'll try to write some when I will work on that again. For the moment, thank you!
One approach for the JS scanner is using JS AST parser like @babel/parser
and @babel/traverse
to traverse and find the items.
Example here:
let fs = require('fs')
let parser = require("@babel/parser")
let traverse = require("@babel/traverse")
function collectCalls(filepath) {
let results = []
let code = fs.readFileSync(filepath).toString()
let ast = parser.parse(code, {
// parse in strict mode and allow module declarations
sourceType: "module",
plugins: [
// enable jsx and flow syntax
"jsx",
],
});
traverse.default(ast, {
CallExpression(path) {
//console.log(path.node)
let { loc, start, end } = path.node
let { type, object: objectNode, property: propertyNode } = path.node.callee
if (type == 'MemberExpression' && objectNode.name == 'I18n' && (propertyNode.name == 't' || propertyNode.name == 'translate')) {
let [ { value: rawKey }, defaultArg ] = path.node.arguments
let h = {
path: filepath,
pos: start,
line_num: loc.start.line,
line_pos: loc.start.column,
line: code.substring(start, end),
raw_key: rawKey || null,
default_arg: null,
}
if (defaultArg && defaultArg.type == 'ObjectExpression') {
let node = defaultArg.properties.find(node => node.key.name == 'defaultValue')
if (node) {
if (node.value.type == 'StringLiteral') {
h.default_arg = node.value.value
} else if (node.value.type == 'ObjectExpression') {
h.default_arg = node.value.properties.reduce((obj, property) => {
obj[property.key.name] = property.value.value
return obj
}, {})
}
}
}
results.push(h)
}
},
})
return results
}
Or a simple fix would be to update the pattern to allow a lowercase i18n.t
.
https://github.com/glebm/i18n-tasks/blob/a2b06e3cf8cebb10cf47f680e82779f353839815/lib/i18n/tasks/scanners/pattern_scanner.rb#L15
Something like:
- TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-]I18n\.|I18n\.)t(?:ranslate)?/
+ TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-](?:I|i)18n\.|(?:I|i)18n\.)t(?:ranslate)?/
Or:
- TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-]I18n\.|I18n\.)t(?:ranslate)?/
+ TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-][Ii]18n\.|[Ii]18n\.)t(?:ranslate)?/
Either one of these patches is working for me.