Projet

Général

Profil

Paste
Télécharger (19,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / i18n / i18n.module @ 9faa5de0

1
<?php
2

    
3
/**
4
 * @file
5
 * Internationalization (i18n) module.
6
 *
7
 * This module extends multilingual support being the base module for the i18n package.
8
 * - Multilingual variables
9
 * - Extended languages for nodes
10
 * - Extended language API
11
 *
12
 * @author Jose A. Reyero, 2004
13
 */
14

    
15
// All multilingual options disabled
16
define('I18N_LANGUAGE_DISABLED', 0);
17
// Language list will include all enabled languages
18
define('I18N_LANGUAGE_ENABLED', 1);
19
// Language list will include also disabled languages
20
define('I18N_LANGUAGE_EXTENDED', 4);
21
// Disabled languages will be hidden when possible
22
define('I18N_LANGUAGE_HIDDEN', 8);
23
// All defined languages will be allowed but hidden when possible
24
define('I18N_LANGUAGE_EXTENDED_NOT_DISPLAYED', I18N_LANGUAGE_EXTENDED | I18N_LANGUAGE_HIDDEN);
25

    
26
// No multilingual options
27
define('I18N_MODE_NONE', 0);
28
// Localizable object. Run through the localization system
29
define('I18N_MODE_LOCALIZE', 1);
30
// Predefined language for this object and all related ones.
31
define('I18N_MODE_LANGUAGE', 2);
32
// Multilingual objects, translatable but not localizable.
33
define('I18N_MODE_TRANSLATE', 4);
34
// Objects are translatable (if they have language or localizable if not)
35
define('I18N_MODE_MULTIPLE', I18N_MODE_LOCALIZE | I18N_MODE_TRANSLATE);
36

    
37
/**
38
 * Global variable for i18n context language.
39
 */
40
define('I18N_LANGUAGE_TYPE_CONTEXT', 'i18n_language_context');
41

    
42
/**
43
 * Implements hook_boot().
44
 */
45
function i18n_boot() {
46
  // Just make sure the module is loaded for boot and the API is available.
47
}
48

    
49
/**
50
 * Implements hook_hook_info().
51
 */
52
function i18n_hook_info() {
53
  $hooks['i18n_object_info'] = array(
54
    'group' => 'i18n',
55
  );
56
  return $hooks;
57
}
58

    
59
/**
60
 * WARNING: Obsolete function, use other i18n_language_* instead.
61
 *
62
 * Get global language object, make sure it is initialized
63
 *
64
 * @param $language
65
 *   Language code or language object to convert to valid language object
66
 */
67
function i18n_language($language = NULL) {
68
  if ($language && ($language_object = i18n_language_object($language))) {
69
    return $language_object;
70
  }
71
  else {
72
    return i18n_language_interface();
73
  }
74
}
75

    
76
/**
77
 * Get language object from language code or object.
78
 *
79
 * @param $language
80
 *   Language code or language object to convert to valid language object.
81
 * @return
82
 *   Language object if this is an object or a valid language code.
83
 */
84
function i18n_language_object($language) {
85
  if (is_object($language)) {
86
    return $language;
87
  }
88
  else {
89
    $list = language_list();
90
    return isset($list[$language]) ? $list[$language] : NULL;
91
  }
92
}
93

    
94
/**
95
 * Get interface language, make sure it is initialized.
96
 */
97
function i18n_language_interface() {
98
  if (empty($GLOBALS[LANGUAGE_TYPE_INTERFACE])) {
99
    // We don't have language yet, initialize the language system and retry
100
    drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
101
  }
102
  return $GLOBALS[LANGUAGE_TYPE_INTERFACE];
103
}
104

    
105
/**
106
 * Get content language, make sure it is initialized.
107
 */
108
function i18n_language_content() {
109
  if (empty($GLOBALS[LANGUAGE_TYPE_CONTENT])) {
110
    // We don't have language yet, initialize the language system and retry
111
    drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
112
  }
113
  return $GLOBALS[LANGUAGE_TYPE_CONTENT];
114
}
115

    
116
/**
117
 * Get / set language from current context / content.
118
 *
119
 * Depending on the page content we may need to use a different language for some operations.
120
 *
121
 * This should be the language of the specific page content. I.e. node language for node pages
122
 * or term language for taxonomy term page.
123
 *
124
 * @param $language
125
 *   Optional language object to set for context.
126
 * @return
127
 *   Context language object, which defaults to content language.
128
 *
129
 * @see hook_i18n_context_language().
130
 */
131
function i18n_language_context($language = NULL) {
132
  if ($language) {
133
    $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = $language;
134
  }
135
  elseif (!isset($GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT])) {
136
    // It will default to content language.
137
    $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = i18n_language_content();
138
    // Get language from the first module that provides it.
139
    foreach (module_implements('i18n_context_language') as $module) {
140
      if ($language = module_invoke($module, 'i18n_context_language')) {
141
        $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = $language;
142
        break;
143
      }
144
    }
145
  }
146
  return $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT];
