Skip to content

Commit 287b2f8

Browse files
committed
I18N: Mail: Make PHPMailer messages translatable.
Adds a new `WP_PHPMailer` class to leverage the WordPress i18n system with PHPMailer, so that any user-visible error messages can be properly translated. Props sukhendu2002, swissspidy, audrasjb, iandunn, nacin, mark-k. Fixes #23311. git-svn-id: https://develop.svn.wordpress.org/trunk@59592 602fd350-edb4-49c9-b593-d223f7449a82
1 parent d5f8c9b commit 287b2f8

File tree

9 files changed

+192
-8
lines changed

9 files changed

+192
-8
lines changed

src/wp-includes/class-wp-locale-switcher.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,18 +273,23 @@ private function load_translations( $locale ) {
273273
* @since 4.7.0
274274
*
275275
* @global WP_Locale $wp_locale WordPress date and time locale object.
276+
* @global PHPMailer\PHPMailer\PHPMailer $phpmailer
276277
*
277278
* @param string $locale The locale to change to.
278279
*/
279280
private function change_locale( $locale ) {
280-
global $wp_locale;
281+
global $wp_locale, $phpmailer;
281282

282283
$this->load_translations( $locale );
283284

284285
$wp_locale = new WP_Locale();
285286

286287
WP_Translation_Controller::get_instance()->set_locale( $locale );
287288

289+
if ( $phpmailer instanceof WP_PHPMailer ) {
290+
$phpmailer->SetLanguage();
291+
}
292+
288293
/**
289294
* Fires when the locale is switched to or restored.
290295
*
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
/**
3+
* WordPress PHPMailer class.
4+
*
5+
* @package WordPress
6+
* @since 6.8.0
7+
*/
8+
9+
/**
10+
* WordPress PHPMailer class.
11+
*
12+
* Overrides the internationalization method in order to use WordPress' instead.
13+
*
14+
* @since 6.8.0
15+
*/
16+
class WP_PHPMailer extends PHPMailer\PHPMailer\PHPMailer {
17+
18+
/**
19+
* Constructor.
20+
*
21+
* @since 6.8.0
22+
*
23+
* @param bool $exceptions Optional. Whether to throw exceptions for errors. Default false.
24+
*/
25+
public function __construct( $exceptions = false ) {
26+
parent::__construct( $exceptions );
27+
$this->SetLanguage();
28+
}
29+
30+
/**
31+
* Defines the error messages using WordPress' internationalization method.
32+
*
33+
* @since 6.8.0
34+
*
35+
* @return true Always returns true.
36+
*/
37+
public function SetLanguage( $langcode = 'en', $lang_path = '' ) {
38+
$error_strings = array(
39+
'authenticate' => __( 'SMTP Error: Could not authenticate.' ),
40+
'buggy_php' => sprintf(
41+
/* translators: 1: mail.add_x_header. 2: php.ini */
42+
__(
43+
'Your version of PHP is affected by a bug that may result in corrupted messages. To fix it, switch to sending using SMTP, disable the %1$s option in your %2$s, or switch to MacOS or Linux, or upgrade your PHP version.'
44+
),
45+
'mail.add_x_header',
46+
'php.ini'
47+
),
48+
'connect_host' => __( 'SMTP Error: Could not connect to SMTP host.' ),
49+
'data_not_accepted' => __( 'SMTP Error: data not accepted.' ),
50+
'empty_message' => __( 'Message body empty' ),
51+
/* translators: There is a space after the colon. */
52+
'encoding' => __( 'Unknown encoding: ' ),
53+
/* translators: There is a space after the colon. */
54+
'execute' => __( 'Could not execute: ' ),
55+
/* translators: There is a space after the colon. */
56+
'extension_missing' => __( 'Extension missing: ' ),
57+
/* translators: There is a space after the colon. */
58+
'file_access' => __( 'Could not access file: ' ),
59+
/* translators: There is a space after the colon. */
60+
'file_open' => __( 'File Error: Could not open file: ' ),
61+
/* translators: There is a space after the colon. */
62+
'from_failed' => __( 'The following From address failed: ' ),
63+
'instantiate' => __( 'Could not instantiate mail function.' ),
64+
/* translators: There is a space after the colon. */
65+
'invalid_address' => __( 'Invalid address: ' ),
66+
'invalid_header' => __( 'Invalid header name or value' ),
67+
/* translators: There is a space after the colon. */
68+
'invalid_hostentry' => __( 'Invalid hostentry: ' ),
69+
/* translators: There is a space after the colon. */
70+
'invalid_host' => __( 'Invalid host: ' ),
71+
/* translators: There is a space at the beginning. */
72+
'mailer_not_supported' => __( ' mailer is not supported.' ),
73+
'provide_address' => __( 'You must provide at least one recipient email address.' ),
74+
/* translators: There is a space after the colon. */
75+
'recipients_failed' => __( 'SMTP Error: The following recipients failed: ' ),
76+
/* translators: There is a space after the colon. */
77+
'signing' => __( 'Signing Error: ' ),
78+
/* translators: There is a space after the colon. */
79+
'smtp_code' => __( 'SMTP code: ' ),
80+
/* translators: There is a space after the colon. */
81+
'smtp_code_ex' => __( 'Additional SMTP info: ' ),
82+
'smtp_connect_failed' => __( 'SMTP connect() failed.' ),
83+
/* translators: There is a space after the colon. */
84+
'smtp_detail' => __( 'Detail: ' ),
85+
/* translators: There is a space after the colon. */
86+
'smtp_error' => __( 'SMTP server error: ' ),
87+
/* translators: There is a space after the colon. */
88+
'variable_set' => __( 'Cannot set or reset variable: ' ),
89+
);
90+
$this->language = $error_strings;
91+
return true;
92+
}
93+
}

src/wp-includes/pluggable.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array()
251251
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
252252
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
253253
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
254-
$phpmailer = new PHPMailer\PHPMailer\PHPMailer( true );
254+
require_once ABSPATH . WPINC . '/class-wp-phpmailer.php';
255+
$phpmailer = new WP_PHPMailer( true );
255256

256257
$phpmailer::$validator = static function ( $email ) {
257258
return (bool) is_email( $email );
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<?php
2-
return ['domain'=>NULL,'plural-forms'=>'nplurals=2; plural=n != 1;','messages'=>['html_lang_attribute'=>'de-DE','text directionltr'=>'ltr','number_format_decimal_point'=>',','number_format_thousands_sep'=>'.','Update %s now'=>'Jetzt %s aktualisieren','[%1$s] Confirm Action: %2$s'=>'[%1$s] Aktion bestätigen: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Löschauftrag ausgeführt','[%s] Personal Data Export'=>'[%s] Export personenbezogener Daten'],'language'=>'de_DE','x-generator'=>'Poedit 2.2.1'];
2+
return ['domain'=>NULL,'plural-forms'=>'nplurals=2; plural=n != 1;','messages'=>['html_lang_attribute'=>'de-DE','text directionltr'=>'ltr','number_format_decimal_point'=>',','number_format_thousands_sep'=>'.','Update %s now'=>'Jetzt %s aktualisieren','[%1$s] Confirm Action: %2$s'=>'[%1$s] Aktion bestätigen: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Löschauftrag ausgeführt','[%s] Personal Data Export'=>'[%s] Export personenbezogener Daten', 'You must provide at least one recipient email address.'=>'Du musst mindestens eine Empfänger-E-Mail-Adresse angeben.'],'language'=>'de_DE','x-generator'=>'Poedit 2.2.1'];
102 Bytes
Binary file not shown.

tests/phpunit/data/languages/de_DE.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ msgstr "[%s] Löschauftrag ausgeführt"
5757
#: wp-admin/includes/file.php:2415
5858
msgid "[%s] Personal Data Export"
5959
msgstr "[%s] Export personenbezogener Daten"
60+
61+
#: wp-includes/class-wp-phpmailer.php:71
62+
msgid "You must provide at least one recipient email address."
63+
msgstr "Du musst mindestens eine Empfänger-E-Mail-Adresse angeben."

tests/phpunit/includes/bootstrap.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,6 @@
246246
$multisite = $multisite || ( defined( 'WP_TESTS_MULTISITE' ) && WP_TESTS_MULTISITE );
247247
$multisite = $multisite || ( defined( 'MULTISITE' ) && MULTISITE );
248248

249-
// Override the PHPMailer.
250-
require_once __DIR__ . '/mock-mailer.php';
251-
$phpmailer = new MockPHPMailer( true );
252-
253249
if ( ! defined( 'WP_DEFAULT_THEME' ) ) {
254250
define( 'WP_DEFAULT_THEME', 'default' );
255251
}
@@ -305,6 +301,11 @@ function wp_tests_options( $value ) {
305301
// Load WordPress.
306302
require_once ABSPATH . 'wp-settings.php';
307303

304+
// Override the PHPMailer.
305+
require_once __DIR__ . '/mock-mailer.php';
306+
307+
$phpmailer = new MockPHPMailer( true );
308+
308309
// Delete any default posts & related data.
309310
_delete_all_posts();
310311

tests/phpunit/includes/mock-mailer.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
<?php
2+
/**
3+
* Mock PHPMailer class for testing.
4+
*
5+
* @package WordPress
6+
* @subpackage UnitTests
7+
* @since 4.5.0
8+
*/
9+
210
require_once ABSPATH . 'wp-includes/PHPMailer/PHPMailer.php';
311
require_once ABSPATH . 'wp-includes/PHPMailer/Exception.php';
12+
require_once ABSPATH . 'wp-includes/class-wp-phpmailer.php';
413

5-
class MockPHPMailer extends PHPMailer\PHPMailer\PHPMailer {
14+
/**
15+
* Test class extending WP_PHPMailer.
16+
*
17+
* @since 4.5.0
18+
*/
19+
class MockPHPMailer extends WP_PHPMailer {
620
public $mock_sent = array();
721

822
public function preSend() {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Unit tests covering PHPMailer translations.
4+
*
5+
* @package WordPress
6+
* @subpackage PHPMailer
7+
* @since 6.8.0
8+
*/
9+
10+
/**
11+
* Class Test_PHPMailer_Translations.
12+
*
13+
* Provides tests for PHPMailer translations.
14+
*
15+
* @group mail
16+
* @group i18n
17+
* @group l10n
18+
*
19+
* @since 6.8.0
20+
*/
21+
class Test_PHPMailer_Translations extends WP_UnitTestCase {
22+
/**
23+
* Tests that PHPMailer error message translation works as expected.
24+
*
25+
* @ticket 23311
26+
*/
27+
public function test_missing_recipient_error_message_should_be_translated() {
28+
reset_phpmailer_instance();
29+
30+
$is_switched = switch_to_locale( 'de_DE' );
31+
32+
$phpmailer = tests_retrieve_phpmailer_instance();
33+
$phpmailer->setFrom( '[email protected]' );
34+
35+
try {
36+
$phpmailer->send();
37+
$this->fail( 'Expected exception was not thrown' );
38+
} catch ( PHPMailer\PHPMailer\Exception $e ) {
39+
$error_message = $e->getMessage();
40+
} finally {
41+
if ( $is_switched ) {
42+
restore_previous_locale();
43+
}
44+
}
45+
46+
$this->assertSame(
47+
'Du musst mindestens eine Empfänger-E-Mail-Adresse angeben.',
48+
$error_message,
49+
'Error message is not translated as expected'
50+
);
51+
}
52+
53+
/**
54+
* Test that PHPMailer error message keys are consistent across implementations.
55+
*
56+
* @ticket 23311
57+
*/
58+
public function test_all_error_message_keys_should_be_translated() {
59+
reset_phpmailer_instance();
60+
61+
$phpmailer = new PHPMailer\PHPMailer\PHPMailer();
62+
$wp_phpmailer = tests_retrieve_phpmailer_instance();
63+
64+
$this->assertEqualSets( array_keys( $phpmailer->GetTranslations() ), array_keys( $wp_phpmailer->GetTranslations() ) );
65+
}
66+
}

0 commit comments

Comments
 (0)