/** * Gettext Scanner Script for Twig Projects * v1.3 * * Developed by Luís Silva * https://github.com/luism-s */ /** * Purpose: * Scan Twig and PHP files in the given directories for gettext function calls and output a POT file for translation. * * Description: * While working with Wordpress using the Twig template engine, one might find easier to use gettext * functions in .twig files for string translation. To simplify the scanning of .twig files for those same functions, this * script was built to parse .twig files, wrap occurrences of gettext function calls in php tags and * output the result as a .php file and from that generate a POT file. * It also scans PHP files as well. * * Context: https://github.com/timber/timber/issues/1465 * * Usage: `gulp pot` * * Logic: * - Iterates over all given .twig files * - Search and replace for gettext functions in Twig files and wraps them around PHP tags * - Outputs each file as .php into a cache folder * - Scan all .php files for gettext functions using 'gulp-wp-pot' (cache included) * - Generate .pot file * * Dependencies: * `npm install gulp gulp-if del gulp-wp-pot gulp-replace gulp-rename run-sequence` * * Warning: * This script has only ben tested in the context of Wordpress theme development using Timber. * * TODO: * Cover `translate_nooped_plural` function. */ const gulp = require('gulp'); const gulpif = require('gulp-if'); const del = require('del'); const wpPot = require('gulp-wp-pot'); const replace = require('gulp-replace'); const rename = require('gulp-rename'); const runSequence = require('run-sequence'); /** * Configuration Options * * All paths are as if this script is * located in the root of the theme and all the Twig * files are located under /views */ const config = { "text_domain" : "theme-test", // Replace with your domain "twig_files" : "views/**/*.twig", // Twig Files "php_files" : "**/*.php", // PHP Files "cacheFolder" : "views/cache", // Cache Folder "destFolder" : "languages", // Folder where .pot file will be saved "keepCache" : true // Delete cache files after script finishes }; /** * __ * _e * _x * _xn * _ex * _n_noop * _nx_noop * translate -> Match __, _e, _x and so on * \( -> Match ( * \s*? -> Match empty space 0 or infinite times, as few times as possible (ungreedy) * ['"] -> Match ' or " * .+? -> Match any character, 1 to infinite times, as few times as possible (ungreedy) * , -> Match , * .+? -> Match any character, 1 to infinite times, as few times as possible (ungreedy) * \) -> Match ) */ const gettext_regex = { // _e( "text", "domain" ) // __( "text", "domain" ) // translate( "text", "domain" ) // esc_attr__( "text", "domain" ) // esc_attr_e( "text", "domain" ) // esc_html__( "text", "domain" ) // esc_html_e( "text", "domain" ) simple: /(__|_e|translate|esc_attr__|esc_attr_e|esc_html__|esc_html_e)\(\s*?['"].+?['"]\s*?,\s*?['"].+?['"]\s*?\)/g, // _n( "single", "plural", number, "domain" ) plural: /_n\(\s*?['"].*?['"]\s*?,\s*?['"].*?['"]\s*?,\s*?.+?\s*?,\s*?['"].+?['"]\s*?\)/g, // _x( "text", "context", "domain" ) // _ex( "text", "context", "domain" ) // esc_attr_x( "text", "context", "domain" ) // esc_html_x( "text", "context", "domain" ) // _nx( "single", "plural", "number", "context", "domain" ) disambiguation: /(_x|_ex|_nx|esc_attr_x|esc_html_x)\(\s*?['"].+?['"]\s*?,\s*?['"].+?['"]\s*?,\s*?['"].+?['"]\s*?\)/g, // _n_noop( "singular", "plural", "domain" ) // _nx_noop( "singular", "plural", "context", "domain" ) noop: /(_n_noop|_nx_noop)\((\s*?['"].+?['"]\s*?),(\s*?['"]\w+?['"]\s*?,){0,1}\s*?['"].+?['"]\s*?\)/g, }; /** * Main Task */ gulp.task('pot', function(callback) { runSequence('compile-twigs', 'generate-pot', callback); }); /** * Generate POT file from all .php files in the theme, * including the cache folder. */ gulp.task('generate-pot', () => { const output = gulp.src(config.php_files) .pipe(wpPot({ domain: config.text_domain })) .pipe(gulp.dest(`${config.destFolder}/${config.text_domain}.pot`)) .pipe(gulpif(!config.keepCache, del.bind(null, [config.cacheFolder], { force: true }))); return output; }); /** * Fake Twig Gettext Compiler * * Searches and replaces all occurences of __('string', 'domain'), _e('string', 'domain') and so on, * with or and saves the content * in a .php file with the same name in the cache folder. * * Functions supported: * * Simple: __(), _e(), translate() * Plural: _n() * Disambiguation: _x(), _ex(), _nx() * Noop: _n_loop(), _nx_noop() */ gulp.task('compile-twigs', () => { del.bind(null, [config.cacheFolder], {force: true}) // Iterate over .twig files gulp.src(config.twig_files) // Search for Gettext function calls and wrap them around PHP tags. .pipe(replace(gettext_regex.simple, match => ``)) .pipe(replace(gettext_regex.plural, match => ``)) .pipe(replace(gettext_regex.disambiguation, match => ``)) .pipe(replace(gettext_regex.noop, match => ``)) // Rename file with .php extension .pipe(rename({ extname: '.php', })) // Output the result to the cache folder as a .php file. .pipe(gulp.dest(config.cacheFolder)); });