Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
drupal
Commits
1b9cde9d
Commit
1b9cde9d
authored
Oct 09, 2009
by
webchick
Browse files
#282191
by plach, nedjo, catch, et al: TF
#1
: Allow different interface language for the same path.
parent
d72c5656
Changes
13
Hide whitespace changes
Inline
Side-by-side
includes/bootstrap.inc
View file @
1b9cde9d
...
...
@@ -154,27 +154,19 @@
define
(
'DRUPAL_KILOBYTE'
,
1024
);
/**
*
No language negotiation. The defaul
t language
is used
.
*
The type of language used to define the conten
t language.
*/
define
(
'LANGUAGE_
NEGOTIATION_NONE'
,
0
);
define
(
'LANGUAGE_
TYPE_CONTENT'
,
'language'
);
/**
* Path based negotiation with fallback to default language if no defined path
* prefix identified.
* The type of language used to select the user interface.
*/
define
(
'LANGUAGE_
NEGOTIATION_PATH_DEFAULT'
,
1
);
define
(
'LANGUAGE_
TYPE_INTERFACE'
,
'language_interface'
);
/**
* Path based negotiation with fallback to user preferences and browser
* language detection if no defined path prefix identified.
* The type of language used for URLs.
*/
define
(
'LANGUAGE_NEGOTIATION_PATH'
,
2
);
/**
* Domain based negotiation with fallback to default language if no language
* identified by domain.
*/
define
(
'LANGUAGE_NEGOTIATION_DOMAIN'
,
3
);
define
(
'LANGUAGE_TYPE_URL'
,
'language_url'
);
/**
* Language written left to right. Possible value of $language->direction.
...
...
@@ -1628,22 +1620,49 @@ function get_t() {
}
/**
*
Choose a language for the current page, based on site and user preferenc
es.
*
Initialize all the defined language typ
es.
*/
function
drupal_language_initialize
()
{
global
$language
,
$user
;
$types
=
language_types
()
;
// Ensure the language is correctly returned, even without multilanguage support.
// Useful for eg. XML/HTML 'lang' attributes.
if
(
variable_get
(
'language_count'
,
1
)
==
1
)
{
$language
=
language_default
();
$default
=
language_default
();
foreach
(
$types
as
$type
)
{
$GLOBALS
[
$type
]
=
$default
;
}
}
else
{
include_once
DRUPAL_ROOT
.
'/includes/language.inc'
;
$language
=
language_initialize
();
foreach
(
$types
as
$type
)
{
$GLOBALS
[
$type
]
=
language_initialize
(
$type
);
}
}
}
/**
* The built-in language types.
*
* @return
* An array of key-values pairs where the key is the language type and the
* value is its configurability.
*/
function
drupal_language_types
()
{
return
array
(
LANGUAGE_TYPE_CONTENT
=>
TRUE
,
LANGUAGE_TYPE_INTERFACE
=>
TRUE
,
LANGUAGE_TYPE_URL
=>
FALSE
,
);
}
/**
* Return an array of the available language types.
*/
function
language_types
()
{
return
array_keys
(
variable_get
(
'language_types'
,
drupal_language_types
()));
}
/**
* Get a list of languages set up indexed by the specified key
*
...
...
includes/common.inc
View file @
1b9cde9d
...
...
@@ -392,6 +392,26 @@ function drupal_get_query_parameters(array $query = NULL, array $exclude = array
return
$params
;
}
/**
* Split an URL-encoded query string into an array.
*
* @param $query
* The query string to split.
*
* @return
* An array of url decoded couples $param_name => $value.
*/
function
drupal_get_query_array
(
$query
)
{
$result
=
array
();
if
(
!
empty
(
$query
))
{
foreach
(
explode
(
'&'
,
$query
)
as
$param
)
{
$param
=
explode
(
'='
,
$param
);
$result
[
$param
[
0
]]
=
isset
(
$param
[
1
])
?
rawurldecode
(
$param
[
1
])
:
''
;
}
}
return
$result
;
}
/**
* Parse an array into a valid, rawurlencoded query string.
*
...
...
@@ -1455,12 +1475,12 @@ function fix_gpc_magic() {
* The translated string.
*/
function
t
(
$string
,
array
$args
=
array
(),
array
$options
=
array
())
{
global
$language
;
global
$language
_interface
;
static
$custom_strings
;
// Merge in default.
if
(
empty
(
$options
[
'langcode'
]))
{
$options
[
'langcode'
]
=
isset
(
$language
->
language
)
?
$language
->
language
:
'en'
;
$options
[
'langcode'
]
=
isset
(
$language
_interface
->
language
)
?
$language
_interface
->
language
:
'en'
;
}
if
(
empty
(
$options
[
'context'
]))
{
$options
[
'context'
]
=
''
;
...
...
@@ -2438,7 +2458,8 @@ function url($path = NULL, array $options = array()) {
$path
=
''
;
}
elseif
(
!
empty
(
$path
)
&&
!
$options
[
'alias'
])
{
$path
=
drupal_get_path_alias
(
$path
,
isset
(
$options
[
'language'
])
?
$options
[
'language'
]
->
language
:
''
);
$language
=
isset
(
$options
[
'language'
])
&&
isset
(
$options
[
'language'
]
->
language
)
?
$options
[
'language'
]
->
language
:
''
;
$path
=
drupal_get_path_alias
(
$path
,
$language
);
}
if
(
function_exists
(
'custom_url_rewrite_outbound'
))
{
...
...
@@ -2546,7 +2567,7 @@ function drupal_attributes(array $attributes = array()) {
* an HTML string containing a link to the given path.
*/
function
l
(
$text
,
$path
,
array
$options
=
array
())
{
global
$language
;
global
$language
_url
;
// Merge in defaults.
$options
+=
array
(
...
...
@@ -2556,7 +2577,7 @@ function l($text, $path, array $options = array()) {
// Append active class.
if
((
$path
==
$_GET
[
'q'
]
||
(
$path
==
'<front>'
&&
drupal_is_front_page
()))
&&
(
empty
(
$options
[
'language'
])
||
$options
[
'language'
]
->
language
==
$language
->
language
))
{
(
empty
(
$options
[
'language'
])
||
$options
[
'language'
]
->
language
==
$language
_url
->
language
))
{
$options
[
'attributes'
][
'class'
][]
=
'active'
;
}
...
...
includes/language.inc
View file @
1b9cde9d
...
...
@@ -7,138 +7,392 @@
*/
/**
* Choose a language for the page, based on language negotiation settings.
*/
function
language_initialize
()
{
global
$user
;
// Configured presentation language mode.
$mode
=
variable_get
(
'language_negotiation'
,
LANGUAGE_NEGOTIATION_NONE
);
// Get a list of enabled languages.
$languages
=
language_list
(
'enabled'
);
$languages
=
$languages
[
1
];
switch
(
$mode
)
{
case
LANGUAGE_NEGOTIATION_NONE
:
return
language_default
();
case
LANGUAGE_NEGOTIATION_DOMAIN
:
foreach
(
$languages
as
$language
)
{
$host
=
parse_url
(
$language
->
domain
,
PHP_URL_HOST
);
if
(
$host
&&
(
$_SERVER
[
'HTTP_HOST'
]
==
$host
))
{
return
$language
;
}
}
return
language_default
();
case
LANGUAGE_NEGOTIATION_PATH_DEFAULT
:
case
LANGUAGE_NEGOTIATION_PATH
:
// $_GET['q'] might not be available at this time, because
// path initialization runs after the language bootstrap phase.
$args
=
isset
(
$_GET
[
'q'
])
?
explode
(
'/'
,
$_GET
[
'q'
])
:
array
();
$prefix
=
array_shift
(
$args
);
// Search prefix within enabled languages.
foreach
(
$languages
as
$language
)
{
if
(
!
empty
(
$language
->
prefix
)
&&
$language
->
prefix
==
$prefix
)
{
// Rebuild $GET['q'] with the language removed.
$_GET
[
'q'
]
=
implode
(
'/'
,
$args
);
return
$language
;
}
* No language negotiation. The default language is used.
*/
define
(
'LANGUAGE_NEGOTIATION_DEFAULT'
,
'language-default'
);
/**
* Return all the defined language types.
*
* @return
* An array of language type names. The name will be used as the global
* variable name the language value will be stored in.
*/
function
language_types_info
()
{
$language_types
=
&
drupal_static
(
__FUNCTION__
);
if
(
!
isset
(
$language_types
))
{
$language_types
=
module_invoke_all
(
'language_types_info'
);
// Let other modules alter the list of language types.
drupal_alter
(
'language_types_info'
,
$language_types
);
}
return
$language_types
;
}
/**
* Return only the configurable language types.
*
* A language type maybe configurable or fixed. A fixed language type is a type
* whose negotiation values are unchangable and defined while defining the
* language type itself.
*
* @return
* An array of language type names.
*/
function
language_types_configurable
()
{
$configurable
=
&
drupal_static
(
__FUNCTION__
);
if
(
!
isset
(
$configurable
))
{
$types
=
variable_get
(
'language_types'
,
drupal_language_types
());
$configurable
=
array_keys
(
array_filter
(
$types
));
}
return
$configurable
;
}
/**
* Disable the given language types.
*
* @param $types
* An array of language types.
*/
function
language_types_disable
(
$types
)
{
$enabled_types
=
variable_get
(
'language_types'
,
drupal_language_types
());
foreach
(
$types
as
$type
)
{
unset
(
$enabled_types
[
$type
]);
}
variable_set
(
'language_types'
,
$enabled_types
);
}
/**
* Check if a language provider is enabled.
*
* This has two possible behaviors:
* - If $provider_id is given return its ID if enabled, FALSE otherwise.
* - If no ID is passed the first enabled language provider is returned.
*
* @param $type
* The language negotiation type.
* @param $provider_id
* The language provider ID.
*
* @return
* The provider ID if it is enabled, FALSE otherwise.
*/
function
language_negotiation_get
(
$type
,
$provider_id
=
NULL
)
{
$negotiation
=
variable_get
(
"language_negotiation_
$type
"
,
array
());
if
(
empty
(
$negotiation
))
{
return
empty
(
$provider_id
)
?
LANGUAGE_NEGOTIATION_DEFAULT
:
FALSE
;
}
if
(
empty
(
$provider_id
))
{
return
key
(
$negotiation
);
}
if
(
isset
(
$negotiation
[
$provider_id
]))
{
return
$provider_id
;
}
return
FALSE
;
}
/**
* Check if the given language provider is enabled for any configurable language
* type.
*
* @param $provider_id
* The language provider ID.
*
* @return
* TRUE if there is at least one language type for which the give language
* provider is enabled, FALSE otherwise.
*/
function
language_negotiation_get_any
(
$provider_id
)
{
foreach
(
language_types_configurable
()
as
$type
)
{
if
(
language_negotiation_get
(
$type
,
$provider_id
))
{
return
TRUE
;
}
}
return
FALSE
;
}
/**
* Return the language switch links for the given language.
*
* @param $type
* The language negotiation type.
* @param $path
* The internal path the switch links will be relative to.
*
* @return
* A keyed array of links ready to be themed.
*/
function
language_negotiation_get_switch_links
(
$type
,
$path
)
{
$links
=
FALSE
;
$negotiation
=
variable_get
(
"language_negotiation_
$type
"
,
array
());
foreach
(
$negotiation
as
$id
=>
$provider
)
{
if
(
isset
(
$provider
[
'callbacks'
][
'switcher'
]))
{
if
(
isset
(
$provider
[
'file'
]))
{
require_once
DRUPAL_ROOT
.
'/'
.
$provider
[
'file'
];
}
if
(
$mode
==
LANGUAGE_NEGOTIATION_PATH_DEFAULT
)
{
// If we did not found the language by prefix, choose the default.
return
language_default
();
$callback
=
$provider
[
'callbacks'
][
'switcher'
];
$result
=
$callback
(
$type
,
$path
);
if
(
!
empty
(
$result
))
{
// Allow modules to provide translations for specific links.
drupal_alter
(
'language_switch_links'
,
$result
,
$type
,
$path
);
$links
=
(
object
)
array
(
'links'
=>
$result
,
'provider'
=>
$id
);
break
;
}
break
;
}
}
return
$links
;
}
/**
* Save a list of language providers.
*
* @param $type
* The language negotiation type.
* @param $language_providers
* An array of language provider ids.
*/
function
language_negotiation_set
(
$type
,
$language_providers
)
{
// Save only the necessary fields.
$provider_fields
=
array
(
'callbacks'
,
'file'
,
'cache'
);
$negotiation
=
array
();
$providers_weight
=
array
();
$defined_providers
=
language_negotiation_info
();
$default_types
=
language_types_configurable
();
// Initialize the providers weight list.
foreach
(
$language_providers
as
$id
=>
$provider
)
{
$providers_weight
[
$id
]
=
language_provider_weight
(
$provider
);
}
// User language.
if
(
$user
->
uid
&&
isset
(
$languages
[
$user
->
language
]))
{
return
$languages
[
$user
->
language
];
// Order providers list by weight.
asort
(
$providers_weight
);
foreach
(
$providers_weight
as
$id
=>
$weight
)
{
if
(
isset
(
$defined_providers
[
$id
]))
{
$provider
=
$defined_providers
[
$id
];
// If the provider does not express any preference about types, make it
// available for any configurable type.
$types
=
array_flip
(
isset
(
$provider
[
'types'
])
?
$provider
[
'types'
]
:
$default_types
);
// Check if the provider is defined and has the right type.
if
(
isset
(
$types
[
$type
]))
{
$provider_data
=
array
();
foreach
(
$provider_fields
as
$field
)
{
if
(
isset
(
$provider
[
$field
]))
{
$provider_data
[
$field
]
=
$provider
[
$field
];
}
}
$negotiation
[
$id
]
=
$provider_data
;
}
}
}
// Browser accept-language parsing.
if
(
$language
=
language_from_browser
())
{
return
$language
;
variable_set
(
"language_negotiation_
$type
"
,
$negotiation
);
}
/**
* Return all the defined language providers.
*
* @return
* An array of language providers.
*/
function
language_negotiation_info
()
{
$language_providers
=
&
drupal_static
(
__FUNCTION__
);
if
(
!
isset
(
$language_providers
))
{
// Collect all the module-defined language negotiation providers.
$language_providers
=
module_invoke_all
(
'language_negotiation_info'
);
// Add the default language provider.
$language_providers
[
LANGUAGE_NEGOTIATION_DEFAULT
]
=
array
(
'callbacks'
=>
array
(
'language'
=>
'language_from_default'
),
'weight'
=>
10
,
'name'
=>
t
(
'Default'
),
'description'
=>
t
(
'The default site language (@language_name) is used.'
,
array
(
'@language_name'
=>
language_default
()
->
native
)),
);
// Let other modules alter the list of language providers.
drupal_alter
(
'language_negotiation_info'
,
$language_providers
);
}
// Fall back on the default if everything else fails.
return
language_default
();
return
$language_providers
;
}
/**
* Identify language from the Accept-language HTTP header we got.
* Helper function used to cache the language providers results.
*
* @param $provider_id
* The language provider ID.
* @param $provider
* The language provider to be invoked. If not passed it will be explicitly
* loaded through language_negotiation_info().
*
* @return
* The language provider's return value.
*/
function
language_from_browser
()
{
// Specified by the user via the browser's Accept Language setting
// Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
$browser_langs
=
array
();
function
language_provider_invoke
(
$provider_id
,
$provider
=
NULL
)
{
$results
=
&
drupal_static
(
__FUNCTION__
);
if
(
isset
(
$_SERVER
[
'HTTP_ACCEPT_LANGUAGE'
]))
{
$browser_accept
=
explode
(
","
,
$_SERVER
[
'HTTP_ACCEPT_LANGUAGE'
]);
for
(
$i
=
0
;
$i
<
count
(
$browser_accept
);
$i
++
)
{
// The language part is either a code or a code with a quality.
// We cannot do anything with a * code, so it is skipped.
// If the quality is missing, it is assumed to be 1 according to the RFC.
if
(
preg_match
(
"!([a-z-]+)(;q=([0-9
\\
.]+))?!"
,
trim
(
$browser_accept
[
$i
]),
$found
))
{
$browser_langs
[
$found
[
1
]]
=
(
isset
(
$found
[
3
])
?
(
float
)
$found
[
3
]
:
1.0
);
}
if
(
!
isset
(
$results
[
$provider_id
]))
{
global
$user
;
// Get languages grouped by status and select only the enabled ones.
$languages
=
language_list
(
'enabled'
);
$languages
=
$languages
[
1
];
if
(
!
isset
(
$provider
))
{
$providers
=
language_negotiation_info
();
$provider
=
$providers
[
$provider_id
];
}
if
(
isset
(
$provider
[
'file'
]))
{
require_once
DRUPAL_ROOT
.
'/'
.
$provider
[
'file'
];
}
// If the language provider has no cache preference or this is satisified
// we can execute the callback.
$cache
=
!
isset
(
$provider
[
'cache'
])
||
$user
->
uid
||
$provider
[
'cache'
]
==
variable_get
(
'cache'
,
CACHE_DISABLED
);
$callback
=
isset
(
$provider
[
'callbacks'
][
'language'
])
?
$provider
[
'callbacks'
][
'language'
]
:
FALSE
;
$langcode
=
$cache
&&
function_exists
(
$callback
)
?
$callback
(
$languages
)
:
FALSE
;
$results
[
$provider_id
]
=
isset
(
$languages
[
$langcode
])
?
$languages
[
$langcode
]
:
FALSE
;
}
// Order the codes by quality
arsort
(
$browser_langs
);
return
$results
[
$provider_id
];
}
/**
* Return the passed language provider weight or a default value.
*
* @param $provider
* A language provider data structure.
*
* @return
* A numeric weight.
*/
function
language_provider_weight
(
$provider
)
{
$default
=
is_numeric
(
$provider
)
?
$provider
:
0
;
return
isset
(
$provider
[
'weight'
])
&&
is_numeric
(
$provider
[
'weight'
])
?
$provider
[
'weight'
]
:
$default
;
}
// Try to find the first preferred language we have
$languages
=
language_list
(
'enabled'
);
foreach
(
$browser_langs
as
$langcode
=>
$q
)
{
if
(
isset
(
$languages
[
'1'
][
$langcode
]))
{
return
$languages
[
'1'
][
$langcode
];
/**
* Choose a language for the given type based on language negotiation settings.
*
* @param $type
* The language type.
*
* @return
* The negotiated language object.
*/
function
language_initialize
(
$type
)
{
// Execute the language providers in the order they were set up and return the
// first valid language found.
$negotiation
=
variable_get
(
"language_negotiation_
$type
"
,
array
());
foreach
(
$negotiation
as
$id
=>
$provider
)
{
$language
=
language_provider_invoke
(
$id
,
$provider
);
if
(
$language
)
{
return
$language
;
}
}
// If no other language was found use the default one.
return
language_default
();
}
/**
* Rewrite URLs with language based prefix. Parameters are the same
* as those of the url() function.
* Default language provider.
*
* @return
* The default language code.
*/
function
language_url_rewrite
(
&
$path
,
&
$options
)
{
global
$language
;
function
language_from_default
()
{
return
language_default
()
->
language
;
}
/**
* Rewrite URLs allowing modules to hook in.
*
* @param $path
* The path to rewrite.
* @param $options
* An associative array of additional options as in url().
*/
function
language_url_rewrite
(
&
$path
,
&
$options
)
{
// Only modify relative (insite) URLs.
if
(
!
$options
[
'external'
])
{
static
$callbacks
;
// Language can be passed as an option, or we go for current language.
if
(
!
isset
(
$options
[
'language'
]))
{
$options
[
'language'
]
=
$language
;
}
if
(
!
isset
(
$callbacks
))
{
$callbacks
=
array
();
switch
(
variable_get
(
'language_negotiation'
,
LANGUAGE_NEGOTIATION_NONE
))
{
case
LANGUAGE_NEGOTIATION_NONE
:
// No language dependent path allowed in this mode.
unset
(
$options
[
'language'
]);
break
;
foreach
(
language_types_configurable
()
as
$type
)
{
// Get url rewriter callbacks only from enabled language providers.
$negotiation
=
variable_get
(
"language_negotiation_
$type
"
,
array
());
case
LANGUAGE_NEGOTIATION_DOMAIN
:
if
(
$options
[
'language'
]
->
domain
)
{
// Ask for an absolute URL with our modified base_url.
$options
[
'absolute'
]
=
TRUE
;
$options
[
'base_url'
]
=
$options
[
'language'
]
->
domain
;
}
break
;
foreach
(
$negotiation
as
$id
=>
$provider
)
{
if
(
isset
(
$provider
[
'file'
]))
{
require_once
DRUPAL_ROOT
.
'/'
.
$provider
[
'file'
];
}
ca
s
e
LANGUAGE_NEGOTIATION_PATH_DEFAULT
:
$default
=
language_default
();
if
(
$options
[
'language'
]
->
language
==
$default
->
language
)
{
break
;
// Avoid dupli
ca
t
e
callback entries.
if
(
isset
(
$provider
[
'callbacks'
][
'url_rewrite'
]))
{
$callbacks
[
$provider
[
'callbacks'
][
'url_rewrite'
]]
=
NULL
;
}
}
// Intentionally no break here.
}
ca
se
LANGUAGE_NEGOTIATION_PATH
:
if
(
!
empty
(
$options
[
'language'
]
->
prefix
))
{
$options
[
'prefix'
]
=
$options
[
'language'
]
->
prefix
.
'/'
;
}
break
;
$
ca
llbacks
=
array_keys
(
$callbacks
);
}
foreach
(
$callbacks
as
$callback
)
{
$callback
(
$path
,
$options
)
;
}
}
}
/**
* Split the given path into prefix and actual path.
*
* Parse the given path and return the language object identified by the
* prefix and the actual path.
*