-
-
Save csalmeida/51e95684ada90ae1230b0c85ae9be49a to your computer and use it in GitHub Desktop.
Gettext Scanner Gulp 4 Script for Twig Projects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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` | |
* | |
* 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'); | |
/** | |
* 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', gulp.series(['compile-twigs', 'generate-pot'])); | |
/** | |
* 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 <?php __('string', 'domain'); ?> or <?php _e('string', 'domain'); ?> 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 | |
const output = gulp.src(config.twig_files) | |
// Search for Gettext function calls and wrap them around PHP tags. | |
.pipe(replace(gettext_regex.simple, match => `<?php ${match}; ?>`)) | |
.pipe(replace(gettext_regex.plural, match => `<?php ${match}; ?>`)) | |
.pipe(replace(gettext_regex.disambiguation, match => `<?php ${match}; ?>`)) | |
.pipe(replace(gettext_regex.noop, match => `<?php ${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)); | |
return output; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "twig-gettext-pot-parser", | |
"version": "1.2.0", | |
"dependencies": { | |
"gulp": "3.9.1", | |
"del": "^3.0.0", | |
"gulp-if": "^2.0.2", | |
"gulp-rename": "^1.2.2", | |
"gulp-replace": "^0.6.1", | |
"gulp-wp-pot": "^2.2.0", | |
}, | |
"scripts": { | |
"twig-pot": "gulp pot" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Include Wordpress gettext functions in Twig if necessary | |
*/ | |
// Init twig engine | |
global $twig; | |
$loader = new Twig_Loader_Filesystem( '/views' ); | |
$twig = new Twig_Environment( $loader, [] ); | |
// Add wordpress gettext functions | |
$twig->addFunction(new Twig_SimpleFunction('__', function( $text, $domain = 'default' ) { | |
return __($text, $domain); | |
} )); | |
$twig->addFunction(new Twig_SimpleFunction('_n', function( $single, $plural, $number, $domain = 'default' ) { | |
return _n($single, $plural, $number, $domain); | |
} )); | |
$twig->addFunction(new Twig_SimpleFunction('_x', function( $text, $context, $domain = 'default' ) { | |
return _x($text, $context, $domain); | |
} )); | |
$twig->addFunction(new Twig_SimpleFunction('_ex', function( $text, $context, $domain = 'default' ) { | |
return _ex($text, $context, $domain); | |
} )); | |
$twig->addFunction(new Twig_SimpleFunction('_nx', function( $single, $plural, $number, $context, $domain = 'default' ) { | |
return _nx($single, $plural, $number, $context, $domain); | |
} )); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment