starting with 4.7.x (5.x to come later) - for established sites enabling i18n, adding the language prefix for the default language means all the site paths suddnly change. This may be disconcerting for established users, and may cause SEO problems (different solution here: http://drupal.org/node/190414)

Attached patch is a proposed new option for i18n to omit the language prefix when using the default language.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pwolanin’s picture

Title: Option to not prefix paths with the defualt language » Option to not prefix paths with the default language
pwolanin’s picture

and in the patch too... (ua -> au)

pwolanin’s picture

note the patch was rolled form the modules directory, so you'll need to apply it as "patch -p1" if you're in the i18n directory.

neurer’s picture

Would love to see this for 5.x. Any idea, on when you'd be able to provide a patch? Would gladly assist in testing if that helps.

pwolanin’s picture

@jant - the patch for 5.x will be similar (but the files have changed). I'll try to whip something up in the next day or so. Can you test (or at least look at) the 4.7 patch?

pwolanin’s picture

here's the exact same patch for 5.x

Jose Reyero’s picture

Status: Needs review » Needs work

The idea looks good.

About the patch:
- There are some i18n api functions that could be used, saving some code, like i18n_default_language()
- The new settings look a bit confusing to me. The fact that we should note on the settings page is that this option is not compatible with 'browser language detection'.

I'm thinking of something like

Default path behaviour:
(How to handle paths without language prefix)
- Detect browser language and redirect
- Add default language prefix and redirect
- Assume default language (No language prefix for default language)

Does this make sense? Anyone can think of a better wording for these options?

mr.j’s picture

Project: Internationalization » Internationalization improvements for Drupal 6.x-dev
Version: 4.7.x-1.x-dev »
Status: Needs work » Needs review

Thanks. subscribing

mr.j’s picture

Project: Internationalization improvements for Drupal 6.x-dev » Internationalization
Version: » 4.7.x-1.x-dev
Status: Needs review » Needs work

Sorry I dont know why all the status flags changed on my previous message.

lazly’s picture

Hi!

A try the last patch at 5.x, and i dont find any problem, it works fine!

Lazly

jacon’s picture

It works! Yet I have a another problem with the language switcher - it contains the language prefix even for the default language.
How can we avoid that?

AexChecker’s picture

Version: 4.7.x-1.x-dev » 5.x-2.2
Assigned: pwolanin » AexChecker
Status: Needs work » Needs review
FileSize
2.85 KB

so need add option to omit the language prefix for front page when using the default language

druvision’s picture

Priority: Normal » Critical

Confirmed - it works for my pathauto-enabled one-language blog (http://www.levavie.com), where Hebrew (RTL) is the default language.

My blog is one-language. I needed I18n only for the RTL language detection used by the lightbox.

In my opinion, non-prefixing should be the default! Otherwise this breaks the upgrade path. You should put this default in the install script to preserve backwards compatibility for one-language sites. The facts that all URLs suddently change is not SE-friendly.

Amnon
-
Professional: Drupal Israel | Drupal Development & Consulting | Eco-Healing | Effective Hosting Strategies | בניית אתרים
Personal: Hitech Dolphin: Regain Simple Joy :)

druvision’s picture

When I said "This" I've meant the third patch, http://drupal.org/files/issues/i18n_prefix_default-687146-5x-6.patch

druvision’s picture

Tested more deeply.

This patch needs enhancement in case multiple languages are enabled, and the default language is not English.

Current behavior is a bug: Language for logged-in user's is forced to be English regardless of their preferences or the path. My only remedy was to disable English.

Suggested behavior: In this case, it should still be possible to hide the prefixes for the default (non-English) language.

druvision’s picture

Added info: My only remedy was to disable English and to revent back to the situation where only one language is enabled. Only then, it was backwards compatible.

AexChecker’s picture

FileSize
4.69 KB

I read post #13 - 16 and updated my path


/**
 * Produces i18n paths, with language prefix
 * If path is empty or site frontpage, path = 'lang'
 * Check for frontpage and search for alias before adding language
 *
 * @param string $path
 *  Drupal Path
 * @param string $lang
 *  Language prefix
 *
 * @return string
 *  Drupal path with language prefix.
 */
function i18n_path($path, $lang) {
  $temp = @trim($path, '/');
  $lang = ($lang = @trim($lang)) ? $lang : i18n_get_lang();
  $bool = (!variable_get('i18n_prefix_default', 0) && $cur_lang == $lang);
  $def_lang  = i18n_default_language();
  $frontpage = i18n_frontpage($lang);
  if (!$path || $path == $frontpage || !$temp || $temp == $frontpage) {
    $path = $bool ? i18n_url_rewrite('alias', $path, $path) : $lang;
    $path = @trim($path, '/');
    return $path;
  }
  if(($alias = drupal_lookup_path('alias', $path))) {
    if ($lang == i18n_get_lang_prefix($alias)) {
      i18n_get_lang_prefix($alias, TRUE); // Alias without language prefix
    } else {
      $alias = i18n_url_rewrite('alias', $alias, $path);
    }
    $path = $alias;
  }
  $path = $bool ? $path : $lang .'/'. $path;
  $path = @trim($path, '/');
  return $path;
}

/**
 * Rewrites path with current language and removes
 * prefix if searching for source path
 *
 * @param string $type
 * @param string $path
 * @param string $original
 *
 * @return string
 */
function i18n_url_rewrite($type, $path, $original){
  if ($type == 'alias') {
    $def_lang = i18n_default_language();
    $cur_lang = i18n_get_lang();
    $pre_lang = i18n_get_lang_prefix($path, TRUE);
    $bool = (!variable_get('i18n_prefix_default', 0) && $cur_lang == $def_lang);
    if ($pre_lang) {
    	$path = ($pre_lang == $def_lang)
    	      ? ($bool ? $path : $pre_lang .'/'. $path)
    	      : ($pre_lang .'/'. $path);
    } else {
    	$path = ($bool)
    	      ? ($path)
    	      : ($path ? $cur_lang . '/'. $path : $cur_lang);
    }
    $path = trim($path, '/');
  }
  elseif ($type == 'source') {
    if ($path == $original) {
    	$path = i18n_get_normal_path($path);
    } else {
    	// Path may have been dealiased but still have language prefix
    	i18n_get_lang_prefix($path, TRUE);
    }
  }
  return $path;
}

sdecabooter’s picture

AexChecker, the last attached patch does not want to apply against the i18n 5.x-2.2 release.
Can you check?

AexChecker’s picture

FileSize
42.78 KB
25.03 KB

By logic

1. Simply change i18n_frontpage

/**
 * Language dependent front page
 * This function will search for aliases like 'en/home', 'es/home'...,
 * but for default language return aliase without prefix
 *
 * @param string [optional] $lang
 *  ISO 639 language code.
 *
 * @return string
 *  Language dependent path of front page.
 */
function i18n_frontpage($lang = NULL) {
  static $paths = array();
  $lang = $lang ? strtolower(@trim($lang)) : _i18n_get_lang();
  if (!isset($paths[$lang])) {
    // Case when add language prefix for default_language
    $bool = (!variable_get('i18n_prefix_default', 0)
          && $lang == i18n_default_language());
    $path = $bool
          ? variable_get('site_frontpage','node')
          : $lang.'/'.variable_get('site_frontpage','node');
    $paths[$lang] = i18n_get_normal_path($path);
  }
  return $paths[$lang];
}

2. i18n_get_browser_lang

/**
 * Get language from browser settings, but only if it is a valid language
 *
 * @return string
 *  Language code.
 */
function i18n_get_browser_lang() {
  static $browser_lang; // Cash result
  if (!isset($browser_lang)) {
    $languages = i18n_supported_languages();
    $exploded_server = explode(";", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
    $accept = explode(',', array_shift($exploded_server));
    $browser_lang = '';
    foreach ($accept as $lang) {
      if (($lang = @trim($lang)) &&
            (array_key_exists(($lang = strtolower($lang)), $languages)
              || array_key_exists(($lang = substr($lang, 0, 2)), $languages))) {
        $browser_lang = $lang;
        break;
      }
    }
  }
  return $browser_lang;
}

3.

/**
 * Get language code from path.
 *
 * @param $path
 *  Drupal path.
 * @param $trim
 *  TRUE to remove language code from $path.
 *
 * @return string
 *  Language code.
 */
function i18n_get_lang_prefix(&$path, $trim = FALSE) {
  $exploded_path = explode('/', $path);
  $maybelang = @strtolower(array_shift($exploded_path));
  $languages = i18n_languages();
  if (!array_key_exists($maybelang, $languages)){
    $maybelang = ''; // NO Language code.
  } elseif ($trim) {
    $path = substr($path, strlen($maybelang) + 1);
  }
  return $maybelang;
}

And Snippet for _phptemplate_variables() as appendix

/**
 * Override or insert PHPTemplate variables into the templates.
 */
function _phptemplate_variables($hook, $vars) {

// ...

  // Language prefix for Front Page 
  if (isset($vars['base_path']) && !empty($vars['language'])) {
    if (function_exists('i18n_default_language') && $vars['language'] != i18n_default_language()) {
      $vars['language']  = strtolower(@trim($vars['language']));
      $vars['base_path'] = rtrim($vars['base_path'], '/');
      $vars['base_path'].= '/'. $vars['language'];
    }
  }

// ...

  return $vars;
}
lejon’s picture

Title: Option to not prefix paths with the default language » Does this fix the problem with image paths?

Is this how to fix the problem with image paths?

See: http://drupal.org/node/208474
http://drupal.org/node/222155

If it is, can you explain how to apply the patch? Sorry, I'm a bit of a newb...

sdecabooter’s picture

Title: Does this fix the problem with image paths? » Option to not prefix paths with the default language
AexChecker’s picture

FileSize
42.6 KB
38.19 KB

I rewiev and fix my path. Pleas check it.

main changes:


// ...

// add private function

/**
 * Case when add language prefix for Drupal path.
 *
 * @param string $lang
 *  Language.
 *
 * @return bool
 *  TRUE when need add language prefix for language, or FALSE.
 */
function _i18n_add_lang_prefix($lang){
  static $languages = array();
  if (!isset($languages[$lang])) {
    $default = i18n_default_language();
    $current = i18n_get_lang();
    $languages[$lang] = (!(!variable_get('i18n_prefix_default', 0) && $lang == $default)) || ($current != $default);
  }
  return $languages[$lang];
}
 
// ...

/**
 * Implementation of hook_init()
 *
 * May do a redirect from home page for not to get wrong versions in cache
 * Warning: when in bootstrap mode, this may be called before i18n_get_lang()
 *
 * @return void
 */
function i18n_init(){
  global $i18n_langpath;
  $lang = i18n_get_lang();
  $path = _i18n_get_original_path();
  // Init selection mode
  i18n_selection_mode(variable_get('i18n_selection_mode', 'simple'));
  // Multi tables, for backwards compatibility and experimentation
  _i18n_set_db_prefix($lang);

  if ($path == '') { // Main page
    // Check for update or cron scripts to disable rewriting and redirection
    if(preg_match('|/(?!index\.php)\w+\.php|', request_uri())){
      i18n_selection_mode('off');
    } elseif ($lang != i18n_default_language()) {
      // Redirect to main page in $lang when it's not default language.
      _i18n_goto($lang);
    } else {
      $_GET['q'] = i18n_frontpage($lang);
    }
  } elseif ($lang == $path) { // When path is only language code
    $_GET['q'] = i18n_frontpage($lang);
  }
  elseif ($i18n_langpath) {
    //search alias with and without lang and remove lang.
    $_GET['q'] = i18n_get_normal_path($path);
  }
  // If not in bootstrap, variable init
  if(!_i18n_is_bootstrap()){
    //include drupal_get_path('module', 'i18n').'/i18n.inc';
    i18n_variable_init();
  }
  if (!_i18n_add_lang_prefix($lang) && i18n_get_lang_prefix($path, TRUE)) {
    _i18n_goto($path);
  }
}

// ...

/**
 * More i18n API
 */

/**
 * Produces i18n paths, with language prefix
 * If path is empty or site frontpage, path = 'lang'
 * Check for frontpage and search for alias before adding language
 *
 * @param string $path
 *  Drupal Path
 * @param string $lang
 *  Language prefix
 *
 * @return string
 *  Drupal path with language prefix.
 */
function i18n_path($path, $lang){
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  }
  if(($alias = drupal_lookup_path('alias', $path)) && (!($prefix = i18n_get_lang_prefix($alias, TRUE)) || ($prefix && $prefix == $lang))) {
  	$path = $alias;
  }
  if (_i18n_add_lang_prefix($lang)) {
  	$path = $lang .'/'. $path;
  }
  return $path;
}

// ...

/**
 * Rewrites path with current language and removes
 * prefix if searching for source path
 *
 * @param string $type
 * @param string $path
 * @param string $original
 *
 * @return string
 */
function i18n_url_rewrite($type, $path, $original){
  if ($type == 'alias' && !i18n_get_lang_prefix($path) && _i18n_add_lang_prefix(($cur_lang = i18n_get_lang()))) {
    $path = $path ? $cur_lang .'/'. $path : $cur_lang;
  } elseif ($type == 'source') {
    if ($path == $original) {
      $path = i18n_get_normal_path($path);
    } else {
      // Path may have been dealiased but still have language prefix
      i18n_get_lang_prefix($path, TRUE);
    }
  }
  return $path;
}

AexChecker’s picture

Status: Needs review » Fixed
FileSize
42.72 KB
38.4 KB

fix problem for frontpage

function i18n_init(){
  global $i18n_langpath;
  $lang = i18n_get_lang();
  $path = _i18n_get_original_path();
  // Init selection mode
  i18n_selection_mode(variable_get('i18n_selection_mode', 'simple'));
  // Multi tables, for backwards compatibility and experimentation
  _i18n_set_db_prefix($lang);

  if ($path == '') { // Main page
    // Check for update or cron scripts to disable rewriting and redirection
    if(preg_match('|/(?!index\.php)\w+\.php|', request_uri())){
      i18n_selection_mode('off');
    } elseif ($lang != i18n_default_language()) {
      // Redirect to main page in $lang when it's not default language.
      _i18n_goto($lang);
    } else {
      $_GET['q'] = i18n_frontpage($lang);
    }
  } elseif ($lang == $path) { // When path is only language code
    $_GET['q'] = i18n_frontpage($lang);
    if ($lang == i18n_default_language()) {
      i18n_get_lang_prefix($path, TRUE);
      _i18n_goto($path);
    }
  }
  elseif ($i18n_langpath) {
    //search alias with and without lang and remove lang.
    $_GET['q'] = i18n_get_normal_path($path);
  }
  // If not in bootstrap, variable init
  if(!_i18n_is_bootstrap()){
    //include drupal_get_path('module', 'i18n').'/i18n.inc';
    i18n_variable_init();
  }
  if (!_i18n_add_lang_prefix($lang) && i18n_get_lang_prefix($path, TRUE)) {
    _i18n_goto($path);
  }
}

Jose Reyero’s picture

Status: Fixed » Needs review

Hey, great that you got this fixed, but better don't change the issue status till we get the code committed :-)

(This is looking good, just I'm working on the Drupal 6 branch this days so I haven't yet tried this one, will be back soon)

hunvreus’s picture

Great patch. I guess it'll be in the next version of i18n. There is a small problem though; try to set a Custom Language as a default language. Once you have switched to a non-default language, the Translations block urls are gonna be prefixed with the code of this language; if you browsed the french version www.site.com/fr/mypage, then Translations will display www.site.com/fr/en/mypage for the English version. Give it a try...

akahn’s picture

Tracking.

lejon’s picture

Is this how to fix the problem with image paths?

See: http://drupal.org/node/208474
http://drupal.org/node/222155

If it is, can you explain how to apply the patch? Sorry, I'm a bit of a newb... Do you just replace the current i18n.module in the modules folder with your version above?

Thanks.

AexChecker’s picture

FileSize
44.1 KB

2 post #25

/**
 * Get language code from path.
 *
 * @param string $path
 *  Drupal path.
 * @param bool [optional] $trim
 *  TRUE to remove language code from $path.
 *
 * @return string
 *  Language code.
 */
function i18n_get_lang_prefix(&$path, $trim = FALSE){
  $exploded_path = explode('/', $path);
  $maybelang = @strtolower(array_shift($exploded_path));
  $languages = i18n_languages();
  if (array_key_exists($maybelang, $languages)) {
    $last_lang = '';
    $exploded_path_exists = count($exploded_path);
    foreach ($exploded_path as $k => $alt_lang) {
      if (!array_key_exists($alt_lang, $languages)) {
        break;
      }
      $last_lang = $alt_lang;
      unset($exploded_path[$k]);
    }

    // Normal Case
    // /uk => /uk
    if (!$exploded_path) {
      // Redirect for frontpage with many lang prefix
      // /uk/en/fr/ru => /uk
      if ($exploded_path_exists) {
        _i18n_goto($maybelang);
      }
      $path = $trim ? '' : $maybelang;
    }

    // Redirect for page with many lang prefix
    // 1. Translation if is node or taxonomy
    //    /uk/ru/en/bla_bla => /en/bla_bla => /uk/bla_bla
    // 2. Or change lang
    //    /uk/ru/en/bla_bla => /uk/bla_bla
    elseif ($last_lang) {
      $new_path           = join('/', $exploded_path);
      $last_lang_new_path = $last_lang .'/'. $new_path;
      if ($last_lang_new_path == ($new_path = drupal_get_normal_path($last_lang_new_path))) {
        $new_path = drupal_get_normal_path($new_path);
      }
      if (module_exists('translation') && function_exists('translation_url')) {
        $new_path = translation_url($new_path, $maybelang);
      }
      _i18n_goto($new_path);
    }

    // Normal Case:
    // 1. Without lang prefix: /en/bla_bla => /bla_bla
    elseif ($trim) {
      $path = substr($path, strlen($maybelang) + 1);
    }
    // 2. With lang prefix:    /en/bla_bla => /en/bla_bla
    // else {}
  } else {
    $maybelang = ''; // NO Language code.
  }
  return $maybelang;
}
AexChecker’s picture

2 #27
I think, for i18n_path function need add additional chek_function for path for add lang prefix

/**
* Produces i18n paths, with language prefix
* If path is empty or site frontpage, path = 'lang'
* Check for frontpage and search for alias before adding language
*
* @param string $path
*  Drupal Path
* @param string $lang
*  Language prefix
*
* @return string
*  Drupal path with language prefix.
*/
function i18n_path($path, $lang){
  if (!$path || $path == i18n_frontpage($lang)) {
    return $lang;
  }
  if(($alias = drupal_lookup_path('alias', $path)) && (!($prefix = i18n_get_lang_prefix($alias, TRUE)) || ($prefix && $prefix == $lang))) {
      $path = $alias;
  }
  if (_i18n_add_lang_prefix($lang) && _i18n_add_lang_prefix_for_path($path)) {
      $path = $lang .'/'. $path;
  }
  return $path;
}

I write _i18n_add_lang_prefix_for_path(), when I have more time.


/**
* Case when omit or add language prefix for Drupal path.
*
* @param string $path
*  Drupal Path
*
* @return bool
* TRUE - add language prefix, FALSE - omit language prefix
*/
function _i18n_add_lang_prefix_for_path($path){
  // .....
  // ... ... ...
}
momper’s picture

hello

i searched so long for this solution - the absolutly right way - thanks a lot ... + please don't stop with this stuff - it's a must for a multilanguage system ...
i have to work with this on a production server - i hope i have luck :)

