Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom namespaced functions and components #913

Merged
merged 1 commit into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/lexers/javascript-lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,16 @@ export default class JavascriptLexer extends BaseLexer {
}

const isTranslationFunction =
// If the expression is a string literal, we can just check if it's in the
// list of functions
(node.expression.text && this.functions.includes(node.expression.text)) ||
// Support the case where the function is contained in a namespace, i.e.
// match `i18n.t()` when this.functions = ['t'].
(node.expression.name &&
this.functions.includes(node.expression.name.text))
this.functions.includes(node.expression.name.text)) ||
// Support matching the namespace as well, i.e. match `i18n.t()` but _not_
// `l10n.t()` when this.functions = ['i18n.t']
this.functions.includes(this.expressionToName(node.expression))

if (isTranslationFunction) {
const keyArgument = node.arguments.shift()
Expand Down Expand Up @@ -376,4 +383,24 @@ export default class JavascriptLexer extends BaseLexer {

return string
}

/**
* Recursively computes the name of a dot-separated expression, e.g. `t` or `t.ns`
* @type {(expression: ts.LeftHandSideExpression | ts.JsxTagNameExpression) => string}
*/
expressionToName(expression) {
if (expression) {
if (expression.text) {
return expression.text
} else if (expression.name) {
return [
this.expressionToName(expression.expression),
this.expressionToName(expression.name),
]
.filter((s) => s && s.length > 0)
.join('.')
}
}
return undefined
}
}
4 changes: 3 additions & 1 deletion src/lexers/jsx-lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ export default class JsxLexer extends JavascriptLexer {

const getKey = (node) => getPropValue(node, this.attr)

if (this.componentFunctions.includes(tagNode.tagName.text)) {
if (
this.componentFunctions.includes(this.expressionToName(tagNode.tagName))
) {
const entry = {}
entry.key = getKey(tagNode)

Expand Down
6 changes: 4 additions & 2 deletions test/lexers/javascript-lexer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ describe('JavascriptLexer', () => {
})

it('supports a `functions` option', (done) => {
const Lexer = new JavascriptLexer({ functions: ['tt', '_e'] })
const content = 'tt("first") + _e("second")'
const Lexer = new JavascriptLexer({ functions: ['tt', '_e', 'f.g'] })
const content = 'tt("first") + _e("second") + x.tt("third") + f.g("fourth")'
assert.deepEqual(Lexer.extract(content), [
{ key: 'first' },
{ key: 'second' },
{ key: 'third' },
{ key: 'fourth' },
])
done()
})
Expand Down
12 changes: 11 additions & 1 deletion test/lexers/jsx-lexer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,27 @@ describe('JsxLexer', () => {

it('extracts keys from user-defined components', (done) => {
const Lexer = new JsxLexer({
componentFunctions: ['Translate', 'FooBar'],
componentFunctions: [
'Translate',
'FooBar',
'Namespace.A',
'Double.Namespace.B',
],
})
const content = `<div>
<Translate i18nKey="something">Something to translate.</Translate>
<NotSupported i18nKey="jkl">asdf</NotSupported>
<NotSupported.Translate i18nKey="jkl">asdf</NotSupported.Translate>
<FooBar i18nKey="asdf">Lorum Ipsum</FooBar>
<Namespace.A i18nKey="namespaced">Namespaced</Namespace.A>
<Double.Namespace.B i18nKey="namespaced2">Namespaced2</Double.Namespace.B>
</div>
`
assert.deepEqual(Lexer.extract(content), [
{ key: 'something', defaultValue: 'Something to translate.' },
{ key: 'asdf', defaultValue: 'Lorum Ipsum' },
{ key: 'namespaced', defaultValue: 'Namespaced' },
{ key: 'namespaced2', defaultValue: 'Namespaced2' },
])
done()
})
Expand Down