Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

I18n: Correct WPCOM locale fallback when theme .mo files use {textdomain}-{locale} names under the themes directory.
31 changes: 25 additions & 6 deletions projects/plugins/wpcomsh/i18n.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
*
* @see p8yzl4-4c-p2
*
* @param string $mofile .mo language file being loaded by load_textdomain().
* @param string $mofile .mo language file being loaded by load_textdomain().
* @param string $domain Text domain.
* @return string $mofile same or alternate mo file.
*/
function wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile ) {
function wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, $domain = '' ) {
if ( file_exists( $mofile ) ) {
return $mofile;
}
Expand All @@ -27,8 +28,26 @@ function wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile ) {
require JETPACK__GLOTPRESS_LOCALES_PATH;
}

$locale_slug = basename( $mofile, '.mo' );
$actual_locale_slug = $locale_slug;
$original_mo_basename = basename( $mofile, '.mo' );
$locale_slug = $original_mo_basename;

$theme_root = function_exists( 'get_theme_root' )
? wp_normalize_path( trailingslashit( get_theme_root() ) )
: '';

// On WP Cloud sites, get_template_directory() returns /wordpress/themes/pub...
// which is the actual path to the theme, symlinked from wp-content/themes.
// _load_textdomain_just_in_time sees that the folders don't match
// and builds the mo file as "{$path}{$domain}-{$locale}.mo", so we need to
// remove the domain prefix to get the locale.
$remove_domain_prefix_for_theme = is_string( $domain ) && '' !== $domain
&& '' !== $theme_root
&& str_starts_with( wp_normalize_path( $mofile ), $theme_root )
&& str_starts_with( $original_mo_basename, $domain . '-' );

if ( $remove_domain_prefix_for_theme ) {
$locale_slug = str_replace( $domain . '-', '', $original_mo_basename );
}

// These locales are not in our GP_Locales file, so rewrite them.
$locale_mappings = array(
Expand Down Expand Up @@ -56,10 +75,10 @@ function wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile ) {
}

// phpcs:ignore WordPress.PHP.PregQuoteDelimiter.Missing
$mofile = preg_replace( '/' . preg_quote( $actual_locale_slug ) . '\.mo$/', $locale_slug . '.mo', $mofile );
$mofile = preg_replace( '/' . preg_quote( $original_mo_basename ) . '\.mo$/', $locale_slug . '.mo', $mofile );
return $mofile;
}
add_filter( 'load_textdomain_mofile', 'wpcomsh_wporg_to_wpcom_locale_mo_file', 9999 );
add_filter( 'load_textdomain_mofile', 'wpcomsh_wporg_to_wpcom_locale_mo_file', 9999, 2 );

// Load translations for wpcomsh itself via MO file.
add_action(
Expand Down
85 changes: 85 additions & 0 deletions projects/plugins/wpcomsh/tests/I18nTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* Tests for i18n.php.
*
* @package wpcomsh
*/

/**
* Class I18nTest.
*/
class I18nTest extends WP_UnitTestCase {
use \Automattic\Jetpack\PHPUnit\WP_UnitTestCase_Fix;

/**
* Ensure GlotPress locales are loadable (normally provided by Jetpack on Atomic).
*/
public function set_up() {
parent::set_up();

if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) ) {
$locales = dirname( __DIR__, 3 ) . '/packages/compat/lib/locales.php';
if ( ! file_exists( $locales ) ) {
$this->markTestSkipped( 'Jetpack compat locales file not found: ' . $locales );
}
define( 'JETPACK__GLOTPRESS_LOCALES_PATH', $locales );
}
}

/**
* When the .mo file already exists, the path must not be rewritten.
*/
public function test_returns_unchanged_when_mo_file_exists() {
$tmp = tempnam( sys_get_temp_dir(), 'wpcomsh-i18n-' );
$this->assertNotFalse( $tmp );
$mofile = $tmp . '.mo';
$this->assertTrue( rename( $tmp, $mofile ) );

$result = wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, 'any-domain' );
$this->assertSame( $mofile, $result );

$this->assertTrue( unlink( $mofile ) );
}

public function test_rewrites_domain_prefixed_theme_mo_nb_no_to_no_slug() {
$theme_root = trailingslashit( wp_normalize_path( get_theme_root() ) );
$mofile = $theme_root . 'faketheme/languages/faketheme-nb_NO.mo';
$this->assertFalse( file_exists( $mofile ) );

$result = wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, 'faketheme' );

$this->assertSame( $theme_root . 'faketheme/languages/no.mo', $result );
}

public function test_rewrites_domain_prefixed_theme_mo_de_de_formal() {
$theme_root = trailingslashit( wp_normalize_path( get_theme_root() ) );
$mofile = $theme_root . 'faketheme/languages/faketheme-de_DE_formal.mo';
$this->assertFalse( file_exists( $mofile ) );

$result = wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, 'faketheme' );

$this->assertSame( $theme_root . 'faketheme/languages/de.mo', $result );
}

public function test_rewrites_unprefixed_theme_mo_de_de_to_de_slug() {
$theme_root = trailingslashit( wp_normalize_path( get_theme_root() ) );
$mofile = $theme_root . 'faketheme/languages/de_DE.mo';
$this->assertFalse( file_exists( $mofile ) );

$result = wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, 'faketheme' );

$this->assertSame( $theme_root . 'faketheme/languages/de.mo', $result );
}

/**
* Paths outside get_theme_root() do not strip {domain}- from the basename; unknown wp_locale leaves path unchanged.
*/
public function test_does_not_rewrite_plugin_style_path_with_domain_like_basename() {
$mofile = trailingslashit( wp_normalize_path( WP_PLUGIN_DIR ) ) . 'some-plugin/languages/some-plugin-nb_NO.mo';
$this->assertFalse( file_exists( $mofile ) );

$result = wpcomsh_wporg_to_wpcom_locale_mo_file( $mofile, 'some-plugin' );

$this->assertSame( $mofile, $result );
}
}
Loading