Skip to content

Instantly share code, notes, and snippets.

@jack-arturo
Created August 22, 2025 20:39
Show Gist options
  • Save jack-arturo/419d0aae732915a3ebbb5b88604a0ec7 to your computer and use it in GitHub Desktop.
Save jack-arturo/419d0aae732915a3ebbb5b88604a0ec7 to your computer and use it in GitHub Desktop.
Automated deploy process for WP Fusion (local)
const { src, dest, series, parallel } = require( 'gulp' );
const replace = require( 'gulp-replace' );
const { exec } = require( 'child_process' );
const fs = require( 'fs' );
const replacementPath = {
phpFiles: './**/*.php',
exclude: [ '!./node_modules/**', '!./vendor/**', '!./languages/**' ],
mainPluginFile: './wp-fusion.php',
readmeFile: './readme.txt',
potFile: './languages/wp-fusion.pot',
configDir: './config',
languagesDir: './languages',
};
// Target languages for translation
const targetLanguages = [
'es_ES', // Spanish
'fr_FR', // French
'de_DE', // German
'it_IT', // Italian
'pt_BR', // Portuguese (Brazil)
'nl_NL', // Dutch
'ru_RU', // Russian
'ja', // Japanese
'zh_CN', // Chinese (Simplified)
'ko_KR', // Korean
];
function getVersionFromReadme() {
const fs = require( 'fs' );
const readmeContent = fs.readFileSync( replacementPath.readmeFile, 'utf8' );
const match = readmeContent.match( /^Stable tag:\s*(.+)$/m );
if ( ! match ) {
throw new Error( 'Version not found in readme.txt' );
}
return match[ 1 ].trim();
}
function updatePHPDocs() {
const version = getVersionFromReadme();
return src( [ replacementPath.phpFiles, ...replacementPath.exclude ] )
.pipe( replace( /\bx\.x(?:\.x)?\b/g, version ) )
.pipe( dest( './' ) );
}
function updateMainPluginFile() {
const version = getVersionFromReadme();
return src( replacementPath.mainPluginFile, { base: './' } )
.pipe(
replace( /^\s*\*\s*Version:\s*.+$/m, ` * Version: ${ version }` )
)
.pipe( dest( './' ) );
}
function updateVersionConstant() {
const version = getVersionFromReadme();
return src( replacementPath.mainPluginFile, { base: './' } )
.pipe(
replace(
/(define\(\s*['"]WP_FUSION_VERSION['"],\s*['"]).+?(['"]\s*\);)/,
`$1${ version }$2`
)
)
.pipe( dest( './' ) );
}
function updatePOHeaders() {
const version = getVersionFromReadme();
const headerFile = `${ replacementPath.configDir }/po-header.json`;
if ( fs.existsSync( headerFile ) ) {
const headers = JSON.parse( fs.readFileSync( headerFile, 'utf8' ) );
headers[ 'Project-Id-Version' ] = `WP Fusion ${ version }`;
fs.writeFileSync( headerFile, JSON.stringify( headers, null, '\t' ) );
}
return Promise.resolve();
}
function generatePotFile( done ) {
// Disable Xdebug in CI environment to prevent infinite loop detection
const xdebugSettings = process.env.CI ? '-d xdebug.mode=off' : '';
const command = `php ${ xdebugSettings } -d memory_limit=1024M -d error_reporting='E_ALL & ~E_DEPRECATED' -d max_execution_time=300 $(command -v wp) i18n make-pot ./ ./languages/wp-fusion.pot --exclude=node_modules,vendor --allow-root`;
exec( command, ( err, stdout, stderr ) => {
if ( err ) {
const fatalError = stderr
.split( '\n' )
.filter( ( line ) => ! line.includes( 'Deprecated' ) )
.join( '\n' );
if ( fatalError.trim() ) {
console.error( `Error: ${ fatalError }` );
} else if ( stdout && stdout.includes( 'Fatal error' ) ) {
console.error( `Error: ${ stdout }` );
}
done( err );
} else {
console.log( stdout );
done();
}
} );
}
function generateTranslations( done ) {
// Check if POT file exists
if ( ! fs.existsSync( replacementPath.potFile ) ) {
console.error(
'POT file not found. Please run generatePotFile first.'
);
done( new Error( 'POT file not found' ) );
return;
}
// Check if .env file exists
if ( ! fs.existsSync( './.env' ) ) {
console.warn(
'No .env file found. Please copy env-example.txt to .env and configure your OpenAI API key.'
);
console.log( 'Skipping automatic translations...' );
done();
return;
}
console.log( 'Starting incremental translations with Potomatic...' );
console.log( `Languages: ${ targetLanguages.join( ', ' ) }` );
console.log( 'Only new/changed strings will be translated' );
// Process each language individually to use existing .po files for incremental translation
let languageIndex = 0;
function processNextLanguage() {
if ( languageIndex >= targetLanguages.length ) {
console.log( 'All translations completed successfully!' );
done();
return;
}
const language = targetLanguages[ languageIndex ];
const existingPoFile = `${ replacementPath.languagesDir }/wp-fusion-${ language }.po`;
// Build Potomatic command for this language
let command =
`npx potomatic ` +
`--pot-file-path ${ replacementPath.potFile } ` +
`--target-languages ${ language } ` +
`--output-dir ${ replacementPath.languagesDir } ` +
`--po-file-prefix wp-fusion- ` +
`--locale-format wp_locale ` +
`--use-dictionary ` +
`--dictionary-path ${ replacementPath.configDir }/dictionaries ` +
`--max-retries 3 ` +
`--temperature 0.3 ` +
`--verbose-level 1 ` +
`--timeout 120`;
// Add existing .po file for incremental translation if it exists
if ( fs.existsSync( existingPoFile ) ) {
command += ` --input-po-path ${ existingPoFile }`;
console.log(
`Processing ${ language }: Updating existing translations (incremental)`
);
} else {
console.log(
`Processing ${ language }: Creating new translation file`
);
}
exec( command, ( err, stdout, stderr ) => {
if ( err ) {
console.error(
`Translation error for ${ language }: ${ err.message }`
);
console.error( stderr );
console.warn(
`Skipping ${ language } and continuing with next language`
);
} else {
console.log( `${ language } completed successfully` );
if ( stdout.includes( 'strings translated' ) ) {
// Extract and show translation summary
const summaryMatch = stdout.match( /(\d+) translated/ );
if ( summaryMatch ) {
console.log(
` → ${ summaryMatch[ 1 ] } strings translated`
);
}
}
}
languageIndex++;
processNextLanguage();
} );
}
processNextLanguage();
}
function testTranslationSingle( done ) {
// Check if POT file exists
if ( ! fs.existsSync( replacementPath.potFile ) ) {
console.error(
'POT file not found. Please run generatePotFile first.'
);
done( new Error( 'POT file not found' ) );
return;
}
// Check if .env file exists
if ( ! fs.existsSync( './.env' ) ) {
console.warn(
'No .env file found. Please copy env-example.txt to .env and configure your OpenAI API key.'
);
console.log( 'Skipping test translation...' );
done();
return;
}
// Test with just German for quick verification
const testLanguage = 'de_DE';
const existingPoFile = `${ replacementPath.languagesDir }/wp-fusion-${ testLanguage }.po`;
// Potomatic command with test settings - smaller batch, more verbose, cost limit
let command =
`npx potomatic ` +
`--pot-file-path ${ replacementPath.potFile } ` +
`--target-languages ${ testLanguage } ` +
`--output-dir ${ replacementPath.languagesDir } ` +
`--po-file-prefix wp-fusion-test- ` +
`--locale-format wp_locale ` +
`--use-dictionary ` +
`--dictionary-path ${ replacementPath.configDir }/dictionaries ` +
`--batch-size 5 ` +
`--max-strings-per-job 50 ` +
`--max-cost 5 ` +
`--jobs 1 ` +
`--max-retries 2 ` +
`--temperature 0.3 ` +
`--verbose-level 2 ` +
`--timeout 60`;
// Add existing .po file for incremental translation if it exists
if ( fs.existsSync( existingPoFile ) ) {
command += ` --input-po-path ${ existingPoFile }`;
console.log(
'Using existing German translations as base (incremental)'
);
} else {
console.log( 'Creating fresh German test translation' );
}
console.log( 'Starting TEST translation with Potomatic...' );
console.log( `Test language: German (${ testLanguage })` );
console.log( 'Limited to 50 strings, $5 max cost' );
console.log( 'Output file: wp-fusion-test-de_DE.po' );
exec( command, ( err, stdout, stderr ) => {
if ( err ) {
console.error( `Test translation error: ${ err.message }` );
console.error( stderr );
done( err );
} else {
console.log( 'Test translation completed successfully!' );
console.log( stdout );
console.log(
'\n🎉 Test completed! Check languages/wp-fusion-test-de_DE.po for results.'
);
done();
}
} );
}
function createMoFiles( done ) {
// Generate .mo files from .po files
const command = `find ${ replacementPath.languagesDir } -name "*.po" -exec sh -c 'msgfmt "$1" -o "\${1%.po}.mo"' _ {} \\;`;
exec( command, ( err, stdout, stderr ) => {
if ( err ) {
console.warn(
`Warning: Could not generate .mo files: ${ err.message }`
);
// Don't fail build for .mo generation errors
} else {
console.log( 'Generated .mo files successfully' );
}
done();
} );
}
// Export individual tasks
exports.updatePHPDocs = updatePHPDocs;
exports.updateMainPluginFile = updateMainPluginFile;
exports.updateVersionConstant = updateVersionConstant;
exports.updatePOHeaders = updatePOHeaders;
exports.generatePotFile = generatePotFile;
exports.generateTranslations = generateTranslations;
exports.testTranslationSingle = testTranslationSingle;
exports.createMoFiles = createMoFiles;
// Main build task without translations
exports.build = series(
updatePHPDocs,
updateMainPluginFile,
updateVersionConstant,
updatePOHeaders,
generatePotFile
);
// Complete release task with translations
exports.release = series(
updatePHPDocs,
updateMainPluginFile,
updateVersionConstant,
updatePOHeaders,
generatePotFile,
generateTranslations,
createMoFiles
);
// Default task (now includes translations for complete release)
exports.default = series(
updatePHPDocs,
updateMainPluginFile,
updateVersionConstant,
updatePOHeaders,
generatePotFile,
generateTranslations,
createMoFiles
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment