Hello,

I am creating a new site in 2 languages, English and Dutch. Dutch is my default language.

I am working with pathauto to create paths automatically in this way, that the path is: [Menu-title]\[Menu-parents]\[Page-title].

This is my pattern that I use.
[node:menu-link:menu:name]/[node:menu-link:parents]/[node:menu-link]

The menu's are translated.

In my default language, everything works fine. Than I hit the translate button. I place the page in the same menu because the menu is translated. Everything works fine, except 1 thing --> [node:menu-link:menu:name]. The titel is not translated.

For example

Dutch: ?q=nl/nieuwsberichten/basisartikelpagina
English: ?q=en/nieuwsberichten/basicpagearticle

In the english path, I would like to see the translated version: ?q=en/news/basicpagearticle

Can someone help me.
I am using
Token 7.x-1.4
Pathauto 7.x-1.2

Comments

carlovdb’s picture

Does anybody have an idea how to solve this?

joel_osc’s picture

This may help...I am using the pathauto token [node:menu-link:parents:join-path]/[node:title] for my path. But, yes in a second language it does not work. I just grabbed the join-path token code from pathauto and changed it to do the proper translation which for me uses entity translation.

/**
 * Implements hook_token_info().
 */
function mymodule_token_info() {
  $info = array();

  $info['tokens']['array']['join-et-path'] = array(
    'name' => t('Joined path translated'),
    'description' => t('The array values each cleaned by Pathauto, translated and then joined with the slash into a string that resembles an URL.'),
  );

  return $info;
}

/**
 * Implements hook_tokens().
 */
function mymodule_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'array' && !empty($data['array'])) {
    $array = $data['array'];

    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'join-et-path':
          module_load_include('inc', 'pathauto');
          $values = array();
          foreach (element_children($array) as $key) {
            $name = implode(':', array('menu', 'item', $key, 'title'));
            $value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key];
            $value = i18n_string($name, $value, array('langcode' =>'fr'));
            $value = pathauto_cleanstring($value);
            $values[] = $value;
          }
          $replacements[$original] = implode('/', $values);
          break;
      }
    }
  }

  return $replacements;
}

Note that you should end your token in "-path" so pathauto knows not to strip the slashes out.

jeroent’s picture

Issue summary: View changes

Joel_osc's code worked for me, but I made a little change :

/**
 * Implements hook_token_info().
 */
function mymodule_token_info() {
  $info = array();
  $info['tokens']['array']['join-et-path'] = array(
    'name' => t('Joined path translated'),
    'description' => t('The array values each cleaned by Pathauto, translated and then joined with the slash into a string that resembles an URL.'),
  );
  return $info;
}
/**
 * Implements hook_tokens().
 */
function mymodule_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'array' && !empty($data['array'])) {
    $array = $data['array'];
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'join-et-path':
          module_load_include('inc', 'pathauto');
          $values = array();
          foreach (element_children($array) as $key) {
            $name = implode(':', array('menu', 'item', $key, 'title'));
            $value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key];
            $value = i18n_string($name, $value, array('langcode' => $options['language']->language));
            $value = pathauto_cleanstring($value);
            $values[] = $value;
          }
          $replacements[$original] = implode('/', $values);
          break;
      }
    }
  }
  return $replacements;
}
lmeurs’s picture

Thanks for this solution! To point out the difference between #2 and #3, the first has the language hard set to fr as in:

$value = i18n_string($name, $value, array('langcode' =>'fr'));

where #3 uses the language sent as option:

$value = i18n_string($name, $value, array('langcode' => $options['language']->language));

I encountered a problem when using Pathauto's bulk generate at admin/config/search/path/update_bulk from another language than the default: the paths are generated in the language in which bulk generate is called from.

Say the default language is English and Dutch (nl) is set as the second language. When using bulk generate from nl/admin/config/search/path/update_bulk (note the Dutch langcode prefix), the $data['array'][$mlid] variable that hook_tokens() receives has already been localized, so is in Dutch. This localized value is being used as the fallback value by i18n_string(): if no translation can be found, this localized fallback value is used. But the value in the default language is not stored in an i18n db table, so cannot be found by i18n_string(), so the localized version is being used!

My fix: if the page request's language differs from the site's default, load the unlocalized menu link title and use this value as default.

EDIT: My fix is to load the menu item and use it's unlocalized title as default value. I also implemented an alternative for the [node:menu-link:title] token, namely [node:menu-link:et-title].

NB: I changed the token's specifier from join-et-path to et-join-path.

