Using Translation Placeholders

Objectives
  • Learn about the three types of placeholders you can use in translatable text.
  • Research the translation service and the class that provides it using the reference documentation.
  • Add a new route and controller and use the service to demonstrate the different replacement placeholder types.

Replacement Placeholders

Placeholders are like variables inside the text you want to translate. They are substituted with the values you provide, but:

  • The replaced values themselves are not translated.
  • The replaced values are processed and, depending on the placeholder type, will be processed and/or sanitized (made safe) before being displayed.

Let's look at some examples.

Type 1: @variable

Usage:

  • Use this placeholder as the default choice for anything displayed on the site.
  • However, do not us it within HTML attributes, JavaScript, or CSS: doing so is a security risk.

Processing:

  • Special characters are converted to HTML entities via HTML::escape

Example 1

Image your source text is something like "You have x new messages", where 'x` can be any number. This is a typical example of a dynamic string

Without placeholders, you would only be able to translate the entire string literally: "Vous avez x messages" in French, which is not very useful.

With the placeholder system you can do the following:

Code
$counter = 17;
$message = t("You have @number messages.", [ '@number' => $counter ]);

Someone who adds a French translation for that string would enter:
'Vous avez @counter messages.'

Example 2

Code
// In this example we present a user's comment in a preview mode and ask them
// if they really want to publish it. We must consider all user input to be UNSAFE, 
// because we never know what malicious users may try to save into our database.

// Retrieve the comment the user just submitted.
// This is a line of imaginary code. The point here is not to show how to retrieve comments,
// but how to deal with potentially unsafe data.
$submitted_comment = retrieve_comment_that_was_just_submitted();

$message = t("Are you sure you want to publish the following comment? @comment", 
  [ '@comment' => $submitted_comment ]
);

Type 2: %variable

Usage

Use when the replacement value is to be wrapped in <em> tags. However, do not us it within HTML attributes, JavaScript, or CSS: doing so is a security risk.

Processing

Other than the addition of <em> tags to display the text as italic, no processing happens.

Example

Code
$title = 'Why most plants are green';
$message = t("Your page %title has been saved.", ['%title' => $title]);

Output: Your page Why most plants are green has been saved.


Type 3: :variable

Usage

Use when dangerous protocols may cause a security issue.

Processing

Special characters are converted to HTML entities via HTML::escape. Dangerous protocols are removed via UrlHelper::stripDangerousProtocols.

Example

Code
// The following example fits into a scenario where someome submits the title and link 
// of a blog post to your Drupal site.

// The code uses 2 imaginary functions to retrieve user-submitted content.
// Again, the focus is not on these 2 functions, but how to turn them into a link in a safe
// way, knowing you should never trust user-submitted data.

$link_text = retrieve_blog_title();
$link_address = retrieve_blog_link;

$link = t('<a href=":url">@link_text</a>', [
  ':url' => $link_address,
  '@link_text' => $link_text,
]);

Activity

In this task you will create a new controller and route to demonstrate the use of the three types of placeholder in the t() function.

  • Add a new '/translation' route linked to a controller method called TranslationDemo.
  • Add a new TranslationController with a TranslationDemo method.
  • Make the method do the following:
  • Create 3 variables ($msg1, $msg2, $msg3).
  • Use the t() function to assign a value to each of the three variables, using a @ placeholder for the first, a % placeholder for the second, and a : placeholder for the third
  • Print the 3 messages on screen.
  • At the end of your new controller method, you can combine the three message variables and return them like this:
return [
 '#markup' => "<p>$msg1</p><p>$msg2</p><p>$msg3</p>",
];
Hide hints
show hints

Activity

  • Add a new /placeholders route.
  • Add a new method to the TranslationController
  • This new method should:
    • Use t() to translate a dynamic string that says "You have x unread messages and y read messages", replacing x and y with appropriate placeholders.
    • Return the translated message.
  • You can use as many placeholders as you want, as long as you provide a value for each of the placeholders.
  • The " Type 3: :variable" section in this unit demonstrates how to work with more than one placeholder. In that example both placeholders were of a different type, but you can use placeholders of the same type as well.
Hide hints
show hints

Activity

  • Which of the three placeholders is the most common one - the one you're likely to use most often?
Hide hints
show hints

Activity

  • Why is it important that user-provided input gets sanitized (also referred to as escaped)?
Hide hints
show hints

Summary

  • t() offers three types of replacement placeholders.
  • Each type of placeholder results in different processing and/or sanitisation of the provided value.
  • All user-generated input must be sanitised before you attempt to display it. If you don't, you're creating a security issue.
  • You can use as many placeholders as you want in a translatable string.