147
}
148

    
149
/**
150
 * Menu object loader, language
151
 */
152
function i18n_language_load($langcode) {
153
  $list = language_list();
154
  return isset($list[$langcode]) ? $list[$langcode] : FALSE;
155
}
156

    
157
/**
158
 * Get language selector form element
159
 */
160
function i18n_element_language_select($default = LANGUAGE_NONE) {
161
  if (is_object($default) || is_array($default)) {
162
    $default = i18n_object_langcode($default, LANGUAGE_NONE);
163
  }
164
  return array(
165
    '#type' => 'select',
166
    '#title' => t('Language'),
167
    '#default_value' => $default,
168
    '#options' => array(LANGUAGE_NONE => t('Language neutral')) + i18n_language_list(),
169
  );
170
}
171

    
172
/**
173
 * Get language field for hook_field_extra_fields()
174
 */
175
function i18n_language_field_extra() {
176
  return array(
177
    'form' => array(
178
      'language' => array(
179
        'label' => t('Language'),
180
        'description' => t('Language selection'),
181
        'weight' => 0,
182
      ),
183
    ),
184
    'display' => array(
185
      'language' => array(
186
        'label' => t('Language'),
187
        'description' => t('Language'),
188
        'weight' => 0,
189
      ),
190
    ),
191
  );
192
}
193

    
194
/**
195
 * Get full language list
196
 *
197
 * @todo See about creating a permission for seeing disabled languages
198
 */
199
function i18n_language_list($field = 'name', $mode = NULL) {
200
  $mode = isset($mode) ? $mode : variable_get('i18n_language_list', I18N_LANGUAGE_ENABLED);
201
  $all = I18N_LANGUAGE_EXTENDED & $mode;
202
  return locale_language_list($field, $all);
203
}
204

    
205
/**
206
 * Get language name for any defined (enabled or not) language
207
 *
208
 * @see locale_language_list()
209
 */
210
function i18n_language_name($lang) {
211
  $list = &drupal_static(__FUNCTION__);
212
  if (!isset($list)) {
213
    $list = locale_language_list('name', TRUE);
214
  }
215
  if (!$lang || $lang === LANGUAGE_NONE) {
216
    return t('Undefined');
217
  }
218
  elseif (isset($list[$lang])) {
219
    return check_plain($list[$lang]);
220
  }
221
  else {
222
    return t('Unknown');
223
  }
224
}
225

    
226
/**
227
 * Get valid language code for current page or check whether the code is a defined language
228
 */
229
function i18n_langcode($langcode = NULL) {
230
  return $langcode && $langcode !== LANGUAGE_NONE ? $langcode : i18n_language()->language;
231
}
232

    
233
/**
234
 * Implements hook_help().
235
 */
236
function i18n_help($path, $arg) {
237
  switch ($path) {
238
    case 'admin/help#i18n' :
239
      $output = '<p>' . t('This module improves support for multilingual content in Drupal sites:') . '</p>';
240
      $output .= '<ul>';
241
      $output .= '<li>' . t('Shows content depending on page language.') . '</li>';
242
      $output .= '<li>' . t('Handles multilingual variables.') . '</li>';
243
      $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>';
244
      $output .= '<li>' . t('Provides a block for language selection and two theme functions: <em>i18n_flags</em> and <em>i18n_links</em>.') . '</li>';
245
      $output .= '</ul>';
246
      $output .= '<p>' . t('This is the base module for several others adding different features:') . '</p>';
247
      $output .= '<ul>';
248
      $output .= '<li>' . t('Multilingual menu items.') . '</li>';
249
      $output .= '<li>' . t('Multilingual taxonomy adds a language field for taxonomy vocabularies and terms.') . '</li>';
250
      $output .= '</ul>';
251
      $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>';
252
      return $output;
253

    
254
    case 'admin/config/language/i18n':
255
      $output = '<ul>';
256
      $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/structure/types'))) . '</li>';
257
      $output .= '</ul>';
258
      return $output;
259
  }
260
}
261

    
262
/**
263
 * Implements hook_menu().
264
 */
