Как вывести переводимый текст с HTML ссылкой в WordPress

18 мар. 2017 г., 06:28:20
Просмотры: 13.5K
Голосов: 9

Я относительно новичок в WordPress, когда дело касается "глубокой" кастомизации.

Сейчас у меня есть такая функция:

esc_html_e( 'Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров. Пожалуйста, свяжитесь с нами по email info@whitesandsamuiresort.com.', 'awebooking' );

И текст отображается нормально.

Но какую функцию мне нужно использовать, если я хочу добавить a href (HTML ссылку)?

Я хочу получить такой текст:

Уважаемый гость, по предоставленной информации мы не смогли найти свободных номеров.

Пожалуйста, свяжитесь с нами через <a href="http://www.whitesandsamuiresort.com/contact-us/">страницу контактов</a> или по email info@whitesandsamuiresort.com.

У меня проблема с одновременной поддержкой перевода (интернационализации/локализации) и экранированием HTML ссылок.

0
Все ответы на вопрос 3
3

Поскольку функция 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' )
    )
);

Такой подход позволяет:

  1. Экранировать все необходимые переведенные тексты.

  2. Разрешить HTML для ссылок, email (с синтаксисом mailto:) и т.д.

  3. Дать переводчикам возможность менять порядок текста для разных языков. Используется нотация подстановки аргументов (%1$s, %2$s и т.д.), чтобы переводчики могли изменить порядок переведенного текста при необходимости.


Метод-3 (Обновленный и Рекомендуемый):

Как правильно отметил @shea, Метод-2 работает хорошо, но может быть сложным для переводчиков при поддержке разных языков. Поэтому нам нужно решение, которое:

  1. Сохраняет предложения целыми (не разбивает их).

  2. Обеспечивает правильное экранирование.

  3. Предоставляет возможность менять порядок ссылок на контакты и 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' )

Вот и все — простая структура, которая также обеспечивает правильное экранирование и подстановку аргументов.


Подробнее о интернационализации тем и интернационализации плагинов.

18 мар. 2017 г. 12:53:29
Комментарии

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

shea shea
23 мар. 2017 г. 09:07:24

@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 Fayaz
23 мар. 2017 г. 09:17:01

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

shea shea
23 мар. 2017 г. 14:31:47
3

Я считаю, что ответ от @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() ) ) );
26 мар. 2017 г. 07:14:42
Комментарии

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

Fayaz Fayaz
1 апр. 2017 г. 16:57:17

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

shea shea
2 апр. 2017 г. 06:01:21

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

Fayaz Fayaz
2 апр. 2017 г. 09:10:20
1
-2

Если вы используете функцию esc_html_e, то она просто экранирует HTML-теги. Таким образом, вы не сможете использовать HTML-теги внутри этой функции esc_html_e.

18 мар. 2017 г. 07:28:45
Комментарии

OP явно спрашивает, какую функцию ему следует использовать, он понимает, что текущая выбрана неправильно ;)

Mark Kaplun Mark Kaplun
18 мар. 2017 г. 09:50:33