Fixed [#514630].
[project/i18n.git] / i18n.module
CommitLineData
78e8cd8d
JR
1<?php
2// $Id$
55209b2f 3
78e8cd8d 4/**
afcc0a25 5 * @file
4c4a8511 6 * Internationalization (i18n) module.
78e8cd8d 7 *
818652bd
JR
8 * This module extends multilingual support being the base module for the i18n package.
9 * - Multilingual variables
10 * - Extended languages for nodes
11 * - Extended language API
136d636d 12 *
78e8cd8d
JR
13 * @author Jose A. Reyero, 2004
14 */
15
afcc0a25
JR
16// Some constants. Language support modes for content
17define('LANGUAGE_SUPPORT_NONE', 0);
18define('LANGUAGE_SUPPORT_NORMAL', 1);
19define('LANGUAGE_SUPPORT_EXTENDED', 2);
5196f381 20define('LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED', 3);
3314df44 21
90d529a0 22/**
ec427688
JR
23 * Implementation of hook_boot()
24 *
25 * Initialize variables, that will be used to decide on site_frontpage
26 */
27function i18n_boot() {
28 drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
29 _i18n_init();
30}
31
32/**
98f49db3 33 * Implementation of hook_init().
136d636d 34 *
98f49db3 35 * Special fix for site_frontpage, that may have been used before the language variables are loaded.
937ec95a 36 */
98f49db3 37function i18n_init() {
ec427688 38 // If not in bootstrap, variable init. Otherwise we are serving a cached page so we don't need anything else.
98f49db3 39 if (!_i18n_is_bootstrap()) {
ec427688 40 _i18n_init(TRUE);
f8f1d8cf
JR
41 _i18n_init_mode();
42 }
43}
44
45/**
46 * Initialize multilingual variables and use them for site_frontpage
47 *
48 * Special fix for site_frontpage, that may have been used before the language variables are loaded.
49 */
ec427688 50function _i18n_init($check_frontpage = FALSE) {
27bba143 51 static $done, $default_frontpage;
f8f1d8cf
JR
52
53 // Prevent this function from running twice;
54 if (!isset($done)) {
55 $done = TRUE;
b28c2734 56 $default_frontpage = variable_get('site_frontpage', 'node');
b28c2734 57 i18n_variable_init();
27bba143
JR
58 }
59 // We do aditional frontpage check if this has run after first bootstrap phase.
60 // But if this runs in hook_boot we should be ok
61 if ($check_frontpage && $default_frontpage != variable_get('site_frontpage', 'node') && $_GET['q'] == drupal_get_normal_path($default_frontpage)) {
62 $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
afcc0a25 63 }
90d529a0 64}
3128d317 65
6681618a 66/**
f8f1d8cf
JR
67 * Initialize selection mode
68 */
69function _i18n_init_mode() {
70 if (i18n_selection_mode() != 'off') {
71 // Node language when loading specific nodes or creating translations.
72 if (arg(0) == 'node' ) {
73 if (($node = menu_get_object('node')) && $node->language) {
74 i18n_selection_mode('node', $node->language);
75 }
76 elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language'])) {
77 i18n_selection_mode('translation', db_escape_string($_GET['language']));
78 }
79 }
80 elseif (arg(0) == 'admin') {
81 // There are some exceptions for admin pages.
82 if (arg(1) == 'content' && user_access('administer all languages')) {
83 // No restrictions for administration pages.
84 i18n_selection_mode('off');
85 }
95c3e7df 86 elseif (arg(1) == 'build' && (arg(2) == 'menu-customize' || arg(2) == 'menu')) {
f8f1d8cf
JR
87 // All nodes available when editing custom menu items.
88 i18n_selection_mode('off');
89 }
90 }
91 }
92}
93/**
6681618a
JR
94 * Implementation of hook_help().
95 */
affdb36b
JR
96function i18n_help($path = 'admin/help#i18n', $arg) {
97 switch ($path) {
d469e0bb 98 case 'admin/help#i18n' :
98f49db3 99 $output = '<p>'. t('This module improves support for multilingual content in Drupal sites:') .'</p>';
98e35ee4 100 $output .= '<ul>';
10973c0b 101 $output .= '<li>'. t('Shows content depending on page language.') .'</li>';
102 $output .= '<li>'. t('Handles multilingual variables.') .'</li>';
98f49db3 103 $output .= '<li>'. t('Extended language option for chosen content types. For these content types transations will be allowed for all defined languages, not only for enabled ones.') .'</li>';
1d637c88 104 $output .= '<li>'. t('Provides a block for language selection and two theme functions: <em>i18n_flags</em> and <em>i18n_links</em>.') .'</li>';
818652bd 105 $output .= '</ul>';
98f49db3 106 $output .= '<p>'. t('This is the base module for several others adding different features:') .'</p>';
136d636d 107 $output .= '<ul>';
77fa73f1 108 $output .= '<li>'. t('Multilingual menu items.') .'</li>';
109 $output .= '<li>'. t('Multilingual taxonomy adds a language field for taxonomy vocabularies and terms.') .'</li>';
98e35ee4 110 $output .= '</ul>';
98f49db3 111 $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) .'</p>';
98e35ee4 112 return $output;
ac10f554 113
0b07a5e3 114 case 'admin/settings/language/i18n':
818652bd 115 $output = '<ul>';
98f49db3 116 $output .= '<li>'. t('To enable multilingual support for specific content types go to <a href="@configure_content_types">configure content types</a>.', array('@configure_content_types' => url('admin/content/types'))) .'</li>';
117 $output .= '</ul>';
38f996de 118 return $output;
6681618a 119 }
6681618a
JR
120}
121
122/**
8dcb741b 123 * Implementation of hook_menu().
8dcb741b 124 */
afcc0a25 125function i18n_menu() {
0b07a5e3 126 $items['admin/settings/language/i18n'] = array(
afcc0a25 127 'title' => 'Multilingual system',
818652bd
JR
128 'description' => 'Configure extended options for multilingual content and translations.',
129 'page callback' => 'drupal_get_form',
130 'page arguments' => array('i18n_admin_settings'),
131 'access arguments' => array('administer site configuration'),
132 'file' => 'i18n.admin.inc',
e40ff52f 133 'type' => MENU_LOCAL_TASK,
0b07a5e3 134 'weight' => 10,
0173ca0a 135 );
0b07a5e3
JR
136 $items['admin/settings/language/i18n/configure'] = array(
137 'title' => 'Multilingual system',
138 'description' => 'Configure extended options for multilingual content and translations.',
139 //'page callback' => 'drupal_get_form',
140 //'page arguments' => array('i18n_admin_settings'),
141 //'access arguments' => array('administer site configuration'),
142 'file' => 'i18n.admin.inc',
143 'type' => MENU_DEFAULT_LOCAL_TASK,
afcc0a25 144 );
1d40cadd
JR
145 // Autocomplete callback for nodes
146 $items['i18n/node/autocomplete'] = array(
147 'title' => 'Node title autocomplete',
148 'page callback' => 'i18n_node_autocomplete',
149 'access arguments' => array('access content'),
150 'type' => MENU_CALLBACK,
151 'file' => 'i18n.pages.inc',
152 );
8dcb741b
JR
153 return $items;
154}
98e35ee4 155
8dcb741b 156/**
818652bd 157 * Implementation of hook_menu_alter().
136d636d 158 *
98f49db3 159 * Take over the node translation page.
818652bd
JR
160 */
161function i18n_menu_alter(&$items) {
818652bd
JR
162 $items['node/%node/translate']['page callback'] = 'i18n_translation_node_overview';
163 $items['node/%node/translate']['file'] = 'i18n.pages.inc';
164 $items['node/%node/translate']['module'] = 'i18n';
818652bd
JR
165}
166
167/**
98e35ee4
JR
168 * Implementation of hook_nodeapi().
169 */
3314df44
JR
170function i18n_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
171 global $language;
172
173 if (variable_get('language_content_type_' . $node->type, 0)) {
174 // Set current language for new nodes if option enabled
175 if ($op == 'prepare' && empty($node->nid) && empty($node->language) && variable_get('i18n_newnode_current_' . $node->type, 0)) {
176 $node->language = $language->language;
177 }
178 }
179}
180
98e35ee4 181/**
818652bd
JR
182 * Implementation of hook_alter_translation_link().
183 *
98f49db3 184 * Handles links for extended language. The links will have current language.
818652bd
JR
185 */
186function i18n_translation_link_alter(&$links, $path) {
187 global $language;
136d636d 188
818652bd
JR
189 // Check for a node related path, and for its translations.
190 if ((preg_match("!^node/([0-9]+)(/.+|)$!", $path, $matches)) && ($node = node_load((int)$matches[1])) && !empty($node->tnid)) {
1d40cadd 191 // make sure language support is set to LANGUAGE_SUPPORT_EXTENDED, so links
5196f381
JR
192 // dont get added for LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED
193 if (variable_get('i18n_node_'. $node->type, LANGUAGE_SUPPORT_NORMAL) == LANGUAGE_SUPPORT_EXTENDED) {
194 $languages = language_list();
195 $extended = array();
196 foreach (translation_node_get_translations($node->tnid) as $langcode => $translation_node) {
197 if (!isset($links[$langcode]) && isset($languages[$langcode])) {
198 $extended[$langcode] = array(
199 'href' => 'node/'. $translation_node->nid . $matches[2],
200 'language' => $language,
201 'language_icon' => $languages[$langcode],
202 'title' => $languages[$langcode]->native,
203 'attributes' => array('class' => 'language-link'),
204 );
205 }
818652bd 206 }
5196f381
JR
207 // This will run after languageicon module, so we add icon in case that one is enabled.
208 if ($extended && function_exists('languageicons_translation_link_alter')) {
209 languageicons_translation_link_alter($extended, $path);
210 }
211 $links = array_merge($links, $extended);
818652bd 212 }
818652bd
JR
213 }
214}
215
216/**
217 * Implementation of hook_link_alter().
218 *
98f49db3 219 * Handles links for extended languages. Sets current interface language.
818652bd
JR
220 */
221function i18n_link_alter(&$links, $node) {
222 global $language;
136d636d 223
5196f381
JR
224 $language_support = variable_get('i18n_node_'. $node->type, LANGUAGE_SUPPORT_NORMAL);
225
226 // Hide node translation links.
227 if (variable_get('i18n_hide_translation_links', 0) == 1) {
228 foreach ($links as $module => $link) {
229 if (strpos($module, 'node_translation') === 0) {
230 unset($links[$module]);
231 }
232 }
233 }
234
1e456352 235 if (!empty($node->tnid)) {
818652bd 236 foreach (array_keys(i18n_language_list('extended')) as $langcode) {
98f49db3 237 $index = 'node_translation_'. $langcode;
818652bd 238 if (!empty($links[$index])) {
5196f381
JR
239 if ($language_support != LANGUAGE_SUPPORT_EXTENDED && $links[$index]['language']->enabled == 0) {
240 unset($links[$index]);
241 }
242 else {
243 $links[$index]['language'] = $language;
244 }
818652bd
JR
245 }
246 }
247 }
248}
249
250/**
98f49db3 251 * Implementation of hook_user().
136d636d 252 *
98f49db3 253 * Switch to user's language after login.
afcc0a25
JR
254 */
255function i18n_user($op, &$edit, &$account, $category = NULL) {
98f49db3 256 if ($op == 'login' && $account->language) {
afcc0a25
JR
257 $_SESSION['language'] = $account->language;
258 i18n_get_lang($account->language);
98e35ee4 259 }
74c2972e
JR
260}
261
262/**
ac152d40
NR
263 * Implementation of hook_elements().
264 *
265 * Add a process callback for textfields.
266 */
267function i18n_elements() {
268 $type = array();
269 $type['textfield'] = array('#process' => array('i18n_textfield_process'));
270 return $type;
271}
272
273/**
274 * Process callback for textfield elements.
275 *
276 * When editing or translating a node, set Javascript to rewrite autocomplete
277 * paths to use the node language prefix rather than the current content one.
278 */
279function i18n_textfield_process($element) {
280 global $language;
281 static $sent = FALSE;
282
283 // Ensure we send the Javascript only once.
284 if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) {
285 // Add a JS file for node forms.
286 // Determine if we are either editing or translating an existing node.
287 // We can't act on regular node creation because we don't have a specified
288 // node language.
289 $node_edit = $node = menu_get_object() && arg(2) == 'edit' && isset($node->language) && !empty($node->language);
290 $node_translate = arg(0) == 'node' && arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language']);
291 if ($node_edit || $node_translate) {
292 $node_language = $node_edit ? $node->language : $_GET['language'];
293 // Only needed if the node language is different from the interface one.
2d823751 294 if ($node_language != $language->language) {
ac152d40
NR
295 $languages = language_list();
296 if (isset($languages[$node_language])) {
297 drupal_add_js(drupal_get_path('module', 'i18n') . '/i18n.js');
298 // Pass the interface and content language base paths.
299 // Remove any trailing forward slash. Doing so prevents a mismatch
300 // that occurs when a language has no prefix and hence gets a path
301 // with a trailing forward slash.
302 $interface = rtrim(url('', array('absolute' => TRUE)), '/');
303 $content = rtrim(url('', array('absolute' => TRUE, 'language' => $languages[$node_language])), '/');
304 $data = array('interface_path' => $interface, 'content_path' => $content);
305 drupal_add_js(array('i18n' => $data), 'setting');
306 }
307 }
308 }
309 $sent = TRUE;
310 }
311 return $element;
312}
313
314/**
81f2a50c 315 * Simple i18n API
78e8cd8d 316 */
6681618a 317
98e35ee4 318/**
98f49db3 319 * Get language properties.
136d636d 320 *
98e35ee4 321 * @param $code
98f49db3 322 * Language code.
98e35ee4
JR
323 * @param $property
324 * It may be 'name', 'native', 'ltr'...
325 */
818652bd
JR
326function i18n_language_property($code, $property) {
327 $languages = language_list();
16ae3caa 328 return isset($languages[$code]->$property) ? $languages[$code]->$property : NULL;
98e35ee4
JR
329}
330
331/**
98f49db3 332 * Get node language.
90d529a0 333 */
90d529a0 334function i18n_node_get_lang($nid, $default = '') {
98f49db3 335 $lang = db_result(db_query('SELECT language FROM {node} WHERE nid = %d', $nid));
90d529a0
JR
336 return $lang ? $lang : $default ;
337}
338
339/**
98f49db3 340 * Get allowed languages for node.
136d636d 341 *
98f49db3 342 * This allows node types to define its own language list implementing hook 'language_list'.
136d636d 343 *
818652bd 344 * @param $node
98f49db3 345 * Node to retrieve language list for.
818652bd 346 * @param $translate
98f49db3 347 * Only languages available for translation. Filter out existing translations.
818652bd
JR
348 */
349function i18n_node_language_list($node, $translate = FALSE) {
98f49db3 350 // Check if the node module manages its own language list.
818652bd 351 $languages = node_invoke($node, 'language_list', $translate);
0173ca0a 352
818652bd 353 if (!$languages) {
0698d0c8 354 if (variable_get('i18n_node_'. $node->type, 0) >= LANGUAGE_SUPPORT_EXTENDED) {
818652bd 355 $languages = locale_language_list('name', TRUE); // All defined languages
98f49db3 356 }
357 else {
818652bd
JR
358 $languages = locale_language_list(); // All enabled languages
359 }
360 if ($translate && isset($node->tnid) && $node->tnid && ($translations = translation_node_get_translations($node->tnid))) {
361 unset($translations[$node->language]);
362 foreach (array_keys($translations) as $langcode) {
363 unset($languages[$langcode]);
364 }
365 }
3314df44
JR
366 // Language may be locked for this node type, restrict options to current one
367 if (variable_get('i18n_lock_node_' . $node->type, 0) && !empty($node->language) && !empty($languages[$node->language])) {
368 $languages = array($node->language => $languages[$node->language]);
369 }
370 // Check language required for this type (no language neutral)
371 elseif (!variable_get('i18n_required_node_' . $node->type, 0)) {
372 $languages = array('' => t('Language neutral')) + $languages;
0173ca0a 373 }
afcc0a25 374 }
818652bd
JR
375
376 return $languages;
90d529a0
JR
377}
378
379/**
ee361f1b 380 * Selection mode for content.
136d636d 381 *
98f49db3 382 * Warning: when used with params they need to be escaped, as some values are thrown directly in queries.
136d636d 383 *
98f49db3 384 * Allows several modes for query rewriting and to change them programatically.
385 * off = No language conditions inserted.
386 * simple = Only current language and no language.
387 * mixed = Only current and default languages.
388 * strict = Only current language.
389 * default = Only default language.
390 * user = User defined, in the module's settings page.
391 * params = Gets the stored params.
392 * reset = Returns to previous.
393 * custom = add custom where clause, like "%alias.language = 'en'".
394 */
395function i18n_selection_mode($mode = NULL, $params = NULL) {
f8f1d8cf 396 static $current_mode;
8dcb741b
JR
397 static $current_value = '';
398 static $store = array();
136d636d 399
f8f1d8cf
JR
400 // Initialization, first time this runs
401 if (!isset($current_mode)) {
ee361f1b 402 $current_mode = variable_get('i18n_selection_mode', 'simple');
ee361f1b 403 }
136d636d 404
98f49db3 405 if (!$mode) {
8dcb741b 406 return $current_mode;
98f49db3 407 }
408 elseif ($mode == 'params') {
8dcb741b 409 return $current_value;
98f49db3 410 }
411 elseif ($mode == 'reset') {
8dcb741b 412 list($current_mode, $current_value) = array_pop($store);
98f49db3 413 }
414 else {
8dcb741b
JR
415 array_push($store, array($current_mode, $current_value));
416 $current_mode = $mode;
417 $current_value = $params;
136d636d 418 }
81f2a50c 419}
74c2972e
JR
420
421/**
98f49db3 422 * Implementation of hook_db_rewrite_sql().
136d636d 423 *
98f49db3 424 * Rewrite node queries so language selection options are enforced.
74c2972e 425 */
98f49db3 426function i18n_db_rewrite_sql($query, $primary_table, $primary_key, $args = array()) {
427 // If mode is 'off' = no rewrite, we cannot return any empty 'where' string so check here.
88d92733
JR
428 $mode = i18n_selection_mode();
429 if ($mode == 'off') return;
136d636d 430
98f49db3 431 // Disable language conditions for views.
432 if (array_key_exists('view', $args)) return;
a1bb6aa6 433
74c2972e
JR
434 switch ($primary_table) {
435 case 'n':
436 case 'node':
98f49db3 437 // No rewrite for queries with subselect ? (views count queries).
438 // @ TO DO Actually these queries look un-rewrittable, check with other developers.
5aaad3de 439 if (preg_match("/FROM \(SELECT/", $query)) return;
98f49db3 440 // No rewrite for translation module queries.
818652bd 441 if (preg_match("/.*FROM {node} $primary_table WHERE.*$primary_table\.tnid/", $query)) return;
98f49db3 442 // When loading specific nodes, language conditions shouldn't apply.
afcc0a25 443 if (preg_match("/WHERE.*\s$primary_table.nid\s*=\s*(\d|%d)/", $query)) return;
98f49db3 444 // If language conditions already there, get out.
afcc0a25 445 if (preg_match("/i18n/", $query)) return;
136d636d 446
447
0ef28a15 448 // Mixed mode is a bit more complex, we need to join in one more table
98f49db3 449 // and add some more conditions, but only if language is not default.
a838e19d 450 if ($mode == 'mixed') {
0ef28a15 451 $result['where'] = i18n_db_rewrite_where($primary_table, 'node', 'simple');
a838e19d 452 if (i18n_get_lang() != i18n_default_language()) {
6bdc58de 453 $result['join'] = "LEFT JOIN {node} i18n ON $primary_table.tnid > 0 AND $primary_table.tnid = i18n.tnid AND i18n.language = '". i18n_get_lang() ."'";
a838e19d 454 // So we show also nodes that have default language.
6bdc58de 455 $result['where'] .= " OR $primary_table.language = '". i18n_default_language() ."' AND i18n.nid IS NULL";
a838e19d 456 }
98f49db3 457 }
458 else {
0ef28a15
JR
459 $result['where'] = i18n_db_rewrite_where($primary_table, 'node', $mode);
460 }
afcc0a25 461 return $result;
74c2972e
JR
462 }
463}
464
98e35ee4 465/**
98f49db3 466 * Rewrites queries depending on rewriting mode.
98e35ee4 467 */
98f49db3 468function i18n_db_rewrite_where($alias, $type, $mode = NULL) {
afcc0a25 469 if (!$mode) {
98f49db3 470 // Some exceptions for query rewrites.
afcc0a25 471 $mode = i18n_selection_mode();
136d636d 472 }
0ef28a15 473
98f49db3 474 // Get languages to simplify query building.
0ef28a15
JR
475 $current = i18n_get_lang();
476 $default = i18n_default_language();
136d636d 477
88d92733 478 if ($mode == 'strict' && $type != 'node') {
98f49db3 479 // Special case. Selection mode is 'strict' but this should be only for node queries.
88d92733 480 $mode = 'simple';
136d636d 481 }
0ef28a15 482 elseif ($mode == 'mixed' && $current == $default) {
98f49db3 483 // If mode is mixed but current = default, is the same as 'simple'.
0ef28a15
JR
484 $mode = 'simple';
485 }
136d636d 486
98f49db3 487 switch ($mode) {
afcc0a25
JR
488 case 'off':
489 return '';
98f49db3 490
74c2972e 491 case 'simple':
0ef28a15 492 return "$alias.language ='$current' OR $alias.language ='' OR $alias.language IS NULL" ;
98f49db3 493
74c2972e 494 case 'mixed':
0ef28a15 495 return "$alias.language ='$current' OR $alias.language ='$default' OR $alias.language ='' OR $alias.language IS NULL" ;
98f49db3 496
74c2972e 497 case 'strict':
0ef28a15 498 return "$alias.language ='$current'" ;
98f49db3 499
74c2972e
JR
500 case 'node':
501 case 'translation':
98f49db3 502 return "$alias.language ='". i18n_selection_mode('params') ."' OR $alias.language ='' OR $alias.language IS NULL" ;
503
74c2972e 504 case 'default':
0ef28a15 505 return "$alias.language ='$default' OR $alias.language ='' OR $alias.language IS NULL" ;
98f49db3 506
74c2972e 507 case 'custom':
afcc0a25 508 return str_replace('%alias', $alias, i18n_selection_mode('params'));
136d636d 509 }
74c2972e
JR
510}
511
512/**
bbb1a8ec
JR
513 * Implementation of hook_preprocess_page().
514 *
515 * Add the language code to the classes for the <body> tag. Unfortunately, some
516 * themes will not respect the variable we're modifying to achieve this - in
517 * particular, Garland and Minelli do not.
518 */
519function i18n_preprocess_page(&$variables) {
520 if (isset($variables['body_classes'])) {
521 global $language;
522 $variables['body_classes'] .= ' i18n-' . $language->language;
523 }
524}
525
526/**
98f49db3 527 * Implementation of hook_exit().
74c2972e 528 */
98f49db3 529function i18n_exit() {
38f996de 530 _i18n_variable_exit();
74c2972e
JR
531}
532
533/**
818652bd 534 * Implementation of hook_form_alter();
136d636d 535 *
98f49db3 536 * This is the place to add language fields to all forms.
74c2972e 537 */
afcc0a25 538function i18n_form_alter(&$form, $form_state, $form_id) {
3314df44
JR
539 global $language;
540
98f49db3 541 switch ($form_id) {
542 case 'node_type_form':
3314df44
JR
543 $disabled = !variable_get('language_content_type_'. $form['#node_type']->type, 0);
544 $form['i18n'] = array(
545 '#type' => 'fieldset',
546 '#title' => t('Multilanguage options'),
547 '#collapsible' => TRUE,
548 '#collapsed' => TRUE,
549 '#description' => t('Extended multilingual options provided by Internationalization module.'),
550 '#disabled' => $disabled,
551 );
552 // Add disabled message
553 if ($disabled) {
0173ca0a 554 $form['i18n']['#description'] .= ' <em>' . t('These will be available only when you enable Multilingual support in Workflow settings above.') . '</em>';
3314df44
JR
555 }
556 // Some settings about node languages
557 $form['i18n']['options'] = array(
558 '#title' => t('Options for node language'),
559 '#type' => 'fieldset',
560 '#disabled' => $disabled,
561 );
562 $form['i18n']['options']['i18n_newnode_current'] = array(
563 '#type' => 'checkbox',
564 '#title' => t('Set current language as default for new content.'),
565 '#default_value' => variable_get('i18n_newnode_current_' . $form['#node_type']->type, 0),
566 '#disabled' => $disabled,
567 );
568 $form['i18n']['options']['i18n_required_node'] = array(
569 '#type' => 'checkbox',
570 '#title' => t('Require language (Do not allow Language Neutral).'),
571 '#default_value' => variable_get('i18n_required_node_' . $form['#node_type']->type, 0),
572 '#disabled' => $disabled,
573 );
574 $form['i18n']['options']['i18n_lock_node'] = array(
575 '#type' => 'checkbox',
576 '#title' => t('Lock language (Cannot be changed).'),
577 '#default_value' => variable_get('i18n_lock_node_' . $form['#node_type']->type, 0),
578 '#disabled' => $disabled,
579 );
98f49db3 580 // Add extended language support option to content type form.
3314df44 581 $form['i18n']['i18n_node'] = array(
98f49db3 582 '#type' => 'radios',
583 '#title' => t('Extended language support'),
584 '#default_value' => variable_get('i18n_node_'. $form['#node_type']->type, LANGUAGE_SUPPORT_NORMAL),
585 '#options' => _i18n_content_language_options(),
3314df44
JR
586 '#description' => t('If enabled, all defined languages will be allowed for this content type in addition to only enabled ones. This is useful to have more languages for content than for the interface.'),
587 '#disabled' => $disabled,
98f49db3 588 );
589 break;
afcc0a25 590
74c2972e 591 default:
a1887ef3 592 // Extensions for node edit forms
818652bd 593 if (isset($form['#id']) && $form['#id'] == 'node-form') {
a1887ef3 594 if (isset($form['#node']->type)) {
3314df44
JR
595 if (variable_get('language_content_type_'. $form['#node']->type, 0)) {
596 if (!empty($form['language']['#options'])) {
597 $form['language']['#options'] = i18n_node_language_list($form['#node'], TRUE);
598 }
98f49db3 599 }
3314df44
JR
600 elseif (!isset($form['#node']->nid)) {
601 // Set language to empty for not multilingual nodes when creating
602 $form['language'] = array('#type' => 'value', '#value' => '');
74c2972e 603 }
74c2972e 604 }
74c2972e 605 }
a1887ef3 606
98f49db3 607 // Multilingual variables in settings form.
afcc0a25 608 if (isset($form['#theme']) && $form['#theme'] == 'system_settings_form' && $variables = variable_get('i18n_variables', 0)) {
66f2307e 609 if ($i18n_variables = i18n_form_alter_settings($form, $variables)) {
27bba143 610 array_unshift($form['#submit'], 'i18n_variable_form_submit');
66f2307e 611 $form['#i18n_variables'] = $i18n_variables;
98e35ee4
JR
612 }
613 }
136d636d 614 }
74c2972e
JR
615}
616
617/**
afcc0a25 618 * Implementation of hook_perm().
136d636d 619 *
afcc0a25
JR
620 * Permissions defined
621 * - administer all languages
622 * Disables language conditions for administration pages, so the user can view objects for all languages at the same time.
623 * This applies for: menu items, taxonomy
1d40cadd
JR
624 * - administer translations
625 * Will allow to add/remove existing nodes to/from translation sets.
afcc0a25
JR
626 */
627function i18n_perm() {
1d40cadd
JR
628 return array('administer all languages', 'administer translations');
629}
630
631/**
4c4a8511 632 * Implementation of hook_theme().
1d40cadd
JR
633 */
634function i18n_theme() {
635 return array(
636 'i18n_node_select_translation' => array(
637 'arguments' => array('element' => NULL),
638 'file' => 'i18n.pages.inc',
639 ),
640 );
afcc0a25 641}
98f49db3 642
afcc0a25
JR
643/**
644 * Process menu and menu item add/edit form submissions.
645 */
affdb36b
JR
646function i18n_menu_edit_item_form_submit($form, &$form_state) {
647 $mid = menu_edit_item_save($form_state['values']);
648 db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $form_state['values']['language'], $mid);
afcc0a25
JR
649 return 'admin/build/menu';
650}
651
652/**
98f49db3 653 * Check for multilingual variables in form.
98e35ee4
JR
654 */
655function i18n_form_alter_settings(&$form, &$variables) {
6ca1272a 656 $result = array();
afcc0a25 657 foreach (element_children($form) as $field) {
6ca1272a 658 if (count(element_children($form[$field])) && empty($form[$field]['#tree'])) {
98e35ee4
JR
659 $result += i18n_form_alter_settings($form[$field], $variables);
660 }
661 elseif (in_array($field, $variables)) {
66f2307e
JR
662 // Add form field class: i18n-variable
663 $form[$field]['#attributes']['class'] = !empty($form[$field]['#attributes']['class']) ? $form[$field]['#attributes']['class'] . ' i18n-variable' : 'i18n-variable';
664 $form[$field]['#description'] = !empty($form[$field]['#description']) ? $form[$field]['#description'] : '';
98f49db3 665 $form[$field]['#description'] .= ' <strong>'. t('This is a multilingual variable.') .'</strong>';
66f2307e
JR
666 // Addd field => name to result
667 $result[$field] = !empty($form[$field]['#title']) ? $form[$field]['#title'] : $field;
98e35ee4
JR
668 }
669 }
670 return $result;
671}
672
673/**
98f49db3 674 * Save multilingual variables and remove them from form.
98e35ee4 675 */
afcc0a25 676function i18n_variable_form_submit($form, &$form_state) {
affdb36b 677 $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
98e35ee4
JR
678 $variables = variable_get('i18n_variables', array());
679 $language = i18n_get_lang();
27bba143 680
affdb36b 681 foreach ($form_state['values'] as $key => $value) {
afcc0a25 682 if (in_array($key, $variables)) {
98e35ee4
JR
683 if ($op == t('Reset to defaults')) {
684 i18n_variable_del($key, $language);
685 }
686 else {
affdb36b 687 if (is_array($value) && isset($form_state['values']['array_filter'])) {
98e35ee4 688 $value = array_keys(array_filter($value));
74c2972e 689 }
98e35ee4 690 i18n_variable_set($key, $value, $language);
74c2972e 691 }
affdb36b 692 unset($form_state['values'][$key]);
136d636d 693 }
98e35ee4
JR
694 }
695 // Re-submit form
afcc0a25 696 // system_settings_form_submit($form_id, $form_values);
98e35ee4
JR
697}
698
699/**
98f49db3 700 * Initialization of multilingual variables.
136d636d 701 *
afcc0a25 702 * @param $language
98f49db3 703 * Language to retrieve variables. Defaults to current language.
27bba143
JR
704 * @param $i18n_variables
705 * Optional variable list to initialize
38f996de 706 */
27bba143 707function i18n_variable_init($langcode = NULL, $i18n_variables = NULL) {
38f996de
JR
708 global $conf;
709 global $i18n_conf;
136d636d 710
b28c2734 711 $langcode = $langcode ? $langcode : i18n_get_lang();
27bba143
JR
712 $i18n_variables = $i18n_variables ? $i18n_variables : variable_get('i18n_variables', '');
713
714 if ($i18n_variables) {
715 $i18n_add = array();
afcc0a25
JR
716 if (!$i18n_conf) {
717 $i18n_conf = array();
718 }
27bba143 719 $variables = _i18n_variable_init($langcode);
98f49db3 720 foreach ($i18n_variables as $name) {
27bba143 721 $i18n_add[$name] = isset($variables[$name]) ? $variables[$name] : (isset($conf[$name]) ? $conf[$name] : NULL);
38f996de 722 }
b28c2734 723
27bba143
JR
724 $conf = array_merge($conf, $i18n_add);
725 $i18n_conf = array_merge($i18n_conf, $i18n_add);
38f996de
JR
726 }
727}
728
74c2972e 729/**
4c4a8511 730 * Get language from context.
57f0a2c1
JR
731 */
732function _i18n_get_context_lang() {
733 // Node language when loading specific nodes or creating translations.
734 if (arg(0) == 'node' ) {
735 if (($node = menu_get_object('node')) && $node->language) {
736 return $node->language;
737 }
738 elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language'])) {
739 return $_GET['language'];
740 }
741 }
742}
743
744/**
98f49db3 745 * Helper function to create language selector.
74c2972e 746 */
98f49db3 747function _i18n_language_select($value ='', $description ='', $weight = -20, $languages = NULL) {
818652bd 748 $languages = $languages ? $languages : locale_language_list();
74c2972e 749 return array(
98f49db3 750 '#type' => 'select',
751 '#title' => t('Language'),
752 '#default_value' => $value,
753 '#options' => array_merge(array('' => ''), $languages),
754 '#description' => $description,
755 '#weight' => $weight,
74c2972e
JR
756 );
757}
758
afcc0a25 759/**
98f49db3 760 * Load language variables into array.
afcc0a25 761 */
27bba143 762function _i18n_variable_init($langcode) {
f8f1d8cf 763 static $variables;
27bba143
JR
764
765 if (!isset($variables[$langcode])) {
766 $cacheid = 'variables:'. $langcode;
f8f1d8cf 767 if ($cached = cache_get($cacheid)) {
27bba143 768 $variables[$langcode] = $cached->data;
f8f1d8cf
JR
769 }
770 else {
27bba143
JR
771 $result = db_query("SELECT * FROM {i18n_variable} WHERE language = '%s'", $langcode);
772 $variables[$langcode] = array();
f8f1d8cf 773 while ($variable = db_fetch_object($result)) {
27bba143 774 $variables[$langcode][$variable->name] = unserialize($variable->value);
f8f1d8cf 775 }
27bba143 776 cache_set($cacheid, $variables[$langcode]);
74c2972e 777 }
74c2972e 778 }
27bba143
JR
779
780 return $variables[$langcode];
74c2972e
JR
781}
782
38f996de 783/**
98f49db3 784 * Save multilingual variables that may have been changed by other methods than settings pages.
38f996de 785 */
98f49db3 786function _i18n_variable_exit() {
74c2972e
JR
787 global $i18n_conf;
788 global $conf;
98f49db3 789 if ($i18n_conf) {
afcc0a25 790 $lang = i18n_get_lang();
74c2972e 791 $refresh = FALSE;
98f49db3 792 // Rewritten because array_diff_assoc may fail with array variables.
793 foreach ($i18n_conf as $name => $value) {
ed3081d0 794 if (isset($conf[$name]) && $value != $conf[$name]) {
74c2972e
JR
795 $refresh = TRUE;
796 $i18n_conf[$name] = $conf[$name];
ed3081d0 797 db_query("DELETE FROM {i18n_variable} WHERE name='%s' AND language='%s'", $name, $lang);
74c2972e
JR
798 db_query("INSERT INTO {i18n_variable} (language, name, value) VALUES('%s', '%s', '%s')", $lang, $name, serialize($conf[$name]));
799 }
800 }
98f49db3 801 if ($refresh) {
802 cache_set('variables:'. $lang, $i18n_conf);
74c2972e
JR
803 }
804 }
7027e3b5 805}
afcc0a25
JR
806
807/**
4c4a8511 808 * Check whether we are in bootstrap mode.
136d636d 809 */
98f49db3 810function _i18n_is_bootstrap() {
afcc0a25 811 return !function_exists('drupal_get_headers');
136d636d 812}
afcc0a25
JR
813
814/**
4c4a8511
MD
815 * Drupal 6, backwards compatibility layer.
816 *
afcc0a25
JR
817 * @ TO DO Fully upgrade all the modules and remove
818 */
818652bd 819
afcc0a25
JR
820/**
821 * This one expects to be called first from common.inc
822 */
818652bd 823function i18n_get_lang() {
afcc0a25 824 global $language;
afcc0a25
JR
825 return $language->language;
826}
827
5aaad3de 828/**
818652bd
JR
829 * @defgroup i18n_api Extended language API
830 * @{
831 * This is an extended language API to be used by modules in i18n package.
afcc0a25 832 */
818652bd
JR
833
834/**
4c4a8511 835 * Returns language lists.
818652bd
JR
836 */
837function i18n_language_list($type = 'enabled', $field = 'name') {
838 switch ($type) {
839 case 'enabled':
840 return locale_language_list($field);
98f49db3 841
818652bd
JR
842 case 'extended':
843 $enabled = locale_language_list($field);
844 $defined = locale_language_list($field, TRUE);
845 return array_diff_assoc($defined, $enabled);
afcc0a25 846 }
818652bd 847}
5aaad3de 848
818652bd 849/**
98f49db3 850 * Returns default language code.
818652bd 851 */
98f49db3 852function i18n_default_language() {
136d636d 853 return language_default('language');
afcc0a25
JR
854}
855
856/**
98f49db3 857 * Get list of supported languages, native name.
136d636d 858 *
afcc0a25 859 * @param $all
98f49db3 860 * TRUE to get all defined languages.
afcc0a25
JR
861 */
862function i18n_supported_languages($all = FALSE) {
818652bd 863 return locale_language_list('native', $all);
afcc0a25
JR
864}
865
866/**
afcc0a25
JR
867 * Set a persistent language dependent variable.
868 *
869 * @param $name
870 * The name of the variable to set.
871 * @param $value
872 * The value to set. This can be any PHP data type; these functions take care
873 * of serialization as necessary.
874 * @param $langcode
98f49db3 875 * Language code.
afcc0a25
JR
876 */
877function i18n_variable_set($name, $value, $langcode) {
878 global $conf, $i18n_conf;
879
6a513f8b
JR
880 $serialized_value = serialize($value);
881 db_query("UPDATE {i18n_variable} SET value = '%s' WHERE name = '%s' AND language = '%s'", $serialized_value, $name, $langcode);
882 if (!db_affected_rows()) {
883 @db_query("INSERT INTO {i18n_variable} (name, language, value) VALUES ('%s', '%s', '%s')", $name, $langcode, $serialized_value);
884 }
afcc0a25 885
98f49db3 886 cache_clear_all('variables:'. $langcode, 'cache');
afcc0a25
JR
887
888 $conf[$name] = $value;
889 $i18n_conf[$name] = $value;
890}
891
892/**
27bba143
JR
893 * Get single multilingual variable
894 */
895function i18n_variable_get($name, $langcode, $default = NULL) {
896 if ($variables = _i18n_variable_init($langcode)) {
897 return isset($variables[$name]) ? $variables[$name] : $default;
898 }
899 else {
900 return $default;
901 }
902}
903
904/**
afcc0a25
JR
905 * Unset a persistent multilingual variable.
906 *
907 * @param $name
908 * The name of the variable to undefine.
909 * @param $langcode
98f49db3 910 * Language code.
afcc0a25
JR
911 */
912function i18n_variable_del($name, $langcode) {
913 global $conf, $i18n_conf;
914
915 db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $langcode);
98f49db3 916 cache_clear_all('variables:'. $langcode, 'cache');
afcc0a25
JR
917
918 unset($conf[$name]);
919 unset($i18n_conf[$name]);
920}
921
922/**
98f49db3 923 * Utility. Get part of array variable.
afcc0a25
JR
924 */
925function i18n_array_variable_get($name, $element, $default = NULL) {
926 if (($values = variable_get($name, array())) && isset($values[$element])) {
927 return $values[$element];
98f49db3 928 }
929 else {
afcc0a25 930 return $default;
136d636d 931 }
afcc0a25
JR
932}
933
934/**
98f49db3 935 * Utility. Set part of array variable.
afcc0a25
JR
936 */
937function i18n_array_variable_set($name, $element, $value) {
938 $values = variable_get($name, array());
939 $values[$element] = $value;
940 variable_set($name, $values);
941}
942
943/**
944 * @} End of "defgroup i18n_api".
945 */
e562cb3d 946
98f49db3 947/**
948 * List of language support modes for content.
949 */
e562cb3d
JR
950function _i18n_content_language_options() {
951 return array(
952 LANGUAGE_SUPPORT_NORMAL => t('Normal - All enabled languages will be allowed.'),
5196f381
JR
953 LANGUAGE_SUPPORT_EXTENDED => t('Extended - All defined languages will be allowed.'),
954 LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED => t('Extended, but not displayed - All defined languages will be allowed for input, but not displayed in links.'),
e562cb3d 955 );
5aaad3de 956}