$references ) { foreach ( $references as $ref ) { $pot_content .= "#: " . esc_html( $ref ) . "\n"; } // Securely strip and normalize layout escapes matching native gettext token rules $clean_msgid = str_replace( '\\"', '"', $msgid ); $escaped_msgid = addcslashes( $clean_msgid, '"\\' ); $pot_content .= "msgid \"" . $escaped_msgid . "\"\n"; $pot_content .= "msgstr \"\"\n\n"; } } // 5. Commit compiled raw text safely down to disk layer if ( false === file_put_contents( $pot_file_path, $pot_content ) ) { return new WP_Error( 'write_failed', esc_html__( 'Failed to write generated POT file structure to disk.', 'af-translate' ) ); } return true; } /** * Recursively loops through the directory to grab strings matching the text domain. * Memory-optimized to prevent vendor folder memory crashes. */ private static function extract_strings_from_codebase( $dir_path, $domain ) { $strings = array(); try { $directory = new RecursiveDirectoryIterator( $dir_path, RecursiveDirectoryIterator::SKIP_DOTS ); $iterator = new RecursiveIteratorIterator( $directory ); } catch ( Exception $e ) { return $strings; } // Directories to strictly skip during execution parsing loops $excluded_directories = array( 'node_modules', 'vendor', 'assets', 'bower_components', '.git', '.github' ); // Regex explicitly targets: __, _e, _x, esc_html__, esc_html_e, esc_attr__, esc_attr_e $regex = '/\b(?:__|_e|_x|esc_html__|esc_html_e|esc_attr__|esc_attr_e)\(\s*([\'"])(.*?)\1\s*,\s*([\'"])' . preg_quote( $domain, '/' ) . '\3\s*\)/'; foreach ( $iterator as $file ) { if ( ! $file->isFile() || 'php' !== $file->getExtension() ) { continue; } $file_path = wp_normalize_path( $file->getPathname() ); $path_segments = explode( '/', trim( str_replace( wp_normalize_path( $dir_path ), '', $file_path ), '/' ) ); // Check if file belongs to an excluded asset or dependency group $skip_file = false; foreach ( $excluded_directories as $exclude ) { if ( in_array( $exclude, $path_segments, true ) ) { $skip_file = true; break; } } if ( $skip_file ) { continue; } $content = file_get_contents( $file_path ); if ( false === $content ) { continue; } if ( preg_match_all( $regex, $content, $matches, PREG_SET_ORDER ) ) { // Anonymize full system structure relative to standard workspace targets $relative_file = str_replace( wp_normalize_path( WP_CONTENT_DIR ), '', $file_path ); foreach ( $matches as $match ) { $msgid = $match; if ( empty( $msgid ) ) { continue; } if ( ! isset( $strings[$msgid] ) ) { $strings[$msgid] = array(); } $strings[$msgid][] = ltrim( $relative_file, '/' ); } } } // Deduplicate tracking tags cleanly across references foreach ( $strings as $msgid => $refs ) { $strings[$msgid] = array_unique( $refs ); } return $strings; } /** * Generates a standard GNU gettext headers architecture. */ private static function build_pot_header( $slug ) { $date = date_i18n( 'Y-m-d H:iO' ); $year = date_i18n( 'Y' ); return "# Copyright (C) {$year} AF Themes\n" . "# This file is distributed under the same license as the " . ucfirst( $slug ) . " package.\n" . "msgid \"\"\n" . "msgstr \"\"\n" . "\"Project-Id-Version: " . ucfirst( $slug ) . "\\n\"\n" . "\"POT-Creation-Date: {$date}\\n\"\n" . "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n" . "\"Last-Translator: FULL NAME \\n\"\n" . "\"Language-Team: LANGUAGE \\n\"\n" . "\"MIME-Version: 1.0\\n\"\n" . "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" . "\"Content-Transfer-Encoding: 8bit\\n\"\n" . "\"X-Generator: AF Translate Engine v1.0.0\\n\"\n\n"; } }