9

I am somewhat new in WordPress if it is about "deeper" customization.

Now I am having this function:

esc_html_e( 'Dear Guest, with Your information we not find any room at the moment. Please contact us per email [email protected].', 'awebooking' );

And the text is showing so far.

But what function do I need to use when I like to add an a href (HTML link)?

So I like to have this text:

Dear Guest, with Your information we not find any room at the moment.

Please contact us on our <a href="http://www.whitesandsamuiresort.com/contact-us/">contact page</a> or per email [email protected].

I'm having problem supporting translation (internationalization / localization) and escaping HTML links at the same time.

3 Answers 3

9

Since esc_html_e will escape HTML link (hence will show the HTML anchor as plain text), you'll need to segment the text and escape the non-HTML part with esc_html_e or esc_html__, and print the HTML LINK part without HTML escaping.

Method-1 (just for your understanding):

You may do it in parts, like this:

esc_html_e( 'Dear Guest, with Your information we could not find any room at the moment.', 'text-domain' );
echo "<br><br>";

printf(
    esc_html__( '%1$s %2$s', 'text-domain' ),
    esc_html__( 'Please contact us on our', 'text-domain' ),
    sprintf(
        '<a href="%s">%s</a>',
        esc_url( 'http://www.example.com/contact-us/' ),
        esc_html__( 'Contact Page', 'text-domain' )
    )
);

printf(
    ' or <a href="%s">%s</a>',
    esc_url( 'mailto:[email protected]', array( 'mailto' ) ),
    esc_html__( 'Email', 'text-domain' )
);

Method-2 (just for your understanding):

Obviously, different languages will have different ordering of texts, so to give translators more flexibility (with escaping and ordering of text), you may do it in the following way instead:

printf(
    esc_html__( '%1$s%2$s%3$s%4$s%5$s', 'text-domain' ),
    esc_html__( 'Dear Guest, with Your information we could not find any room at the moment.', 'text-domain' ),
    nl2br( esc_html__( "\n\n", 'text-domain' ) ),
    sprintf(
        esc_html__( '%1$s %2$s', 'text-domain' ),
        esc_html__( 'Please contact us on our', 'text-domain' ),
        sprintf(
            '<a href="%s">%s</a>',
            esc_url( 'http://www.example.com/contact-us/' ),
            esc_html__( 'Contact Page', 'text-domain' )
        )
    ),
    esc_html__( ' or ', 'text-domain' ),
    sprintf(
        '<a href="%s">%s</a>',
        esc_url( 'mailto:[email protected]', array( 'mailto' ) ),
        esc_html__( 'Email', 'text-domain' )
    )
);

This way of doing it will:

  1. Escape all the necessary translated texts.

  2. Allow the HTML for link, email (with mailto: syntax) etc.

  3. Allow translators to have all sorts of different ordering of texts for different languages. The argument swapping notation (%1$s, %2$s etc.) is used so that the translators may reorder the translated text where needed.


Method-3 (Updated & Recommended):

