Score:0

How to alter page title "Create X" to "Add X" for all X?

kz flag

In Drupal 7, there was drupal_get_title()and drupal_set_title(). They're history.

I want to alter the page title "Create X" to "Add X" for all X, and I think hook_preprocess_page_title() in my .theme-file is the right place to do it. (If you disagree, please say so.)

I.e. I want it to be 'Add X' for any 'X' when it originally was 'Create X'. For example: 'Create article' should become 'Add article'. And if the title is 'Bar Article', I should be left as is.

Basically, I want to sniff the title string and if it starts with 'Create ', alter it to 'Add '. This is so far I've gotten, and this is obviously incomplete:

/**
 * Implements hook_preprocess_page_title().
 */

First attempt:

function mytheme_preprocess_page_title(&$variables) {
  $request     = \Drupal::request();
  $route_match = \Drupal::routeMatch();
  $title       = \Drupal::service('title_resolver')->getTitle($request, $route_match->getRouteObject());
}

Second attempt, based upon suggestion by 4k4:

function mytheme_preprocess_page_title(&$variables) {
  $title = $variables['title'];
  if ($title instanceof TranslatableMarkup) {
    $title = $title->getUntranslatedString();
    $title = str_replace('Create', 'Add', $title);
    $variables['title'] = t($title);
  }
  else {
    $variables['title'] = Markup::create(str_replace('Create', 'Add', $title));
  }  
}

The titles I want to change come from Drupal core and they are translatable, so its the instanceof TranslatableMarkup branch that gets executed, but I agree with having a fallback in case they're not, is a good idea.

However, while a good start, this is not a complete solution. It changes the rendered title of the add article form from "Create article" to "Add @name".

enter image description here

I've upvoted the answer by 4k4, as it goes much further towards a solution than my first attempt, but it is still not complete.

I don't want to use the String Overrides module for this for several reasons, mostly because this theme should work across translations without requiring extra configuration when deployed.

No Sssweat avatar
ua flag
Untested, but you could try solving it via template file. [page-title.html.twig](https://api.drupal.org/api/drupal/core%21modules%21system%21templates%21page-title.html.twig/8.2.x) change `{{ title }}` to `{{ title|replace({'Create X': "Add X"}) }}`
Free Radical avatar
kz flag
Thanks @NoSssweat! Yes, it works. I still hope for an answer explaining how to do it in PHP. This will not work with translated titles.
No Sssweat avatar
ua flag
Hmm try `|replace({'Create X'|t: "Add X"|t})`
Score:1
cn flag

I want to alter the page title "Create X" to "Add X" for all X, and I think hook_preprocess_page_title() in my .theme-file is the right place to do it. (If you disagree, please say so.)

You can only change the title displayed in the body tag, not the title metatag in the html tag.

$title = \Drupal::service('title_resolver')->getTitle($request, $route_match->getRouteObject());

This gets you only the title from the route definition or callback, not a title set by #title in the rendered output.

$title is not a string. The string is somewhere in there, but how do I extract it to see what it starts with?

It's a markup object. Either a plain one or a translatable one. See How do you edit content of a Drupal\Core\Render\Markup instance via a preprocess function in Drupal 8? or How do I change text on the submission button in the node form?

How do I actually change it in the context of a preprocess hook. I.e. what is equivalent of drupal_set_title() in this context?

In a preprocess hook you can only change variables which the template is displaying, in this case:

$variables['title'] = 'Foo title';

If it contains HTML markup to avoid autoescaping:

$variables['title'] = \Drupal\Core\Render\Markup::create('<i>Foo</i> title');

If it is translatable:

$variables['title'] = t('Foo title');

Code example:

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Render\Markup;
    
/**
 * Implements hook_preprocess_page_title().
 */
function mymodule_preprocess_page_title(&$variables) {
  $title = $variables['title'];
  if ($title instanceof TranslatableMarkup) {
    $title_untranslated = $title->getUntranslatedString();
    $title_replaced = str_replace('Create', 'Add', $title_untranslated);
    $variables['title'] = t($title_replaced, $title->getArguments());
  }
}

  
Free Radical avatar
kz flag
I want it translatable. However, I don't want the title to be 'Foo title', I want it to be 'Add X' for any 'X' when it originally was 'Create X'. (e.g. 'Create article' should become 'Add article'. And if the title is 'Bar Article', I don't want to do anything. I am going to take a closer look at the links you provide about markup objects, but I still haven't figured out how to figure out if the string starts with 'Create'.
4uk4 avatar
cn flag
OK, so you want to replace the original english version and then translate the replaced version. This is a bit more complicated because the translatable markup returns as string the already translated string and you have to use a method instead of casting it to a string. I add a code example, also with a fallback for the plain markup which works with string casting.
4uk4 avatar
cn flag
I've added the t() arguments for the placeholder. Also I've removed the fallback because the title variable can contain different content, also entire render arrays.
Free Radical avatar
kz flag
Great! The last incarnation works.
4uk4 avatar
cn flag
BTW, the screenshot you've added is the node.add route. You could replace this title callback in the route definition and it works also in breadcrumbs and metatags: https://api.drupal.org/api/drupal/core%21modules%21node%21src%21Controller%21NodeController.php/function/NodeController%3A%3AaddPageTitle/9.2.x
Free Radical avatar
kz flag
Your guidance is much appreciated, but some of it goes way over my head. Could you please tell me where my route definition lives and how to change it (or point me towards a suitable tutorial).
4uk4 avatar
cn flag
See for example (you only have to replace the route name): https://drupal.stackexchange.com/questions/217680/override-user-edit-page-title
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.