Skip to content

JavaScript, TypeScript, JSX

Lukas Geiter edited this page Jul 15, 2023 · 5 revisions

Parser

For extracting messages from JavaScript sources, we'll start by creating a parser:

extractor.createJsExtractor([
    // extractor functions (factories see below)
])

The returned parser instance offers the following methods for parsing source code:

parseFile(fileName, [options]) reads and parses a single file.

parseFilesGlob(pattern, [globOptions], [options]) reads and parses all files matching a pattern.

parseString(source, [fileName], [options]) parses a string.

More details can be found in the API Reference


Built-in Extractors

These are factories included in the library that offer an easy way to create extractor functions.

Function & Method Calls

This factory creates an extractor function that can extract any message that is used in some sort of function or method call. Here's an example:

JsExtractors.callExpression('[this].getText', {
    arguments: {
        text: 0,
        context: 1
    }
})

Matching

The first parameter defines which call expressions should be used for extraction.
The callee name can be provided analog to your code, but without any parenthesis or parameters.

Use [this] instead of this to make the this keyword at the beginning optional.

These are some examples:

Source Code calleeName
getText(...) 'getText'
getText(...) and this.getText(...) '[this].getText'
this.i18n.translations.get(...) 'this.i18n.translations.get'
Multiple Callee Names

If you have differently named calls, that have the same arguments to be extracted, you can pass an array of strings with all their names.

Extracting

The second parameter is an options object which must contain an arguments property. It describes which function arguments contain message data and should get extracted.

Note: Template literals are supported, but only without placeholders. Since the string containing placeholders isn't available during runtime those can't be used for translations anyway.

There are three different pieces of message information that can be extracted and for each of them you can specify the position (starting with zero) of the corresponding argument:

  • text
  • textPlural
  • context

text is required in any case, the others are optional.

Note: Arguments that are not mapped to one of the above properties will just be ignored.

You may customize how strings, especially multi-line strings, are treated (e.g. indentation whitespace) using the content property of options. More on this in the API Reference

Comments

This extractor function can also pull comments from your code and add them to the Gettext catalog as extracted comments. This goes for // single line as well as /* block */ comments.

Note: Block comments that span multiple lines are not supported.

By default all comments on the same line (before or after) of the call expression are extracted. You can change this configuration by adding a comments object to the options you pass in. All available settings are listed in the API Reference.

Embedded HTML

If your JavaScript contains strings of HTML, you can extract messages from that code as well. To do this, you have to create a HTML Parser first, and pass it to this extractor function factory.

For all strings and template strings, the extractor function will pass the contents to the HTML parser to parse it as if it was a normal file.

Here's an example:

let htmlParser = extractor.createHtmlParser([
    HtmlExtractors.elementContent('[translate]', {
        attributes: {
            textPlural: 'translate-plural',
            context: 'translation-context',
            comment: 'translation-comment'
        }
    })
]);

JsExtractors.htmlTemplate(htmlParser);

Full Example

hello.jsx

export class HelloWorld extends React.Component {
    render() {
        return (
            <div>
                <h1>{ t('Hello World!', 'title') }</h1>
            </div>
        );
    }
}

view.jsx

import { HelloWorld } from 'hello.jsx';

export class View extends React.Component {
    constructor(props) {
        super(props);
        this.translations = props.translationService;
    }

    render() {
        return (
            <div>
                <HelloWorld/>
                <button onClick={this.refresh.bind(this)}>
                    { this.translations.get('Refresh') /* Comment */ }
                </button>
            </div>
        );
    }

    refresh() {
        let count = Math.round(Math.random() * 100);
        alert(this.translations.plural(count, 'One new message', '{{n}} new messages'));
    }
}

Extractor configuration

const { GettextExtractor, JsExtractors } = require('gettext-extractor');

let extractor = new GettextExtractor();

extractor
    .createJsParser([
        JsExtractors.callExpression(['t', '[this].translations.get'], {
            arguments: {
                text: 0,
                context: 1
            }
        }),
        JsExtractors.callExpression('[this].translations.plural', {
            arguments: {
                text: 1,
                textPlural: 2,
                context: 3
            }
        })
    ])
    .parseFilesGlob('**/*.@(js|jsx)');

extractor.savePotFile('template.pot');

Messages in template.pot

#: view.jsx:22
msgid "One new message"
msgid_plural "{{n}} new messages"
msgstr[0] ""
msgstr[1] ""

#. Comment
#: view.jsx:14
msgid "Refresh"
msgstr ""

#: hello.jsx:5
msgctxt "title"
msgid "Hello World!"
msgstr ""