Reworked init system, updated patches
[project/i18n.git] / i18n.module
1 <?php
2 // $Id$
3
4 /**
5 * Internationalization (i18n) module
6 *
7 * @author Jose A. Reyero, 2004
8 *
9 */
10
11 /**
12 * Module initialization
13 *
14 * Get language from path if exists and Initialize i18n system
15 *
16 */
17
18 function i18n_init(){
19 global $i18n_langpath;
20
21 $path = i18n_get_original_path();
22 $i18n_langpath = i18n_get_lang_prefix($path);
23 $lang = _i18n_get_lang();
24
25
26 if ($path == '') { // Main page
27 if( variable_get('cache',0) && $lang != i18n_default_language() ) {
28 // Redirect to main page in $lang
29 drupal_goto($lang);
30 } elseif (variable_get('i18n_frontpage',0)){
31 $_GET['q'] = i18n_frontpage();
32 }
33 }
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');
36 }
37 elseif ($i18n_langpath) {
38 //search alias with and without lang and remove lang
39 $_GET['q'] = i18n_get_normal_path($path);
40 }
41
42 // Multi table, for backwards compatibility and experimentation
43 if (variable_get('i18n_multi' , 0)) {
44 i18n_set_db_prefix(_i18n_get_lang());
45 }
46
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;
52 } else {
53 return $path;
54 }
55 } // End of i18n_url_rewrite
56
57 /**
58 * This one expects to be called first from common.inc
59 * When first called, calls _i18n_init();
60 */
61 function i18n_get_lang() {
62 static $i18n_language;
63 //see if the language is already set.
64 if ($i18n_language) {
65 return $i18n_language;
66 } else {
67 _i18n_init();
68 return $i18n_language = _i18n_get_lang();
69 }
70 } // End of i18n_get_lang
71
72 } // End of i18n_init
73
74 /**
75 * Check whether we are in bootstrap mode
76 */
77 function i18n_is_bootstrap(){
78 return !function_exists('drupal_get_headers');
79 }
80
81 /*
82 * Second stage init, from common.inc
83 * Does some additional includes
84 */
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';
89 }
90 /**
91 * Common module functions
92 */
93
94 /**
95 * Implementation of hook_help().
96 */
97 function i18n_help($section = 'admin/help#i18n' ) {
98 switch ($section) {
99 case 'admin/help#i18n' :
100 $output = t('
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>' );
110 break;
111 case 'admin/modules#description' :
112 $output = t('Enables multilingual content. <b>Requires locale module for interface translation</b>' );
113 break;
114 }
115 return $output;
116 }
117
118 /**
119 * Implementation of hook_settings().
120 */
121 function i18n_settings() {
122 global $db_prefix_i18n;
123
124 // Basic settings
125
126 $output .= form_radios(t('Supported languages'), 'i18n_supported_langs' , variable_get('i18n_supported_langs', 'i18n'),
127 array(
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' ));
131 /* Disabled
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>"));
133 */
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'"));
136
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 &lt;img class="i18n-flag" src="modules/i18n/flags/*.png" width="16" height="12"/&gt'));
139
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));
143 }
144 $output .= form_group(t('Nodes' ), $subform, t('Select node types to be translated.' ));
145
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>' );
148
149 // Advanced features
150 // Language dependent tables
151 if (is_array($db_prefix_i18n)) {
152 $multi=true;
153 $text = '<strong>'.t('Current language dependent tables are: '). implode(', ', array_keys($db_prefix_i18n)).'</strong>' ;
154 } else {
155 $text = t("Check the module's SETUP.txt file.");
156 }
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);
158
159 return $output;
160 }
161
162 /**
163 * Implementation of hook_menu().
164 */
165 function i18n_menu($may_cache) {
166 $items = array();
167
168 if ($may_cache) {
169 $items[] = array(
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
176 $items[] = array(
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);
182 */
183 }
184 else {
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;
188 $items[] = array(
189 'path' => 'node/'. arg(1) .'/translation',
190 'title' => t('translation'),
191 'callback' => 'i18n_node_translation',
192 'access' => $access,
193 'type' => $type);
194 }
195 }
196
197 return $items;
198 } // End of i18n_menu
199
200 // translation/node/id/lang
201 // translation/term/id
202
203 function i18n_translation_page() {
204 $op = $_POST['op'] ? $_POST['op'] : arg(1);
205 $edit = $_POST['edit'];
206
207 switch ($op) {
208 case 'node':
209 print theme('page', i18n_translation_add(arg(2), arg(3)));
210 break;
211
212 case t('Preview'):
213 $edit = node_validate($edit);
214 print theme('page', node_preview($edit), t('Preview'));
215 break;
216
217 case t('Submit'):
218 drupal_set_title(t('Submit'));
219 print theme('page', node_submit($edit));
220 break;
221
222 case t('Delete'):
223 print theme('page', node_delete($edit), t('Delete'));
224 break;
225
226 default:
227 // print theme('page', node_page_default(), '');
228 }
229 }
230
231 function i18n_translation_add($nid, $lang) {
232 $type = i18n_get_node_type($nid);
233 return node_add($type);
234 }
235
236 /**
237 * Implementation of hook_link().
238 */
239
240
241 function i18n_link($type, $node = NULL, $teaser = FALSE) {
242 $links = array();
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);
248 }
249 }
250 return $links;
251 }
252
253
254 function i18n_block($op = 'list', $delta = 0) {
255 if ($op == 'list') {
256 $blocks[0]['info'] = t('Languages');
257 }
258 else {
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;
264 }
265
266 return $blocks;
267 }
268
269
270 /**
271 * i18n api
272 */
273
274
275 /**
276 * Gets language, checking in order:
277 *
278 * 1. Path language
279 * 2. User language
280 * 3. Browser language
281 * 4. Default language
282 */
283
284 function _i18n_get_lang() {
285 global $user, $i18n_langpath;
286 static $i18n_lang;
287
288 //see if the language is already set.
289 if ($i18n_lang) {
290 return $i18n_lang;
291 }
292
293 $languages = i18n_supported_languages();
294
295 if ($i18n_langpath && array_key_exists($i18n_langpath,$languages)) {
296 $i18n_lang = $i18n_langpath;
297 }
298 elseif ($user->uid && $user->language && array_key_exists($user->language,$languages)) {
299 $i18n_lang = $user->language;
300 }
301 elseif (variable_get("i18n_browser",0) && $lang=i18n_get_browser_lang()) {
302 $i18n_lang=$lang;
303 }
304 else {
305 $i18n_lang=key($languages);
306 }
307
308 return $i18n_lang;
309 }
310
311 /**
312 * Get list of supported languages
313 */
314 function i18n_supported_languages() {
315 global $i18n_languages;
316 static $languages;
317 if ($languages) {
318 return $languages;
319 }
320 elseif (variable_get('i18n_supported_langs', 'i18n') == 'locale') {
321 $languages = i18n_locale_supported_languages();
322 return $languages;
323 }
324 elseif (is_array($i18n_languages)) {
325 return $languages = $i18n_languages;
326 }
327 else {
328 return array();
329 }
330 }
331
332 /**
333 * Returns default language
334 */
335 function i18n_default_language(){
336 return key(i18n_supported_languages());
337 }
338
339 /**
340 * Returns list of enabled languages from locale module
341 *
342 * * Some code borrowed from locale module
343 */
344 function i18n_locale_supported_languages() {
345 $enabled = array();
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;
349 }
350 return $enabled;
351 }
352
353
354 /**
355 * To get the original path.
356 * Cannot use $_GET["q"] cause it may have been already changed
357 */
358 function i18n_get_original_path() {
359 return isset($_REQUEST["q"]) ? trim($_REQUEST["q"],"/") : '';
360 }
361
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)) {
369 return $lang;
370 }
371 }
372 }
373
374 /**
375 * @name Theme functions
376 * @{
377 */
378
379 /**
380 * Returns language links with optional flags
381 *
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
386 *
387 * @return a string containing the @a links output.
388 */
389
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";
398 }
399 else {
400 $links[] = i18n_l($name, $lang).$delim1.i18n_l($flag, $lang);
401 }
402 }
403 $output =implode($delim2, $links);
404 return $output;
405 }
406
407 function theme_i18n_flags() {
408 return theme_i18n_links(1, 0);
409 }
410
411 function theme_i18n_link_name_flag($lang, $path, $attributes = array()) {
412 static $languages;
413 if (!isset($languages)) {
414 $languages = i18n_supported_languages();
415 }
416 return '<span class="i18n-link">'. l(t($languages[$lang]), $path, $attributes) . '&nbsp;' . l(i18n_flag($lang) , $path, $attributes) .'</span>';
417 }
418
419
420 /*
421 * Creates links for different languages
422 */
423
424 function i18n_l($text, $lang , $url = '' , $attributes = array(), $query = NULL) {
425 global $i18n_langpath;
426 // If !url get from original request
427 if (!$url) {
428 $url = i18n_get_original_path();
429 }
430 // If url has lang_prefix, remove it
431 if (i18n_get_lang_prefix($url)) {
432 $url = substr($url, 3);
433 }
434 // $result= l($text, $url, $attributes, $query);
435 return '<a href="'. i18n_url($url, $lang, $query) .'"'. drupal_attributes($attributes) .'>'. $text .'</a>';
436 }
437
438 function i18n_url($url, $lang, $query = NULL) {
439 if ($url) {
440 return url($lang.'/'.$url, $query);
441 } else {
442 return url($lang,$query);
443 }
444 }
445
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);
451 }
452 return $flag;
453 }
454 }
455 /* @} */
456
457
458 /**
459 * Manage language dependent variables
460 *
461 * Requires a patch in bootstrap.inc
462 */
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 ;
467 } else {
468 return $name;
469 }
470 }
471
472 /**
473 * Get language code from path.
474 *
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
476 */
477 function i18n_get_lang_prefix($path) {
478 if (preg_match("/^\w\w($|\/.*)/", $path)) {
479 return substr($path, 0, 2);
480 }
481 }
482
483 /**
484 * Sets db_prefix to given language
485 */
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));
490 }
491 }
492
493 /**
494 * Language dependent front page
495 */
496 function i18n_frontpage() {
497 $path = _i18n_get_lang().'/'.variable_get('site_frontpage','node');
498 return i18n_get_normal_path($path);
499 }
500
501 /**
502 * This function is similar to drupal_get_normal_path, but language-aware
503 * Also removes language from path
504 */
505 function i18n_get_normal_path($path) {
506 // First, check alias with lang
507 if (($map = drupal_get_path_map()) && isset($map[$path])) {
508 return $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])) {
513 return $map[$path];
514 }
515 }
516 // We only get here when no alias is defined, with or without lang
517 return $path;
518 }
519
520 ?>