Option to not prefix paths with the default language
pwolanin - January 11, 2008 - 04:03
| Project: | Internationalization |
| Version: | 5.x-2.5 |
| Component: | Code |
| Category: | feature request |
| Priority: | critical |
| Assigned: | AexChecker |
| Status: | won't fix |
Description
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.
| Attachment | Size |
|---|---|
| i18n_prefix_default.patch | 2.18 KB |

#1
#2
and in the patch too... (ua -> au)
#3
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.
#4
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.
#5
@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?
#6
here's the exact same patch for 5.x
#7
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?
#8
Thanks. subscribing
#9
Sorry I dont know why all the status flags changed on my previous message.
#10
Hi!
A try the last patch at 5.x, and i dont find any problem, it works fine!
Lazly
#11
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?
#12
so need add option to omit the language prefix for front page when using the default language
#13
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 :)
#14
When I said "This" I've meant the third patch, http://drupal.org/files/issues/i18n_prefix_default-687146-5x-6.patch
#15
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.
#16
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.
#17
I read post #13 - 16 and updated my path
<?php
/**
* 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;
}
?>
#18
AexChecker, the last attached patch does not want to apply against the i18n 5.x-2.2 release.
Can you check?
#19
By logic
1. Simply change
i18n_frontpage<?php/**
* 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<?php/**
* 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.
<?php/**
* 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<?php
/**
* 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;
}
?>
#20
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...
#21
#22
I rewiev and fix my path. Pleas check it.
main changes:
<?php
// ...
// 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;
}
?>
#23
fix problem for frontpage
<?php
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);
}
}
?>
#24
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)
#25
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...
#26
Tracking.
#27
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.
#28
2 post #25
<?php
/**
* 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;
}
?>
#29
2 #27
I think, for
i18n_pathfunction need add additional chek_function for path for add lang prefix<?php/**
* 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.<?php
/**
* 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){
// .....
// ... ... ...
}
?>
#30
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
#31
hello,
will this move some day into the i18n-dev?
i would be so happy ...
greetings momper
#32
+1 - would love to see this functionality in the release.
#33
hello,
is it still usable in the 5.x-2.3 version - or is it in dev?
greetings momper
#34
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
#35
hello,
is anybody interested in this stuff?
greetings momper
#36
hello,
anything new?
greetings momper
#37
all the people are into the 6.x release? i can't believe that i'm nearly the only one ...
greetings momper
#38
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
#39
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.
#40
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.
#41
The new version, 5.x-2.4 is not work with these patchs... Pls help!
#42
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 ...
#43
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.
#44
Cleaning up patch provided and updating it to get it applied over 5.x-2.5.
#45
uhm. the patch failed. here's a .rej file.
#46
Sorry but we won't be adding new features to 5.x.
Please move on to 6.x