265
function i18n_menu() {
266
  $items['admin/config/regional/i18n'] = array(
267
    'title' => 'Multilingual settings',
268
    'description' => 'Configure extended options for multilingual content and translations.',
269
    'page callback' => 'drupal_get_form',
270
    'page arguments' => array('variable_module_form', 'i18n'),
271
    'access arguments' => array('administer site configuration'),
272
    'weight' => 10,
273
  );
274
  $items['admin/config/regional/i18n/configure'] = array(
275
    'title' => 'Multilingual system',
276
    'description' => 'Configure extended options for multilingual content and translations.',
277
    'type' => MENU_DEFAULT_LOCAL_TASK,
278
  );
279
  module_load_include('pages.inc', 'i18n');
280
  $items += i18n_page_menu_items();
281
  return $items;
282
}
283

    
284
/**
285
 * Simple i18n API
286
 */
287

    
288
/**
289
 * Switch select Mode on off if enabled
290
 *
291
 * Usage for disabling content selection for a while then return to previous state
292
 *
293
 *   // Disable selection, but store previous mode
294
 *   $previous = i18n_select(FALSE);
295
 *
296
 *   // Other code to be run without content selection here
297
 *   ..........................
298
 *
299
 *   // Return to previous mode
300
 *   i18n_select($previous);
301
 *
302
 * @param $value
303
 *   Optional, enable/disable selection: TRUE/FALSE
304
 * @return boolean
305
 *   Previous selection mode (TRUE/FALSE)
306
 */
307
function i18n_select($value = NULL) {
308
  static $mode = FALSE;
309

    
310
  if (isset($value)) {
311
    $previous = $mode;
312
    $mode = $value;
313
    return $previous;
314
  }
315
  else {
316
    return $mode;
317
  }
318
}
319

    
320
/**
321
 * Get language properties.
322
 *
323
 * @param $code
324
 *   Language code.
325
 * @param $property
326
 *   It may be 'name', 'native', 'ltr'...
327
 */
328
function i18n_language_property($code, $property) {
329
  $languages = language_list();
330
  return isset($languages[$code]->$property) ? $languages[$code]->$property : NULL;
331
}
332

    
333
/**
334
 * Implements hook_preprocess_html().
335
 */
336
function i18n_preprocess_html(&$variables) {
337
  global $language;
338
  $variables['classes_array'][] = 'i18n-' . $language->language;
339
}
340

    
341
/**
342
 * Translate or update user defined string. Entry point for i18n_string API if enabled.
343
 *
344
 * This function is from i18n_string sub module and is subject to be moved back.
345
 *
346
 * @param $name
347
 *   Textgroup and context glued with ':'.
348
 * @param $default
349
 *   String in default language. Default language may or may not be English.
350
 * @param $options
351
 *   An associative array of additional options, with the following keys:
352
 *   - 'langcode' (defaults to the current language) The language code to translate to a language other than what is used to display the page.
353
 *   - 'filter' Filtering callback to apply to the translated string only
354
 *   - 'format' Input format to apply to the translated string only
355
 *   - 'callback' Callback to apply to the result (both to translated or untranslated string
356
 *   - 'update' (defaults to FALSE) Whether to update source table
357
 *   - 'translate' (defaults to TRUE) Whether to return a translation
358
 *
359
 * @return $string
360
 *   Translated string, $string if not found
361
 */
362
function i18n_string($name, $string, $options = array()) {
363
  $options += array('translate' => TRUE, 'update' => FALSE);
364
  if ($options['update']) {
365
    $result = function_exists('i18n_string_update') ? i18n_string_update($name, $string, $options) : FALSE;
366
  }
367
  if ($options['translate']) {
368
    $result = function_exists('i18n_string_translate') ? i18n_string_translate($name, $string, $options) : $string;
369
  }
370
  return $result;
371
}
372

    
373
/**
374
 * Get object wrapper.
375
 *
376
 * Create an object wrapper or retrieve it from the static cache if
377
 * a wrapper for the same object was created before.
378
 *
379
 * @see i18n_object_info()
380
 *
381
 * @param $type
382
 *   The object type.
383
 */
