Skip to content

Commit

Permalink
Merge pull request #913 from taneliang/taneliang/912
Browse files Browse the repository at this point in the history
Support custom namespaced functions and components
  • Loading branch information
karellm committed Oct 1, 2023
2 parents 55e074c + 4b315d4 commit e7977ef
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 5 deletions.
29 changes: 28 additions & 1 deletion src/lexers/javascript-lexer.js
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
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
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
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

0 comments on commit e7977ef

Please sign in to comment.