Tag: ESLint

  • Improving WordPress Internationalization with ESLint

    Improving WordPress Internationalization with ESLint

    Avid readers will already know that I am very passionate about internationalization (I18N). Some of my most popular blog posts are about that topic:

    Internationalization is an important aspect in WordPress development as it lays the foundation for a project’s global success. Unfortunately, it is often done wrong, but things get better over time thanks to simplified APIs, improved documentation, and tooling. For example, the WordPress Coding Standards for PHP_CodeSniffer has been detecting incorrect usage of I18N functions for years now. However, there was no equivalent for this kind of detection in JavaScript source files — until today.

    Being involved with the development of many JavaScript-heavy WordPress projects, I often see common mistakes when using the @wordpress/i18n package that could be easily caught by some kind of linter. To validate my thinking, I set out to fix this issue and contribute the solution to the WordPress community.

    Extending The WordPress ESLint Plugin

    First, I started writing down all the things that could possibly be developed to help improve the WordPress JavaScript I18N landscape. This includes things like detecting wrong usage of text domains, missing translator comments, and flagging usage of variables in translatable strings. I even thought about detecting strings that should probably be translatable, but currently aren’t. Tricky to do, but one can dream.

    Then, I was looking for the best place to implement this. Luckily, WordPress and also our own projects already use a handy tool for this: ESLint. ESLint is the JavaScript-equivalent of PHPCS, and the @wordpress/eslint-plugin package is the one that can be used to enforce WordPress coding standards. For me, that was the perfect place to start.

    By reading through ESLint’s great developer documentation I learned all about creating custom linter rules, and studying existing rules in the aforementioned package, as well as eslint-plugin-wpcalypso from WordPress.com, hel.

    Before I knew it, I was knee-deep in writing ESLint rules, tests, and fixes for the issues my rules discovered. Hundreds of lines of code later, you can now use these new features in your projects!

    The New I18N ESLint Ruleset

    In total, I ended up creating six new ESLint rules around internationalization, and improving one existing rule. If you’re already using the recommended ruleset from the WordPress ESLint plugin (version 5.0.0 or higher!), you automatically benefit from these enhancements. Alternatively, you can also only extend the I18N ruleset if wanted. For example:

    {
    	"extends": [ "plugin:@wordpress/eslint-plugin/i18n" ]
    }Code language: JSON / JSON with Comments (json)

    It includes the following rules:

    @wordpress/i18n-text-domain

    The 18n-text-domain rule enforces passing valid text domains to translation functions (e.g. only string literals). It flags things like __( 'Hello World' ), but allows __( 'Hello World', 'awesome-sauce' ) if your project’s text domain is awesome-sauce.

    Your desired project text domain can be specified in the ESLint config as follows:

    {
    	"@wordpress/i18n-text-domain": [ "error", {
     		"allowedTextDomain":  "awesome-sauce"
    	} ]
    }Code language: JSON / JSON with Comments (json)

    @wordpress/i18n-translator-comments

    If using translation functions with placeholders in them, they should have accompanying translator comments. The i18n-translator-comments rule flags the lack thereof.

    @wordpress/i18n-no-variables

    In WordPress development, you must call translation functions with valid string literals as arguments. They cannot be variables or functions for technical reasons. Use the i18n-no-variables rule to easily enforce this.

    @wordpress/i18n-no-placeholders-only

    Translatable strings that consist of nothing but a placeholder, e.g. __( '%s' ), cannot be translated. The i18n-no-placeholders-only rule prevents such usage.

    @wordpress/i18n-no-collapsible-whitespace

    With the i18n-no-collapsible-whitespace rule you can prevent using complex whitespace in translatable strings. Relying on HTML to collapse such whitespace can make translation more difficult and lead to unnecessary retranslation.

    @wordpress/i18n-ellipsis

    Lastly, the i18n-ellipsis rule disallows using three dots in translatable strings. Three dots for indicating an ellipsis should be replaced with the UTF-8 character (horizontal ellipsis, U+2026) as it has a more semantic meaning.

    @wordpress/valid-sprintf

    The existing valid-sprintf rule enforces valid usage of the sprintf function exposed by the @wordpress/i18n package. I’ve extended it to catch and prevent a mix of ordered and non-ordered placeholders. Multiple sprintf placeholders should be ordered so that strings can be better translated.