384
function i18n_object($type, $object) {
385
  $key = i18n_object_key($type, $object);
386
  return i18n_get_object($type, $key, $object);
387
}
388

    
389
/**
390
 * Get object wrapper by object key.
391
 *
392
 * @param $type
393
 *   The object type to load e.g. node_type, menu, taxonomy_term.
394
 * @param $key
395
 *   The object key, can be an scalar or an array.
396
 * @param $object
397
 *   Optional Drupal object or array. It will be autoloaded using the key if not present.
398
 *
399
 * @return
400
 *   A fully-populated object wrapper.
401
 */
402
function i18n_get_object($type, $key, $object = NULL) {
403
  $cache = &drupal_static(__FUNCTION__);
404
  $index = is_array($key) ? implode(':', $key) : $key;
405
  if (!isset($cache[$type][$index])) {
406
    $class = i18n_object_info($type, 'class', 'i18n_object_wrapper');
407
    $object_wrapper = new $class($type, $key, $object);
408
    // Do not cache object with empty index.
409
    if (!empty($index)) {
410
      $cache[$type][$index] = $object_wrapper;
411
    }
412
  }
413
  else {
414
    $object_wrapper = $cache[$type][$index];
415
  }
416

    
417
  return $object_wrapper;
418
}
419

    
420
/**
421
 * Get object language code
422
 *
423
 * @param $object
424
 *   Object or array having language field or plain language field
425
 * @param $default
426
 *   What value to return if the object doesn't have a valid language
427
 */
428
function i18n_object_langcode($object, $default = FALSE, $field = 'language') {
429
  $value = i18n_object_field($object, $field, $default);
430
  return $value && $value != LANGUAGE_NONE ? $value : $default;
431
}
432

    
433
/**
434
 * Get translation information for objects
435
 */
436
function i18n_object_info($type = NULL, $property = NULL, $default = NULL) {
437
  $info = &drupal_static(__FUNCTION__);
438
  if (!$info) {
439
    $info = module_invoke_all('i18n_object_info');
440
    drupal_alter('i18n_object_info', $info);
441
  }
442
  if ($property) {
443
    return isset($info[$type][$property]) ? $info[$type][$property] : $default;
444
  }
445
  elseif ($type) {
446
    return isset($info[$type]) ? $info[$type] : array();
447
  }
448
  else {
449
    return $info;
450
  }
451
}
452

    
453
/**
454
 * Get field value from object/array
455
 */
456
function i18n_object_field($object, $field, $default = NULL) {
457
  if (is_array($field)) {
458
    // We can handle a list of fields too. This is useful for multiple keys (like blocks)
459
    foreach ($field as $key => $name) {
460
      $values[$key] = i18n_object_field($object, $name);
461
    }
462
    return $values;
463
  }
464
  elseif (strpos($field, '.')) {
465
    // Access nested properties with the form 'name1.name2..', will map to $object->name1->name2...
466
    $names = explode('.', $field);
467
    $current = array_shift($names);
468
    if ($nested = i18n_object_field($object, $current)) {
469
      return i18n_object_field($nested, implode('.', $names), $default);
470
    }
471
    else {
472
      return $default;
473
    }
474
  }
475
  elseif (is_object($object)) {
476
    return isset($object->$field) ? $object->$field : $default;
477
  }
478
  elseif (is_array($object)) {
479
    return isset($object[$field]) ? $object[$field] : $default;
480
  }
481
  else {
482
    return $default;
483
  }
484
}
485

    
486
/**
487
 * Get key value from object/array
488
 */
489
function i18n_object_key($type, $object, $default = NULL) {
490
  if ($field = i18n_object_info($type, 'key')) {
491
    return i18n_object_field($object, $field, $default);
492
  }
493
  else {
494
    return $default;
495
  }
496
}
497

    
498
/**
499
 * Menu access callback for mixed translation tab
500
 */
501
function i18n_object_translate_access($type, $object) {
502
  return i18n_object($type, $object)->get_translate_access();
503
}
504

    
505
/**
506
 * Get translations for path.
507
 *
508
 * @param $path
509
 *   Path to get translations for or '<front>' for front page.
510
 * @param $check_access
511
 *   Whether to check access to paths, defaults to TRUE
512
 */
