Как вывести переводимый текст с HTML ссылкой в WordPress
Я относительно новичок в WordPress, когда дело касается "глубокой" кастомизации.
Сейчас у меня есть такая функция:
esc_html_e( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров. Пожалуйста, свяжитесь с нами по email info@whitesandsamuiresort.com.', 'awebooking' );
И текст отображается нормально.
Но какую функцию мне нужно использовать, если я хочу добавить a
href
(HTML ссылку)?
Я хочу получить такой текст:
Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.
Пожалуйста, свяжитесь с нами через
<a href="http://www.whitesandsamuiresort.com/contact-us/">страницу контактов</a>
или по emailinfo@whitesandsamuiresort.com
.
У меня проблема с одновременной поддержкой перевода (интернационализации/локализации) и экранированием HTML ссылок.

Поскольку функция esc_html_e
экранирует HTML-ссылки (и, следовательно, будет отображать HTML-якорь как обычный текст), вам нужно разделить текст и экранировать не-HTML часть с помощью esc_html_e
или esc_html__
, а часть с HTML-ССЫЛКОЙ выводить без экранирования HTML.
Метод-1 (для понимания):
Можно сделать это по частям, например так:
esc_html_e( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.', 'text-domain' );
echo "<br><br>";
printf(
esc_html__( '%1$s %2$s', 'text-domain' ),
esc_html__( 'Пожалуйста, свяжитесь с нами через нашу', 'text-domain' ),
sprintf(
'<a href="%s">%s</a>',
esc_url( 'http://www.example.com/contact-us/' ),
esc_html__( 'Страницу контактов', 'text-domain' )
)
);
printf(
' или <a href="%s">%s</a>',
esc_url( 'mailto:info@example.com', array( 'mailto' ) ),
esc_html__( 'Электронную почту', 'text-domain' )
);
Метод-2 (для понимания):
Очевидно, что в разных языках порядок текстов может отличаться, поэтому для предоставления переводчикам большей гибкости (в плане экранирования и порядка текста), можно сделать следующим образом:
printf(
esc_html__( '%1$s%2$s%3$s%4$s%5$s', 'text-domain' ),
esc_html__( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.', 'text-domain' ),
nl2br( esc_html__( "\n\n", 'text-domain' ) ),
sprintf(
esc_html__( '%1$s %2$s', 'text-domain' ),
esc_html__( 'Пожалуйста, свяжитесь с нами через нашу', 'text-domain' ),
sprintf(
'<a href="%s">%s</a>',
esc_url( 'http://www.example.com/contact-us/' ),
esc_html__( 'Страницу контактов', 'text-domain' )
)
),
esc_html__( ' или ', 'text-domain' ),
sprintf(
'<a href="%s">%s</a>',
esc_url( 'mailto:info@example.com', array( 'mailto' ) ),
esc_html__( 'Электронную почту', 'text-domain' )
)
);
Такой подход позволяет:
Экранировать все необходимые переведенные тексты.
Разрешить HTML для ссылок, email (с синтаксисом
mailto:
) и т.д.Дать переводчикам возможность менять порядок текста для разных языков. Используется нотация подстановки аргументов (
%1$s
,%2$s
и т.д.), чтобы переводчики могли изменить порядок переведенного текста при необходимости.
Метод-3 (Обновленный и Рекомендуемый):
Как правильно отметил @shea, Метод-2 работает хорошо, но может быть сложным для переводчиков при поддержке разных языков. Поэтому нам нужно решение, которое:
Сохраняет предложения целыми (не разбивает их).
Обеспечивает правильное экранирование.
Предоставляет возможность менять порядок ссылок на контакты и email (или что-то подобное) в переведенном предложении.
Чтобы избежать сложностей метода-2, следующее решение сохраняет переводимые предложения целыми и одновременно обеспечивает правильное экранирование URL и подстановку аргументов (дополнительные пояснения в комментариях к КОДУ):
// пример URL контактов (может быть из ненадежного источника, например, пользовательского ввода)
$contact_url = 'http://www.example.com/contact-us/';
// экранирование $contact_url
$contact_url = esc_url( $contact_url );
// пример email для контактов (может быть из ненадежного источника)
$contact_email = 'info@example.com';
// экранирование, санитизация и скрытие $contact_email.
// Да, вам все еще может потребоваться санитизация и экранирование email при использовании функции antispambot()
$contact_email = esc_url( sprintf( 'mailto:%s', antispambot( sanitize_email( $contact_email ) ) ), array( 'mailto' ) );
esc_html_e( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.', 'text-domain' );
echo "<br><br>";
printf(
esc_html__( 'Пожалуйста, свяжитесь с нами через нашу %1$s или по %2$s.', 'text-domain' ),
sprintf(
'<a href="%s">%s</a>',
$contact_url,
esc_html__( 'Страницу контактов', 'text-domain' )
),
sprintf(
'<a href="%s">%s</a>',
$contact_email,
esc_html__( 'Электронной почте', 'text-domain' )
)
);
Такой подход дает переводчикам два полных предложения и два отдельных слова для перевода. Таким образом, переводчикам нужно будет заботиться только о следующих простых строках (в то время как КОД позаботится обо всем остальном):
esc_html_e( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.', 'text-domain' );
// ...
esc_html__( 'Пожалуйста, свяжитесь с нами через нашу %1$s или по %2$s', 'text-domain' )
// ...
esc_html__( 'Страницу контактов', 'text-domain' )
// ...
esc_html__( 'Электронной почте', 'text-domain' )
Вот и все — простая структура, которая также обеспечивает правильное экранирование и подстановку аргументов.
Подробнее о интернационализации тем и интернационализации плагинов.

Я не совсем уверен, насколько хорошо второй пример будет работать для всех языков, особенно для тех, где структура предложения отличается от английского. Не лучше ли не разбивать предложения или не разделять слова?

@shea Я не совсем понимаю ваш вопрос, но, например, даже эта строка esc_html__( '%1$s%2$s%3$s%4$s%5$s', 'text-domain' )
может быть изменена на %3$s%2$s%1$s%4$s%5$s
в переводе. Такая гибкость порядка должна покрывать различные структуры языков.

Не во всех языках есть одно слово для "или", или фразы типа "Пожалуйста, свяжитесь с нами через <ссылку>". Я говорю, что лучшим методом было бы предоставлять полные предложения или фразы для перевода, а затем позволять переводчикам делать то, что необходимо. Метод замены аргументов кажется излишне громоздким и неэффективным

Я считаю, что ответ от @Fayaz очень хороший; он определенно на правильном пути, предлагая использовать sprintf()
вместе с переводами для включения HTML-разметки и ссылок.
Однако я не считаю хорошей идеей разбивать предложения на части для перевода, так как во многих языках структура предложений отличается от английской. Разделяя текст на отдельные слова, мы теряем контекст, в котором должно переводиться конкретное слово, что может привести к неоднозначности и ошибкам перевода.
Вместо этого я рекомендую переводить фразы и предложения целиком, а затем, где это необходимо, использовать sprintf
или другие функции экранирования.
Для вашего текста первую часть можно просто перевести с помощью esc_html_e
:
esc_html_e( 'Уважаемый гость, по предоставленным данным мы не смогли найти свободных номеров на данный момент. ', 'text-domain' );
Вторая часть немного сложнее. Предположу, что URL страницы контактов и email вы получаете из базы данных, возможно, используя get_the_permalink()
, get_option()
или другую функцию. Допустим, они хранятся в переменных $contact_page_url
и $contact_email
.
Сначала переведем строку без ссылок. Обратите внимание, что на этом этапе мы используем __()
без экранирования — оно будет применено позже.
__( 'Пожалуйста, свяжитесь с нами через <a href="%1$s">страницу контактов</a> или по email %2$s.', 'text-domain' );
Это дает переводчику свободу в формулировке предложения и размещении ссылки и email там, где это необходимо.
Затем используем sprintf()
для вставки URL ссылки и email. Здесь мы применяем esc_url()
для экранирования URL и antispambot()
для кодирования email, чтобы обеспечить минимальную защиту от спам-ботов:
$text = sprintf(
__( 'Пожалуйста, свяжитесь с нами через <a href="%1$s">страницу контактов</a> или по email %2$s.', 'text-domain' ),
esc_url( $contact_page_url ),
sprintf( '<a href="mailto:%1$s">%1$s</a>', antispambot( $contact_email ) )
);
Наконец, вместо esc_html()
мы используем wp_kses()
, чтобы разрешить только элементы ссылок в переведенном HTML:
echo wp_kses( $text, array( 'a' => array( 'href' => array() ) ) );
Вот и все! Итоговый код:
esc_html_e( 'Уважаемый гость, по предоставленным данным мы не смогли найти свободных номеров на данный момент. ', 'text-domain' );
$text = sprintf(
__( 'Пожалуйста, свяжитесь с нами через <a href="%1$s">страницу контактов</a> или по email %2$s.', 'text-domain' ),
esc_url( $contact_page_url ),
sprintf( '<a href="mailto:%1$s">%1$s</a>', antispambot( $contact_email ) )
);
echo wp_kses( $text, array( 'a' => array( 'href' => array() ) ) );

Мне нравится ваша идея с wp_kses
. Однако есть два момента (1) переводчик может добавить дополнительную ссылку в переводе, и ваш КОД этого не остановит. (2) переводчику придётся предоставить HTML
, т.е. <a href="%1$s">..</a>
в рамках перевода. Возможно, вы этого не хотите, так как они могут допустить ошибки даже в простейшем HTML-теге <a>
.

Справедливо, @Fayaz, это веские аргументы. Если вы считаете их наиболее важными, то, возможно, стоит использовать другое решение :)

+1 за хорошие замечания. Поскольку в вашем решении есть эти проблемы, я обновил своё решение (Метод-3) на этот раз с более простой структурой.