/**
 * Implements hook_token_info().
 *
 * PROBLEM: Pathauto does not translate paths well using Entity Translation and
 *          the [node:menu-link:parents:join-path]/[node:menu-link:title]
 *          tokens.
 * SOLUTION: Use custom tokens.
 * SOLUTION URL: drupal.org/node/1892442
 *
 * NB: Note that you should end path tokens with "-path" so pathauto knows not
 * to strip the slashes.
 */
function YOURMODULE_token_info() {
  $info = array();

  // Define custom token: "et-title" (based on original "title").
  // Usage: [node:menu-link:et-title]
  $info['tokens']['menu-link']['et-title'] = array(
    'name' => t('Title (Entity Translation compatible)'),
    'description' => t('The title of the menu link (Entity Translation compatible).'),
  );

  // Define custom token: "et-join-path" (based on original "join-path").
  // Usage: [node:menu-link:parents:et-join-path]/[node:menu-link:et-title]
  $info['tokens']['array']['et-join-path'] = array(
    'name' => t('Joined path (Entity Translation compatible)'),
    'description' => t('The array values each cleaned by Pathauto and then joined with the slash into a string that resembles an URL (Entity Translation compatible).'),
  );

  return $info;
}

/**
 * Implements hook_tokens().
 */
function YOURMODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();

  // Define sanitize.
  $sanitize = !empty($options['sanitize']);

  // If type is menu-link and data has been received.
  if ($type == 'menu-link' && !empty($data[$type])) {
    $menu_link = $data[$type];

    // Iterate trough tokens.
    foreach ($tokens as $name => $original) {
      // Switch: check token names.
      switch ($name) {

        // If our custom token: "et-title".
        case 'et-title':
          // Define i18n key.
          $i18n_key = implode(':', array('menu', 'item', $menu_link['mlid'], 'title'));

          // Localize menu link title.
          $menu_link_title = i18n_string($i18n_key, $menu_link['link_title'], array('langcode' => $options['language']->language));

          // Set menu link title as replacement value.
          $replacements[$original] = $sanitize ? check_plain($menu_link_title) : $menu_link_title;

          break;
      }
    }
  }

  // If type is array and data has been received.
  if ($type == 'array' && !empty($data[$type])) {
    $array = $data[$type];

    // Iterate trough tokens.
    foreach ($tokens as $name => $original) {
      // Switch: check token names.
      switch ($name) {

        // If our custom token: "et-join-path".
        case 'et-join-path':
          // Include Pathauto.
          module_load_include('inc', 'pathauto');

          // Iterate through children.
          $values = array();
          foreach (element_children($array) as $key) {
            // Get default value from token parameters if render array is
            // provided.
            if (is_array($array[$key])) {
              $default_value = render($array[$key]);
            }
            else {
              // Load menu link using one of Token's functions which caches
              // results.
              $menu_link_item = token_menu_link_load($key);

              // Get title: "title" is localized, "link_title" is unlocalized.
              $default_value = $menu_link_item['link_title'];
            }

            // Define i18n key.
            $i18n_key = implode(':', array('menu', 'item', $key, 'title'));

            // Get localized value.
            $value = i18n_string($i18n_key, $default_value, array('langcode' => $options['language']->language));

            // Add cleaned up value to value array.
            $values[] = pathauto_cleanstring($value);
          }

          // Compose path and set as replacement value.
          $replacements[$original] = implode('/', $values);

          break;

      }
    }
  }

  return $replacements;
}
bohus ulrych’s picture

