-
-
Save dasilvaluis/ebca42b8b8d70e81f8917f675a784060 to your computer and use it in GitHub Desktop.
/** | |
* 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 <?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 | |
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)); | |
}); |
{ | |
"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", | |
"run-sequence": "^2.2.1" | |
}, | |
"scripts": { | |
"twig-pot": "gulp pot" | |
} | |
} |
<?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); | |
} )); |
Hi, thanks for your script! If you're using Gulp 4 you have better to remove
run-sequence
as a dependency and rather use gulp.series and don't forget to signalize that the script has ended withdone();
function/** * Main Task */ gulp.task('pot', gulp.series('compile-twigs', 'generate-pot'));
Hi @vyskoczilova This script is using gulp 3. At the time gulp 4 wasn't mature enough so I left it as it is. Maybe I'll update it in the future. Thank you!
👍 Gracias :)
Had to make a change in order to get it to run on Gulp 4 but this was so useful to create .pot
files out of Twig templates, obrigado! 🙏
Had to make a change in order to get it to run on Gulp 4 but this was so useful to create
.pot
files out of Twig templates, obrigado! 🙏
@csalmeida could you please share your solution? Thinking of adding a file for gulp 4 too.
Sure, I have adapted quite a lot of it for my specific use case but I think it boils down to this:
I've changed the main task to use series()
instead of runSequence()
:
gulp.task('pot', gulp.series(['compile-twigs', 'generate-pot']));
Returned the stream of the compile-twigs
task:
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;
});
Hope this helps! Thanks again.
Hi, thanks for your script! If you're using Gulp 4 you have better to remove
run-sequence
as a dependency and rather use gulp.series and don't forget to signalize that the script has ended withdone();
function