thanks thanks momper

momper’s picture

hello,

will this move some day into the i18n-dev?
i would be so happy ...

greetings momper

scedwar’s picture

+1 - would love to see this functionality in the release.

momper’s picture

hello,

is it still usable in the 5.x-2.3 version - or is it in dev?

greetings momper

momper’s picture

hello,

can we collect some money for a bounty, to put it into dev for 5? does anybody want's to join?
@AexChecker - would you do it? or somebody else? i'm not a programmer ...

thanks and greetings momper

momper’s picture

hello,

is anybody interested in this stuff?

greetings momper

momper’s picture

hello,

anything new?

greetings momper

momper’s picture

all the people are into the 6.x release? i can't believe that i'm nearly the only one ...

greetings momper

willeaton’s picture

Hi Guys, anyone else have the issue of the parent item drop down list not containing all of the menu items? Basically, it appears that my menu list it too long for the drop down of "parent item" and so if the node already exists and has a link set up, when I go to edit it and the parent item is at the bottom of the list (in fact so low, off the list) it messes my menus up and places the link in "navigation" instead of the menu its supposed to be in?

Thanks,
Will

scedwar’s picture

I think you're off topic with that! I'm only interested in getting rid of /en (or whatever the default language is) from the url.

willeaton’s picture

Maybe, I'm just seeing if anyone else had the same issue as a result of the above patch, if so then it is relevant, if not then it only helps me narrow down what is causing it and means everyone else has to scroll just that little bit more to hit the bottom of the page.