513
function i18n_get_path_translations($path, $check_access = TRUE) {
514
  $translations = &drupal_static(__FUNCTION__);
515

    
516
  if (!isset($translations[$path])) {
517
    $translations[$path] = array();
518
    foreach (module_implements('i18n_translate_path') as $module) {
519
      $translated = call_user_func($module . '_i18n_translate_path', $path);
520
      // Add into the array, if two modules returning a translation first takes precedence.
521
      if ($translated) {
522
        $translations[$path] += $translated;
523
      }
524
    }
525
    // Add access information if not there.
526
    foreach ($translations[$path] as $langcode => &$info) {
527
      if (!isset($info['access'])) {
528
        $item = menu_get_item($info['href']);
529
        // If no menu item, it may be an external URL, we allow access.
530
        $info['access'] = $item ? !empty($item['access']) : TRUE;
531
      }
532
    }
533
    // Chance for altering the results.
534
    drupal_alter('i18n_translate_path', $translations[$path], $path);
535
  }
536

    
537
  if ($check_access) {
538
    return array_filter($translations[$path], '_i18n_get_path_translations_access');
539
  }
540
  else {
541
    return $translations[$path];
542
  }
543
}
544

    
545
/**
546
 * Helper function to check access to path translation.
547
 */
548
function _i18n_get_path_translations_access($path) {
549
  return $path['access'];
550
}
551

    
552
/**
553
 * Implements hook_language_switch_links_alter().
554
 *
555
 * Replaces links with pointers to translated versions of the content.
556
 */
557
function i18n_language_switch_links_alter(array &$links, $type, $path) {
558
  // For the front page we have nothing to add to Drupal core links.
559
  if ($path != '<front>' && ($translations = i18n_get_path_translations($path))) {
560
    foreach ($translations as $langcode => $translation) {
561
      if (isset($links[$langcode])) {
562
        $links[$langcode]['href'] = $translation['href'];
563
        if (!empty($translation['title'])) {
564
          $links[$langcode]['attributes']['title'] = $translation['title'];
565
        }
566
      }
567
    }
568
  }
569
}
570

    
571
/**
572
 * Build translation link
573
 */
574
function i18n_translation_link($path, $langcode, $link = array()) {
575
  $language = i18n_language_object($langcode);
576
  $link += array(
577
    'href' => $path,
578
    'title' => $language->native,
579
    'language' => $language,
580
    'i18n_translation' => TRUE,
581
  );
582
  $link['attributes']['class'] = array('language-link');
583
  // @todo Fix languageicons weight, but until that
584
  if (function_exists('languageicons_link_add')) {
585
    languageicons_link_add($link);
586
  }
587
  return $link;
588
}
589

    
590
/**
591
 * Implements hook_form_FORM_ID_alter().
592
 */
593
function i18n_form_block_admin_display_form_alter(&$form, &$form_state) {
594
  $form['#submit'][] = 'i18n_form_block_admin_display_form_submit';
595
}
596

    
597
/**
598
 * Display a help message when enabling the language switcher block.
599
 */
600
function i18n_form_block_admin_display_form_submit($form, &$form_state) {
601
  foreach ($form_state['values']['blocks'] as $key => $block) {
602
    $previous = $form['blocks'][$key]['region']['#default_value'];
603
    if (empty($previous) && $block['region'] != -1 && $block['module'] == 'locale') {
604
      $message = t('The language switcher will appear only after configuring <a href="!url">language detection</a>. You need to enable at least one method that alters URLs like <em>URL</em> or <em>Session</em>.', array('!url' => url('admin/config/regional/language/configure')));
605
      drupal_set_message($message, 'warning', FALSE);
606
      break;
607
    }
608
  }
609
}
610

    
611
/**
612
 * Normal path should be checked with menu item's language to avoid
613
 * troubles when a node and it's translation has the same url alias.
614
 */
615
function i18n_prepare_normal_path($link_path, $language) {
616
  $normal_path = drupal_get_normal_path($link_path, $language);
617
  if ($link_path != $normal_path) {
618
    drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $link_path, '%normal_path' => $normal_path)));
619
  }
620
  return $normal_path;
621
}
622

    
623
/**
624
 * Checks if an entity translation is enabled for the given entity type.
625
 * @param $entity_type
626
 */
627
function i18n_entity_translation_enabled($entity_type) {
628
  $cache = &drupal_static(__FUNCTION__);
629
  if (!isset($cache[$entity_type])) {
630
    // Check if the entity_translation module exists and if so if the given
631
    // entity type is handled.
632
    $cache[$entity_type] = module_exists('entity_translation') && entity_translation_enabled($entity_type);
633
  }
634
  return $cache[$entity_type];
635
}
636

    
637
/**
638
 * Implements hook_modules_enabled().
639
 */
640
function i18n_modules_enabled($modules) {
641
  drupal_static_reset('i18n_object_info');
642
}