Skip to content

Commit

Permalink
I18N: Actually add all the files for [58061], not just the test fixtu…
Browse files Browse the repository at this point in the history
…res.

Improve support for using only PHP translation files.

This builds on top of the PHP translation file support added in WordPress 6.5, improving the behavior for projects using solely `.l10n.php` translation files and no `.mo.` and `.po` files.

Updates `wp_get_installed_translations()`, which is used when updating language packs and when uninstalling plugins/themes (to remove the translations again), to look for PHP translation files and read metadata from them. Additionally, the file lookup is now cached thanks to using `WP_Textdomain_Registry`.

Updates `Language_Pack_Upgrader::check_package()` to allow language packs that only contain PHP translation files. While WordPress.org continues to serve `.mo` and `.po` files, third-party services might want to only use the PHP file format.

See #60554.

git-svn-id: https://develop.svn.wordpress.org/trunk@58062 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
swissspidy committed Apr 30, 2024
1 parent 22796f5 commit 2f0d2de
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 28 deletions.
20 changes: 14 additions & 6 deletions src/wp-admin/includes/class-language-pack-upgrader.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,26 +332,34 @@ public function check_package( $source, $remote_source ) {
// Check that the folder contains a valid language.
$files = $wp_filesystem->dirlist( $remote_source );

// Check to see if a .po and .mo exist in the folder.
$po = false;
$mo = false;
// Check to see if the expected files exist in the folder.
$po = false;
$mo = false;
$php = false;
foreach ( (array) $files as $file => $filedata ) {
if ( str_ends_with( $file, '.po' ) ) {
$po = true;
} elseif ( str_ends_with( $file, '.mo' ) ) {
$mo = true;
} elseif ( str_ends_with( $file, '.l10n.php' ) ) {
$php = true;
}
}

if ( $php ) {
return $source;
}

if ( ! $mo || ! $po ) {
return new WP_Error(
'incompatible_archive_pomo',
$this->strings['incompatible_archive'],
sprintf(
/* translators: 1: .po, 2: .mo */
__( 'The language pack is missing either the %1$s or %2$s files.' ),
/* translators: 1: .po, 2: .mo, 3: .l10n.php */
__( 'The language pack is missing either the %1$s, %2$s, or %3$s files.' ),
'<code>.po</code>',
'<code>.mo</code>'
'<code>.mo</code>',
'<code>.l10n.php</code>'
)
);
}
Expand Down
80 changes: 62 additions & 18 deletions src/wp-includes/l10n.php
Original file line number Diff line number Diff line change
Expand Up @@ -1505,50 +1505,59 @@ function get_available_languages( $dir = null ) {
*
* @since 3.7.0
*
* @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
*
* @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
* @return array Array of language data.
*/
function wp_get_installed_translations( $type ) {
global $wp_textdomain_registry;

if ( 'themes' !== $type && 'plugins' !== $type && 'core' !== $type ) {
return array();
}

$dir = 'core' === $type ? '' : "/$type";
$dir = 'core' === $type ? WP_LANG_DIR : WP_LANG_DIR . "/$type";

if ( ! is_dir( WP_LANG_DIR ) ) {
if ( ! is_dir( $dir ) ) {
return array();
}

if ( $dir && ! is_dir( WP_LANG_DIR . $dir ) ) {
return array();
}

$files = scandir( WP_LANG_DIR . $dir );
$files = $wp_textdomain_registry->get_language_files_from_path( $dir );
if ( ! $files ) {
return array();
}

$language_data = array();

foreach ( $files as $file ) {
if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "$dir/$file" ) ) {
continue;
}
if ( ! str_ends_with( $file, '.po' ) ) {
continue;
}
if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
continue;
}
if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files, true ) ) {
if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?)\.(?:mo|l10n\.php)/', basename( $file ), $match ) ) {
continue;
}

list( , $textdomain, $language ) = $match;
if ( '' === $textdomain ) {
$textdomain = 'default';
}
$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "$dir/$file" );