lazly’s picture

The new version, 5.x-2.4 is not work with these patchs... Pls help!

momper’s picture

hello

i see big plans with internationalization and the localization client - but the standard request to not prefix a language is not possible ... all your clients agree with this? you tell them - sorry it's not possible? i don't understand it ...

greetings momper

p.s. i'm not a programmer - so i can't do it for myself - it would be wonderful ...

tille’s picture

hi,

I know this discussion is about Internationalization - and not Localizer - but since the requested feature seems to work without any problems with Localizer I thought it might be interesting anyways…?

http://drupal.org/project/localizer

…at least I just checked the following:

1) default language = English
english version --> domain.net/node1
german version --> domain.net/de/node1

2) default language = German
german version --> domain.net/node1
english version --> domain.net/en/node1

The related administration panel can be found here:
admin/settings/localizer
-> Language prefix
-> [x] Do not add prefix on the site default language

…for me it seems to work perfectly well - and I just hope that it will stay this way and that there's no other problems lurking from somewhere around the corner…:]

greetz, till.

OnkelTem’s picture

Version: 5.x-2.2 » 5.x-2.5
FileSize
8.97 KB

Cleaning up patch provided and updating it to get it applied over 5.x-2.5.

luco’s picture

FileSize
10.83 KB

uhm. the patch failed. here's a .rej file.

Jose Reyero’s picture

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

Sorry but we won't be adding new features to 5.x.

Please move on to 6.x