0

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.

3
  • 2
    Untested, but you could try solving it via template file. page-title.html.twig change {{ title }} to {{ title|replace({'Create X': "Add X"}) }}
    – No Sssweat
    Commented Aug 7, 2021 at 6:57
  • 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. Commented Aug 7, 2021 at 7:25
  • Hmm try |replace({'Create X'|t: "Add X"|t})
    – No Sssweat
    Commented Aug 7, 2021 at 9:25

1 Answer 1

1

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());
  }
}

  
7
  • 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'. Commented Aug 7, 2021 at 10:00
  • 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
    Commented Aug 7, 2021 at 10:19
  • 1
    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.
    – 4uk4
    Commented Aug 7, 2021 at 11:09
  • Great! The last incarnation works. Commented Aug 7, 2021 at 11:25
  • 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: api.drupal.org/api/drupal/…
    – 4uk4
    Commented Aug 7, 2021 at 11:44

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.