5 * Internationalization (i18n) module
7 * @author Jose A. Reyero, 2004
12 * Module initialization
14 * Get language from path if exists and Initialize i18n system
19 global $i18n_langpath;
21 $path = i18n_get_original_path();
22 $i18n_langpath = i18n_get_lang_prefix($path);
23 $lang = _i18n_get_lang();
26 if ($path == '') { // Main page
27 if( variable_get('cache',0) && $lang != i18n_default_language() ) {
28 // Redirect to main page in $lang
30 } elseif (variable_get('i18n_frontpage',0)){
31 $_GET['q'] = i18n_frontpage();
34 elseif ($lang == $path) { // When path is only language code
35 $_GET['q'] = variable_get('i18n_frontpage',0) ?
i18n_frontpage() : variable_get('site_frontpage','node');
37 elseif ($i18n_langpath) {
38 //search alias with and without lang and remove lang
39 $_GET['q'] = i18n_get_normal_path($path);
42 // Multi table, for backwards compatibility and experimentation
43 if (variable_get('i18n_multi' , 0)) {
44 i18n_set_db_prefix(_i18n_get_lang());
47 // This function are only defined when module is enabled
48 // Otherwise, it messes up the module administration menu
49 function i18n_url_rewrite($path, $mode ='outgoing') {
50 if ($mode == 'outgoing' && !i18n_get_lang_prefix($path)) {
51 return i18n_get_lang() .
'/'.
$path;
55 } // End of i18n_url_rewrite
58 * This one expects to be called first from common.inc
59 * When first called, calls _i18n_init();
61 function i18n_get_lang() {
62 static
$i18n_language;
63 //see if the language is already set.
65 return $i18n_language;
68 return $i18n_language = _i18n_get_lang();
70 } // End of i18n_get_lang
75 * Check whether we are in bootstrap mode
77 function i18n_is_bootstrap(){
78 return !function_exists('drupal_get_headers');
82 * Second stage init, from common.inc
83 * Does some additional includes
85 function _i18n_init() {
86 // Include parts, may be conditional include in the future
87 include_once
'modules/i18n/i18n_node.inc';
88 include_once
'modules/i18n/i18n_taxonomy.inc';
91 * Common module functions
95 * Implementation of hook_help().
97 function i18n_help($section = 'admin/help#i18n' ) {
99 case
'admin/help#i18n' :
101 <p><strong>To be rewritten</strong></p>
102 <p>This module provides support for internationalization of Drupal sites:</p>
103 <ul><li>Translation of the user interface for anonymous users (combined with locale)</li>
104 <li>Multi-language for content. Adds a language field for nodes and taxonomy terms</li>
105 <li>Basic translation management</li>
106 <li>Browser language detection</li>
107 <li>Keeps the language setting accross consecutive requests, using URL rewriting</li>
108 <li>Provides a block for language selection and two theme functions: <i>i18n_flags</i> and <i>i18n_links</i></li></ul>
109 <p><small>Module developed by <a href="http://freelance.reyero.net">freelance.reyero.net</a></small></p>' );
111 case
'admin/modules#description' :
112 $output = t('Enables multilingual content. <b>Requires locale module for interface translation</b>' );
119 * Implementation of hook_settings().
121 function i18n_settings() {
122 global $db_prefix_i18n;
126 $output .
= form_radios(t('Supported languages'), 'i18n_supported_langs' , variable_get('i18n_supported_langs', 'i18n'),
128 'i18n' => t('Defined in the configuration file ($i18n_languages)'),
129 'locale' => t('Defined by the locale module')
130 ), t('Where to get the list of supported languages from' ));
132 $output .= form_radios(t('Simple content translation"), "i18n_content", variable_get("i18n_content", 0), array(t("Disabled"), t("Enabled")), t(' If enabled, prepends language code to url and searches for translated content. <strong>Requires url aliasing with 'path' module.</strong>"));
134 $output .
= form_radios(t('Browser language detection'), 'i18n_browser', variable_get('i18n_browser', 0), array(t('Disabled'), t('Enabled' )));
135 $output .
= form_select(t('Front page'), 'i18n_frontpage', variable_get('i18n_frontpage', 0), array(t('Default'), t('Language dependent')), t(" If 'language dependent' is selected, default front page will be prepended with language code, i.e. 'en/node'"));
137 $output .
= form_textfield(t('Language icons html tag'), 'i18n_flags', variable_get('i18n_flags', '<img class="i18n-flag" src="modules/i18n/flags/*.png" width="16" height="12"/>'), 70, 180,
138 t('HTML tag for flags. Asterisk \'*\' is a placeholder for language code. It should be something like <img class="i18n-flag" src="modules/i18n/flags/*.png" width="16" height="12"/>'));
140 $output .
= t('<h2>Multilingual content</h2>' );
141 foreach (node_list() as
$node) {
142 $subform.
=form_checkbox(t($node), 'i18n_node_'.
$node, 1, variable_get('i18n_node_'.
$node, 0));
144 $output .
= form_group(t('Nodes' ), $subform, t('Select node types to be translated.' ));
146 $output .
= t('<h2>Advanced Features</h2>' );
147 $output .
= t('<p>These are intended only for advanced users. Some changes to the database are required. Please, do read the INSTALL.txt and README.txt files before enabling these options</p>' );
150 // Language dependent tables
151 if (is_array($db_prefix_i18n)) {
153 $text = '<strong>'.
t('Current language dependent tables are: ').
implode(', ', array_keys($db_prefix_i18n)).
'</strong>' ;
155 $text = t("Check the module's SETUP.txt file.");
157 $output .
= form_radios(t('Language dependent tables'), 'i18n_multi', variable_get('i18n_multi', 0), array(t('Disabled'), t('Enabled')), t('If enabled, different tables for each language will be used. They must be defined in the configuration file.') .
' ' .
$text);
163 * Implementation of hook_menu().
165 function i18n_menu($may_cache) {
170 'path' => 'translation',
171 'title' => t('translation'),
172 'callback' => 'i18n_translation_page',
173 'access' => user_access('administer nodes'),
174 'type' => MENU_CALLBACK
);
175 /* Disabled term translations
177 'path' => 'admin/taxonomy/i18n',
178 'title' => t('translation'),
179 'callback' => 'i18n_taxonomy_admin',
180 'access' => user_access('administer taxonomy'),
181 'type' => MENU_LOCAL_TASK);
185 if (arg(0) == 'node' && is_numeric(arg(1)) && variable_get('i18n_node_'.
i18n_get_node_type(arg(1)), 0)) {
186 $access = user_access('administer nodes');
187 $type = MENU_LOCAL_TASK
;
189 'path' => 'node/'.
arg(1) .
'/translation',
190 'title' => t('translation'),
191 'callback' => 'i18n_node_translation',
198 } // End of i18n_menu
200 // translation/node/id/lang
201 // translation/term/id
203 function i18n_translation_page() {
204 $op = $_POST['op'] ?
$_POST['op'] : arg(1);
205 $edit = $_POST['edit'];
209 print theme('page', i18n_translation_add(arg(2), arg(3)));
213 $edit = node_validate($edit);
214 print theme('page', node_preview($edit), t('Preview'));
218 drupal_set_title(t('Submit'));
219 print theme('page', node_submit($edit));
223 print theme('page', node_delete($edit), t('Delete'));
227 // print theme('page', node_page_default(), '');
231 function i18n_translation_add($nid, $lang) {
232 $type = i18n_get_node_type($nid);
233 return node_add($type);
237 * Implementation of hook_link().
241 function i18n_link($type, $node = NULL
, $teaser = FALSE
) {
243 if ($type == 'node' && !$teaser && variable_get('i18n_node_'.
$node->type
, 0)) {
244 $languages = i18n_supported_languages();
245 $translations = i18n_node_get_translations($node->nid
);
246 foreach ($translations as
$lang => $trnode) {
247 $links[]= theme('i18n_link_name_flag', $lang, 'node/'.
$trnode->nid
);
254 function i18n_block($op = 'list', $delta = 0) {
256 $blocks[0]['info'] = t('Languages');
259 $blocks['subject'] = t('Languages');
260 $content = '<table><tr><td>' ;
261 $content .
= theme('i18n_links', 1, 1, '</td><td>', '</td></tr><tr><td>' );
262 $content .
= '</td></tr></table>' ;
263 $blocks['content'] =$content;
276 * Gets language, checking in order:
280 * 3. Browser language
281 * 4. Default language
284 function _i18n_get_lang() {
285 global $user, $i18n_langpath;
288 //see if the language is already set.
293 $languages = i18n_supported_languages();
295 if ($i18n_langpath && array_key_exists($i18n_langpath,$languages)) {
296 $i18n_lang = $i18n_langpath;
298 elseif ($user->uid
&& $user->language
&& array_key_exists($user->language
,$languages)) {
299 $i18n_lang = $user->language
;
301 elseif (variable_get("i18n_browser",0) && $lang=i18n_get_browser_lang()) {
305 $i18n_lang=key($languages);
312 * Get list of supported languages
314 function i18n_supported_languages() {
315 global $i18n_languages;
320 elseif (variable_get('i18n_supported_langs', 'i18n') == 'locale') {
321 $languages = i18n_locale_supported_languages();
324 elseif (is_array($i18n_languages)) {
325 return $languages = $i18n_languages;
333 * Returns default language
335 function i18n_default_language(){
336 return key(i18n_supported_languages());
340 * Returns list of enabled languages from locale module
342 * * Some code borrowed from locale module
344 function i18n_locale_supported_languages() {
346 $result = db_query('SELECT locale, name FROM {locales_meta} WHERE enabled = 1 ORDER BY isdefault DESC, name ASC');
347 while ($row = db_fetch_object($result)) {
348 $enabled[$row->locale
] = $row->name
;
355 * To get the original path.
356 * Cannot use $_GET["q"] cause it may have been already changed
358 function i18n_get_original_path() {
359 return isset($_REQUEST["q"]) ?
trim($_REQUEST["q"],"/") : '';
362 // Get language from browser settings, but only if it is in the $i18n_languages array
363 function i18n_get_browser_lang() {
364 $languages = i18n_supported_languages();
365 $accept=explode(',',array_shift( explode(";",$_SERVER["HTTP_ACCEPT_LANGUAGE"])));
366 foreach ($accept as
$lang) {
367 $lang=substr($lang,0,2);
368 if ( !empty($lang) && array_key_exists($lang,$languages)) {
375 * @name Theme functions
380 * Returns language links with optional flags
382 * @param $flags an integer, 1 to use language flags
383 * @param $names an integer, 1 to use language names
384 * @param $delim1 delimiter to place between language name and flag
385 * @param $delim2 delimiter to place between different languages
387 * @return a string containing the @a links output.
390 function theme_i18n_links($flags = 1, $names = 1, $delim1 = ' ' , $delim2 = ' ' ) {
391 $i18n_lang = i18n_get_lang();
392 $languages = i18n_supported_languages();
393 foreach ($languages as
$lang => $langname) {
394 $name = $names ?
t($langname): '' ; // Should be localized??
395 $flag= $flags ?
i18n_flag($lang) : '' ;
396 if ($lang == $i18n_lang) {
397 $links[] = "<strong>$name</strong>$delim1$flag";
400 $links[] = i18n_l($name, $lang).
$delim1.
i18n_l($flag, $lang);
403 $output =implode($delim2, $links);
407 function theme_i18n_flags() {
408 return theme_i18n_links(1, 0);
411 function theme_i18n_link_name_flag($lang, $path, $attributes = array()) {
413 if (!isset($languages)) {
414 $languages = i18n_supported_languages();
416 return '<span class="i18n-link">'.
l(t($languages[$lang]), $path, $attributes) .
' ' .
l(i18n_flag($lang) , $path, $attributes) .
'</span>';
421 * Creates links for different languages
424 function i18n_l($text, $lang , $url = '' , $attributes = array(), $query = NULL
) {
425 global $i18n_langpath;
426 // If !url get from original request
428 $url = i18n_get_original_path();
430 // If url has lang_prefix, remove it
431 if (i18n_get_lang_prefix($url)) {
432 $url = substr($url, 3);
434 // $result= l($text, $url, $attributes, $query);
435 return '<a href="'.
i18n_url($url, $lang, $query) .
'"'.
drupal_attributes($attributes) .
'>'.
$text .
'</a>';
438 function i18n_url($url, $lang, $query = NULL
) {
440 return url($lang.
'/'.
$url, $query);
442 return url($lang,$query);
446 function i18n_flag($lang, $attribs = array()) {
447 if ($path = variable_get('i18n_flags', '<img class="i18n-flag" src="modules/i18n/flags/*.png" width="16" height="12"/>')) {
448 $flag = str_replace('*' , $lang, $path);
449 if(!empty($attribs)){
450 $flag = str_replace('<','< '.
drupal_attributes($attribs).
' ',$flag);
459 * Manage language dependent variables
461 * Requires a patch in bootstrap.inc
463 function i18n_variable($name) {
464 global $i18n_variables;
465 if (is_array($i18n_variables) and
in_array($name, $i18n_variables)) {
466 return i18n_get_lang().
'_'.
$name ;
473 * Get language code from path.
475 * It doesn't check anymore for valid languages, so any two letter code at the beginning of the path will be taken as a lang code
477 function i18n_get_lang_prefix($path) {
478 if (preg_match("/^\w\w($|\/.*)/", $path)) {
479 return substr($path, 0, 2);
484 * Sets db_prefix to given language
486 function i18n_set_db_prefix($lang) {
487 global $db_prefix, $db_prefix_i18n;
488 if (is_array($db_prefix_i18n)) {
489 $db_prefix = array_merge($db_prefix, str_replace('**', $lang, $db_prefix_i18n));
494 * Language dependent front page
496 function i18n_frontpage() {
497 $path = _i18n_get_lang().
'/'.
variable_get('site_frontpage','node');
498 return i18n_get_normal_path($path);
502 * This function is similar to drupal_get_normal_path, but language-aware
503 * Also removes language from path
505 function i18n_get_normal_path($path) {
506 // First, check alias with lang
507 if (($map = drupal_get_path_map()) && isset($map[$path])) {
509 } elseif (i18n_get_lang_prefix($path)) {
510 // Check alias without lang
511 $path = trim(substr($path,3),'/');
512 if( isset($map[$path])) {
516 // We only get here when no alias is defined, with or without lang