'', 'msgid' => '', 'msgid_plural' => '', 'msgstr' => array() ); $parsing_state = ''; $current_str_index = 0; foreach ( $file_lines as $line ) { $line = trim( self::ensure_string( $line ) ); if ( empty( $line ) || 0 === strpos( $line, '#' ) ) { continue; } if ( 0 === strpos( $line, 'msgctxt' ) ) { if ( ! empty( $current_entry['msgid'] ) || ( '' === $current_entry['msgid'] && ! empty( $current_entry['msgstr'][0] ) ) ) { $strings[] = $current_entry; $current_entry = array( 'msgctxt' => '', 'msgid' => '', 'msgid_plural' => '', 'msgstr' => array() ); } $current_entry['msgctxt'] = self::clean_po_line( $line, 'msgctxt' ); $parsing_state = 'CTXT'; continue; } if ( 0 === strpos( $line, 'msgid_plural' ) ) { $current_entry['msgid_plural'] = self::clean_po_line( $line, 'msgid_plural' ); $parsing_state = 'PLURAL'; continue; } if ( 0 === strpos( $line, 'msgid' ) ) { if ( ! empty( $current_entry['msgid'] ) || ( '' === $current_entry['msgid'] && ! empty( $current_entry['msgstr'][0] ) ) ) { $strings[] = $current_entry; $current_entry = array( 'msgctxt' => '', 'msgid' => '', 'msgid_plural' => '', 'msgstr' => array() ); } $current_entry['msgid'] = self::clean_po_line( $line, 'msgid' ); $parsing_state = 'ID'; continue; } if ( 0 === strpos( $line, 'msgstr' ) ) { if ( preg_match( '/^msgstr\[(\d+)\]/', $line, $matches ) ) { $current_str_index = (int) $matches[1]; $current_entry['msgstr'][$current_str_index] = self::clean_po_line( $line, "msgstr[{$current_str_index}]" ); } else { $current_str_index = 0; $current_entry['msgstr'][$current_str_index] = self::clean_po_line( $line, 'msgstr' ); } $parsing_state = 'STR'; continue; } if ( 0 === strpos( $line, '"' ) ) { $cleaned_append = self::clean_po_line( $line, '' ); if ( 'ID' === $parsing_state ) { $current_entry['msgid'] .= $cleaned_append; } elseif ( 'PLURAL' === $parsing_state ) { $current_entry['msgid_plural'] .= $cleaned_append; } elseif ( 'CTXT' === $parsing_state ) { $current_entry['msgctxt'] .= $cleaned_append; } elseif ( 'STR' === $parsing_state ) { $current_entry['msgstr'][$current_str_index] .= $cleaned_append; } } } if ( ! empty( $current_entry['msgid'] ) || ( '' === $current_entry['msgid'] && ! empty( $current_entry['msgstr'][0] ) ) ) { $strings[] = $current_entry; } // Keep translation elements and predictably filter based ONLY on explicit method invocation parameters. return array_values( array_filter( $strings, function( $item ) use ( $skip_header ) { $is_header_node = ( '' === $item['msgid'] && '' === trim( (string) $item['msgctxt'] ) ); if ( $is_header_node && $skip_header ) { return false; } return ( '' !== $item['msgid'] ) || ( '' === $item['msgid'] && ! empty( $item['msgstr'][0] ) ); })); } /** * Compiles data array state changes back into a standardized Gettext string layout payload. */ public static function generate_po_string( $strings ) { $output = "# Generated securely via AF Translate Workspace Engine Matrix\n"; $header_node = null; $translation_nodes = array(); foreach ( $strings as $node ) { if ( ! isset( $node['msgid'] ) ) { continue; } if ( '' === $node['msgid'] ) { if ( ! empty( $node['msgstr'][0] ) || ! empty( $node['msgstr'] ) ) { $header_node = $node; } } else { $translation_nodes[] = $node; } } // Always prioritize writing the original system structural header node first if ( $header_node ) { $output .= "msgid \"\"\n"; $msgstr_val = is_array( $header_node['msgstr'] ) ? reset( $header_node['msgstr'] ) : $header_node['msgstr']; $lines = explode( "\n", trim( $msgstr_val ) ); $output .= "msgstr \"\"\n"; foreach ( $lines as $line ) { if ( '' === $line ) { continue; } $clean_line = rtrim( $line, '\\n' ); $escaped_line = self::safe_escape_gettext( $clean_line ) . '\\n'; $output .= "\"{$escaped_line}\"\n"; } $output .= "\n"; } else { // Safe fallback schema structure if the file contains no pre-existing header node $output .= "msgid \"\"\nmsgstr \"\"\n" . "\"Project-Id-Version: AF Translate Workspace Core 3.0\\n\"\n" . "\"MIME-Version: 1.0\\n\"\n" . "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" . "\"Content-Transfer-Encoding: 8bit\\n\"\n\n"; } // Process standard translation keys foreach ( $translation_nodes as $node ) { if ( ! empty( $node['msgctxt'] ) ) { $msgctxt = self::safe_escape_gettext( $node['msgctxt'] ); $output .= "msgctxt \"{$msgctxt}\"\n"; } $msgid = self::safe_escape_gettext( $node['msgid'] ); $output .= "msgid \"{$msgid}\"\n"; if ( ! empty( $node['msgid_plural'] ) ) { $msgid_plural = self::safe_escape_gettext( $node['msgid_plural'] ); $output .= "msgid_plural \"{$msgid_plural}\"\n"; $msgstr_array = isset( $node['msgstr'] ) ? (array) $node['msgstr'] : array('', ''); if ( count( $msgstr_array ) < 2 ) { $msgstr_array = array('', ''); } foreach ( $msgstr_array as $index => $val ) { $val_escaped = self::safe_escape_gettext( $val ); $output .= "msgstr[{$index}] \"{$val_escaped}\"\n"; } } else { $msgstr_val = ''; if ( isset( $node['msgstr'] ) ) { $msgstr_val = is_array( $node['msgstr'] ) ? reset( $node['msgstr'] ) : $node['msgstr']; } $msgstr_escaped = self::safe_escape_gettext( $msgstr_val ); $output .= "msgstr \"{$msgstr_escaped}\"\n"; } $output .= "\n"; } return $output; } }