Thank you lmeurs,
your solution works for me even for the bulk update. Nice job!
(I didn't tested the "et-title' token because I'm using node:title). This is my pattern: [node:menu-link:parents:et-join-path]/[node:title]

star-szr’s picture

Version: 7.x-1.2 » 7.x-1.x-dev
Category: Support request » Feature request

It looks like we have some viable code here, I would suggest that we can make this into a patch for pathauto. Could we use the existing join-path token and do a module_exists('entity_translation') within the token handling code perhaps?

star-szr’s picture

Also for what it's worth in my case the code from #3 worked great, bulk updating and all.

suffering drupal’s picture

Hi guys, just a question: do I understand correctly that you do NOT have any problem with the Pathauto and Locale/i18n combination, in the sense that language selection works and you get correct aliases?
Because for me it is not working: https://drupal.org/node/2278085 and neither for a lot of other people it seems: https://drupal.org/node/1234924, https://drupal.org/project/issues/i18n?text=pathauto&status=All&prioriti...

So if you are using Local and Internationalization and your aliases are working well, I'd like to know the rest of your configuration and theme (link to your website if you want) to maybe get a clue for solving the problem. I never had this either in D4, D5 or D6.

Now I am under:
D7.28
Pathauto 7.x-1.2
actually that's it, since the problem arises just with Locale activated, but just in case:

Token 7.x-1.5
Transliteration 7.x-3.2
Global Redirect 7.x-1.5
i18n 7.x-1.11 (deactivated)
Using Marinelli theme 7.x-3.0-beta11

Thanx

Thanx

lmeurs’s picture

@suffering drupal: At least you also need Internationalization (i18n) and it's Menu translation sub-module to translate menu's. Enable these modules, edit your menu at admin/structure/menu/manage/MACHINENAMEOFYOURMENU/edit and under Multilingual options > Translation mode select Translate and Localize. Menu items with language will allow translations. Menu items without language will be localized. Now you can translate your menu items.

With content translation using just i18n I used token [node:menu-link:parents:join-path]/[node:title] (see artofautomation.net), when using the Entity translation module and the custom code from #1892442-4: Pathauto based on menu items with internationalization I used token [node:menu-link:parents:et-join-path]/[node:menu-link:et-title] (see zandvliet.com).

suffering drupal’s picture

YES! Imeurs, thanks for your fast reply, and it seems to be working! Just doing a fast tryout on localhost and still having some confusions. I will take a better look for the real site (when back in Spain from holiday in Holland, leaving tomorrow and preparing now). Will let you know if I did or did not solve them.
Just one thing, what was the setting to also get /en/ for english like you have, instead of nothing as seems to be default? I remember having spent days on this one, still in D5, and I have forgotten again.
Nogmaals bedankt! :)

lmeurs’s picture

@suffering drupal: Great! I am not sure how to force a site using language prefixes for the default language, never had troubles with it. Make sure Path prefix language code is set for all languages at admin/config/regional/language/edit/LANGCODE.

Graag gedaan! :)

jeroent’s picture

Status: Active » Needs review
StatusFileSize
new1.19 KB

@Cottser,

I tried to make a patch of this code. Do you mean something like this?

Patch attached.

Status: Needs review » Needs work

The last submitted patch, 12: 1892442-entity_translation_join_path-12.patch, failed testing.

lmeurs’s picture

@JeroenT: Great to see a patch! I glanced at it, it might have failed since the $options array has not been sent as a 2nd argument to pathauto_cleanstring() as in:

$value = pathauto_cleanstring($value, $options);

I also noticed your patch does not fix the problem when bulk generating paths from another language than the default which I think is rather trivial, see my comment at #4.

jeroent’s picture

Status: Needs work » Needs review
StatusFileSize
new1.49 KB
new1.67 KB

@lmeurs: Thank you for your review and help.

I also made changes as you suggested in #4.

Patch attached.

suffering drupal’s picture

@Imeurs: Your last suggestion was also a hit, thank you with terugwerkende kracht!

jdanthinne’s picture

Status: Needs review » Reviewed & tested by the community

#15 working fine for me too, please commit, as Entity Translation seems to be used more and more…

bohus ulrych’s picture

Hi all, thank you for great job.
Patch #15 seems to be working well for me even for the bulk alias updates now.
I agree with @jdanthinne

bohus ulrych’s picture

Also this patch is needed menu_postion_menu_link_token-1682312-3.patch in my case

dave reid’s picture

Status: Reviewed & tested by the community » Closed (won't fix)

This should be implemented via hook_tokens_alter() in the i18n module - Pathauto and Token modules support core functionality out of the box, not contrib functionality.

ezoulou’s picture

Patch #15 work just fine. Thanks :-)

gnucifer’s picture

StatusFileSize
new9.78 KB

Here is an alternative patch that uses the i18n-API a little bit more cleanly, and generally (in my opinion), plus makes no assumption whether entity translation is in use or not (but works if it is).

gnucifer’s picture

@Dave Reid I see your point, however, i18n-functionality was left incomplete and merely partially implemented in Drupal 7, leaving contrib modules to fix the mess, and if i18n_menu where to provide theses tokens it would lead to a lot of code duplication. A part from passing the $language_code along, the i18n-menu "integration" is basically a one liner. And doing the integration from token-module instead of i18n-menu is much less messy.

gnucifer’s picture

Status: Closed (won't fix) » Needs review

Status: Needs review » Needs work

The last submitted patch, 22: token-i18n-menu-item-1892442-22.patch, failed testing.

The last submitted patch, 22: token-i18n-menu-item-1892442-22.patch, failed testing.

gnucifer’s picture

Oops, I thought this was a token-module issue, my mistake. Will re-post patch where it belongs: https://www.drupal.org/node/1505144#comment-10882462

gnucifer’s picture

Status: Needs work » Closed (won't fix)