As @shea rightly pointed out, Method-2 above works fine, but it may be difficult for the translators to add support for different languages. So we need a solution that:

  1. Keeps the sentences intact (doesn't break sentences).

  2. Does proper escaping.

  3. Provides ways to have different ordering for contact & email links (or anything similar) within the translated sentence.

So to avoid the complication of method-2, the solution below keeps the translatable sentences intact and does the proper URL escaping & argument swapping at the same time (more notes within CODE comments):

// sample contact url (may be from an unsafe place like user input)
$contact_url = 'http://www.example.com/contact-us/';
// escaping $contact_url
$contact_url = esc_url( $contact_url );

// sample contact email (may be from an unsafe place like user input)
$contact_email = '[email protected]';
// escaping, sanitizing & hiding $contact_email.
// Yes, you may still need to sanitize & escape email while using antispambot() function
$contact_email = esc_url( sprintf( 'mailto:%s', antispambot( sanitize_email( $contact_email ) ) ), array( 'mailto' ) );

esc_html_e( 'Dear Guest, with Your information we could not find any room at the moment.', 'text-domain' );
echo "<br><br>";

printf(
    esc_html__( 'Please contact us on our %1$s or per %2$s.', 'text-domain' ),
    sprintf(
        '<a href="%s">%s</a>',
        $contact_url,
        esc_html__( 'Contact Page', 'text-domain' )
        ),
    sprintf(
        '<a href="%s">%s</a>',
        $contact_email,
        esc_html__( 'Email', 'text-domain' )
        )
    );

This way of doing it will give translators two full sentences & two separate words to translate. So a translators will only have to worry about the following simple lines (while the CODE takes care of the rest):

esc_html_e( 'Dear Guest, with Your information we could not find any room at the moment.', 'text-domain' );
// ...
esc_html__( 'Please contact us on our %1$s or per %2$s', 'text-domain' )
// ...
esc_html__( 'Contact Page', 'text-domain' )
// ...
esc_html__( 'Email', 'text-domain' )

That's it, simple structure and does proper escaping and argument swapping as well.


Read more about internationalization for themes & internationalization for plugins.

3
  • I'm not sure sure how well the second example would work for all languages, such as those that use a different sentence structure to English. Wouldn't it be better to not split sentences or separate out words?
    – shea
    Commented Mar 23, 2017 at 7:07
  • @shea I'm not sure what you are asking, but, for example even this line esc_html__( '%1$s%2$s%3$s%4$s%5$s', 'text-domain' ) is changeable to %3$s%2$s%1$s%4$s%5$s with a translation. This flexibility of ordering should cover different language structures.
    – Fayaz
    Commented Mar 23, 2017 at 7:17
  • Not all languages have a single word for "or", or would phrase things like "Please contact us on our <link>". I am saying a better method would be to provide full sentences or phrases to translate, and then let translators do what is necessary to translate this. The argument swapping method seems overly messy and ineffective
    – shea
    Commented Mar 23, 2017 at 12:31
2

I think the answer by @Fayaz is very good; they are definitely on the right path in that you should use sprintf() wrapped around translations to include things such as HTML and links.

However, I don't believe that it is a good idea to split sentences into parts for translation, as many languages have different sentence structures that are not at all compatible with English. By splitting out individual words, a great deal of the context in which a specific word is translated is lot, potentially leading to ambiguity and translation errors.

Instead, I recommend translating phrases and sentences as a whole, and then using sprintf or different esccaping functions where appropriate.

For your text, the first part can simply be translated by itself with esc_html_e:

esc_html_e( 'Dear Guest, with your information we not find any room at the moment. ', 'text-domain' );

The second part is slightly harder. Now, I assume that you are retrieving the page URL and email from the database somehow, probably using get_the_permalink() or get_option() or some other function. So I'm going to assume they are stored in $contact_page_url and $contact_email variables.

The first step is to translate the string, without the links. Note that we need to use __() without escaping at this point - that will come later.

__( 'Please contact us on our <a href="%1$s">contact page</a> or by email %2$s.', text-domain' );

This way the translator is free to word the sentence as necessary and place the link URL and email where they need to.

The next step is to use sprintf() to insert the link URL and email. Note that we use esc_url() here to escape the URL, and also the antispambot() to encode the email address to provide a minimal protection against scrapers:

$text = sprintf(
    __( 'Please contact us on our <a href="%1$s">contact page</a> or by email %2$s.', 'text-domain' );
    esc_url( $contact_page_url ),
    sprintf( '<a href="mailto:%1$s">%1$s</a>', antispambot( $contact_email ) );
);

Finally, rather than using esc_html() , we need to use wp_kses() to only allow link elements in the translated HTML:

echo wp_kses( $text, array( 'a' => array( 'href'  => array() ) ) );

And that's it! The final code is:

esc_html_e( 'Dear Guest, with your information we not find any room at the moment. ', 'text-domain' )

$text = sprintf(
    __( 'Please contact us on our <a href="%1$s">contact page</a> or by 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() ) ) );
3
  • I like your wp_kses idea. However, there are two points (1) a translator may add additional link within the translation and your CODE will not stop that. (2) a translator will have to provide HTML, i.e. <a href="%1$s">..</a> within the translation. You may not want that, as they may make mistakes in properly handling even the simplest HTML <a> tag.
    – Fayaz
    Commented Apr 1, 2017 at 13:57
  • 1
    Fair enough @Fayaz, those are valid points. If you think they're the most important, than you might want to use a different solution :)
    – shea
    Commented Apr 2, 2017 at 3:01
  • +1 for some good points though. Since you solution has those issues, I've updated my solution (Method-3) with simpler structure this time.
    – Fayaz
    Commented Apr 2, 2017 at 6:10
-2

If you are using esc_html_e then this function simply says it escapes the HTML tags. So you can't be able to use HTML tags inside this esc_html_e.

1
  • 3
    the OP explicitly asks what function he should use, he knows it is the wrong one ;) Commented Mar 18, 2017 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.