/** | |
* I18N: WP_Translation_File class. | |
* | |
* @package WordPress | |
* @subpackage I18N | |
* @since 6.5.0 | |
*/ | |
/** | |
* Class WP_Translation_File. | |
* | |
* @since 6.5.0 | |
*/ | |
abstract class WP_Translation_File { | |
/** | |
* List of headers. | |
* | |
* @since 6.5.0 | |
* @var array<string, string> | |
*/ | |
protected $headers = array(); | |
/** | |
* Whether file has been parsed. | |
* | |
* @since 6.5.0 | |
* @var bool | |
*/ | |
protected $parsed = false; | |
/** | |
* Error information. | |
* | |
* @since 6.5.0 | |
* @var string|null Error message or null if no error. | |
*/ | |
protected $error; | |
/** | |
* File name. | |
* | |
* @since 6.5.0 | |
* @var string | |
*/ | |
protected $file = ''; | |
/** | |
* Translation entries. | |
* | |
* @since 6.5.0 | |
* @var array<string, string> | |
*/ | |
protected $entries = array(); | |
/** | |
* Plural forms function. | |
* | |
* @since 6.5.0 | |
* @var callable|null Plural forms. | |
*/ | |
protected $plural_forms = null; | |
/** | |
* Constructor. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $file File to load. | |
*/ | |
protected function __construct( string $file ) { | |
$this->file = $file; | |
} | |
/** | |
* Creates a new WP_Translation_File instance for a given file. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $file File name. | |
* @param string|null $filetype Optional. File type. Default inferred from file name. | |
* @return false|WP_Translation_File | |
*/ | |
public static function create( string $file, ?string $filetype = null ) { | |
if ( ! is_readable( $file ) ) { | |
return false; | |
} | |
if ( null === $filetype ) { | |
$pos = strrpos( $file, '.' ); | |
if ( false !== $pos ) { | |
$filetype = substr( $file, $pos + 1 ); | |
} | |
} | |
switch ( $filetype ) { | |
case 'mo': | |
return new WP_Translation_File_MO( $file ); | |
case 'php': | |
return new WP_Translation_File_PHP( $file ); | |
default: | |
return false; | |
} | |
} | |
/** | |
* Creates a new WP_Translation_File instance for a given file. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $file Source file name. | |
* @param string $filetype Desired target file type. | |
* @return string|false Transformed translation file contents on success, false otherwise. | |
*/ | |
public static function transform( string $file, string $filetype ) { | |
$source = self::create( $file ); | |
if ( false === $source ) { | |
return false; | |
} | |
switch ( $filetype ) { | |
case 'mo': | |
$destination = new WP_Translation_File_MO( '' ); | |
break; | |
case 'php': | |
$destination = new WP_Translation_File_PHP( '' ); | |
break; | |
default: | |
return false; | |
} | |
$success = $destination->import( $source ); | |
if ( ! $success ) { | |
return false; | |
} | |
return $destination->export(); | |
} | |
/** | |
* Returns all headers. | |
* | |
* @since 6.5.0 | |
* | |
* @return array<string, string> Headers. | |
*/ | |
public function headers(): array { | |
if ( ! $this->parsed ) { | |
$this->parse_file(); | |
} | |
return $this->headers; | |
} | |
/** | |
* Returns all entries. | |
* | |
* @since 6.5.0 | |
* | |
* @return array<string, string[]> Entries. | |
*/ | |
public function entries(): array { | |
if ( ! $this->parsed ) { | |
$this->parse_file(); | |
} | |
return $this->entries; | |
} | |
/** | |
* Returns the current error information. | |
* | |
* @since 6.5.0 | |
* | |
* @return string|null Error message or null if no error. | |
*/ | |
public function error() { | |
return $this->error; | |
} | |
/** | |
* Returns the file name. | |
* | |
* @since 6.5.0 | |
* | |
* @return string File name. | |
*/ | |
public function get_file(): string { | |
return $this->file; | |
} | |
/** | |
* Translates a given string. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $text String to translate. | |
* @return false|string Translation(s) on success, false otherwise. | |
*/ | |
public function translate( string $text ) { | |
if ( ! $this->parsed ) { | |
$this->parse_file(); | |
} | |
return $this->entries[ $text ] ?? false; | |
} | |
/** | |
* Returns the plural form for a given number. | |
* | |
* @since 6.5.0 | |
* | |
* @param int $number Count. | |
* @return int Plural form. | |
*/ | |
public function get_plural_form( int $number ): int { | |
if ( ! $this->parsed ) { | |
$this->parse_file(); | |
} | |
if ( null === $this->plural_forms && isset( $this->headers['plural-forms'] ) ) { | |
$expression = $this->get_plural_expression_from_header( $this->headers['plural-forms'] ); | |
$this->plural_forms = $this->make_plural_form_function( $expression ); | |
} | |
if ( is_callable( $this->plural_forms ) ) { | |
/** | |
* Plural form. | |
* | |
* @var int $result Plural form. | |
*/ | |
$result = call_user_func( $this->plural_forms, $number ); | |
return $result; | |
} | |
// Default plural form matches English, only "One" is considered singular. | |
return ( 1 === $number ? 0 : 1 ); | |
} | |
/** | |
* Returns the plural forms expression as a tuple. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $header Plural-Forms header string. | |
* @return string Plural forms expression. | |
*/ | |
protected function get_plural_expression_from_header( string $header ): string { | |
if ( preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches ) ) { | |
return trim( $matches[2] ); | |
} | |
return 'n != 1'; | |
} | |
/** | |
* Makes a function, which will return the right translation index, according to the | |
* plural forms header. | |
* | |
* @since 6.5.0 | |
* | |
* @param string $expression Plural form expression. | |
* @return callable(int $num): int Plural forms function. | |
*/ | |
protected function make_plural_form_function( string $expression ): callable { | |
try { | |
$handler = new Plural_Forms( rtrim( $expression, ';' ) ); | |
return array( $handler, 'get' ); | |
} catch ( Exception $e ) { | |
// Fall back to default plural-form function. | |
return $this->make_plural_form_function( 'n != 1' ); | |
} | |
} | |
/** | |
* Imports translations from another file. | |
* | |
* @since 6.5.0 | |
* | |
* @param WP_Translation_File $source Source file. | |
* @return bool True on success, false otherwise. | |
*/ | |
protected function import( WP_Translation_File $source ): bool { | |
if ( null !== $source->error() ) { | |
return false; | |
} | |
$this->headers = $source->headers(); | |
$this->entries = $source->entries(); | |
$this->error = $source->error(); | |
return null === $this->error; | |
} | |
/** | |
* Parses the file. | |
* | |
* @since 6.5.0 | |
*/ | |
abstract protected function parse_file(); | |
/** | |
* Exports translation contents as a string. | |
* | |
* @since 6.5.0 | |
* | |
* @return string Translation file contents. | |
*/ | |
abstract public function export(); | |
} | |