if ( str_ends_with( $file, '.mo' ) ) {
$pofile = substr_replace( $file, '.po', - strlen( '.mo' ) );

if ( ! file_exists( $pofile ) ) {
continue;
}

$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( $pofile );
} else {
$pofile = substr_replace( $file, '.po', - strlen( '.l10n.php' ) );

// If both a PO and a PHP file exist, prefer the PO file.
if ( file_exists( $pofile ) ) {
continue;
}

$language_data[ $textdomain ][ $language ] = wp_get_l10n_php_file_data( $file );
}
}
return $language_data;
}
Expand Down Expand Up @@ -1578,6 +1587,41 @@ function wp_get_pomo_file_data( $po_file ) {
return $headers;
}

/**
* Extracts headers from a PHP translation file.
*
* @since 6.6.0
*
* @param string $php_file Path to a `.l10n.php` file.
* @return string[] Array of file header values keyed by header name.
*/
function wp_get_l10n_php_file_data( $php_file ) {
$data = (array) include $php_file;

unset( $data['messages'] );
$headers = array(
'POT-Creation-Date' => 'pot-creation-date',
'PO-Revision-Date' => 'po-revision-date',
'Project-Id-Version' => 'project-id-version',
'X-Generator' => 'x-generator',
);

$result = array(
'POT-Creation-Date' => '',
'PO-Revision-Date' => '',
'Project-Id-Version' => '',
'X-Generator' => '',
);

foreach ( $headers as $po_header => $php_header ) {
if ( isset( $data[ $php_header ] ) ) {
$result[ $po_header ] = $data[ $php_header ];
}
}

return $result;
}

/**
* Displays or returns a Language selector.
*
Expand Down
20 changes: 19 additions & 1 deletion tests/phpunit/tests/l10n.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,25 @@ public function test_get_available_languages() {
$this->assertEmpty( $array );

$array = get_available_languages( DIR_TESTDATA . '/languages/' );
$this->assertSame( array( 'de_DE', 'en_GB', 'es_ES', 'ja_JP' ), $array );
$this->assertEqualSets(
array(
'de_DE',
'en_GB',
'es_ES',
'ja_JP',
'de_CH',
),
$array
);
}

/**
* @ticket 35284
* @ticket 60554
*
* @covers ::wp_get_installed_translations
* @covers ::wp_get_pomo_file_data
* @covers ::wp_get_l10n_php_file_data
*/
public function test_wp_get_installed_translations_for_core() {
$installed_translations = wp_get_installed_translations( 'core' );
Expand All @@ -95,6 +107,12 @@ public function test_wp_get_installed_translations_for_core() {
$this->assertSame( '2016-10-25 18:29+0200', $data_es_es['PO-Revision-Date'] );
$this->assertSame( 'Administration', $data_es_es['Project-Id-Version'] );
$this->assertSame( 'Poedit 1.8.10', $data_es_es['X-Generator'] );

$this->assertNotEmpty( $installed_translations['default']['de_CH'] );
$data_en_gb = $installed_translations['default']['de_CH'];
$this->assertSame( '2024-01-31 19:08:22+0000', $data_en_gb['PO-Revision-Date'] );
$this->assertSame( 'WordPress - 6.4.x - Development', $data_en_gb['Project-Id-Version'] );
$this->assertSame( 'GlotPress/4.0.0-beta.2', $data_en_gb['X-Generator'] );
}

/**
Expand Down
9 changes: 6 additions & 3 deletions tests/qunit/fixtures/wp-api-generated.js
Original file line number Diff line number Diff line change
Expand Up @@ -9230,7 +9230,8 @@ mockedApiResponse.Schema = {
"de_DE",
"en_GB",
"es_ES",
"ja_JP"
"ja_JP",
"de_CH"
],
"required": false
},
Expand Down Expand Up @@ -9361,7 +9362,8 @@ mockedApiResponse.Schema = {
"de_DE",
"en_GB",
"es_ES",
"ja_JP"
"ja_JP",
"de_CH"
],
"required": false
},
Expand Down Expand Up @@ -9502,7 +9504,8 @@ mockedApiResponse.Schema = {
"de_DE",
"en_GB",
"es_ES",
"ja_JP"
"ja_JP",
"de_CH"
],
"required": false
},
Expand Down

0 comments on commit 2f0d2de

Please sign in to comment.