cms/drupal/modules/locale/locale.module
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  *   Add language handling functionality and enables the translation of the
       
     6  *   user interface to languages other than English.
       
     7  *
       
     8  *   When enabled, multiple languages can be set up. The site interface
       
     9  *   can be displayed in different languages, as well as nodes can have languages
       
    10  *   assigned. The setup of languages and translations is completely web based.
       
    11  *   Gettext portable object files are supported.
       
    12  */
       
    13 
       
    14 // ---------------------------------------------------------------------------------
       
    15 // Hook implementations
       
    16 
       
    17 /**
       
    18  * Implements hook_help().
       
    19  */
       
    20 function locale_help($path, $arg) {
       
    21   switch ($path) {
       
    22     case 'admin/help#locale':
       
    23       $output = '';
       
    24       $output .= '<h3>' . t('About') . '</h3>';
       
    25       $output .= '<p>' . t('The Locale module allows your Drupal site to be presented in languages other than the default English, and to be multilingual. The Locale module works by maintaining a database of translations, and examining text as it is about to be displayed. When a translation of the text is available in the language to be displayed, the translation is displayed rather than the original text. When a translation is unavailable, the original text is displayed, and then stored for review by a translator. For more information, see the online handbook entry for <a href="@locale">Locale module</a>.', array('@locale' => 'http://drupal.org/documentation/modules/locale/')) . '</p>';
       
    26       $output .= '<h3>' . t('Uses') . '</h3>';
       
    27       $output .= '<dl>';
       
    28       $output .= '<dt>' . t('Translating interface text') . '</dt>';
       
    29       $output .= '<dd>' . t('Translations of text in the Drupal interface may be provided by:');
       
    30       $output .= '<ul>';
       
    31       $output .= '<li>' . t("Translating within your site, using the Locale module's integrated <a href='@translate'>translation interface</a>.", array('@translate' => url('admin/config/regional/translate'))) . '</li>';
       
    32       $output .= '<li>' . t('Importing files from a set of existing translations, known as a translation package. A translation package enables the display of a specific version of Drupal in a specific language, and contains files in the Gettext Portable Object (<em>.po</em>) format. Although not all languages are available for every version of Drupal, translation packages for many languages are available for download from the <a href="@translations">Drupal translations page</a>.', array('@translations' => 'http://localize.drupal.org')) . '</li>';
       
    33       $output .= '<li>' . t("If an existing translation package does not meet your needs, the Gettext Portable Object (<em>.po</em>) files within a package may be modified, or new <em>.po</em> files may be created, using a desktop Gettext editor. The Locale module's <a href='@import'>import</a> feature allows the translated strings from a new or modified <em>.po</em> file to be added to your site. The Locale module's <a href='@export'>export</a> feature generates files from your site's translated strings, that can either be shared with others or edited offline by a Gettext translation editor.", array('@import' => url('admin/config/regional/translate/import'), '@export' => url('admin/config/regional/translate/export'))) . '</li>';
       
    34       $output .= '</ul></dd>';
       
    35       $output .= '<dt>' . t('Configuring a multilingual site') . '</dt>';
       
    36       $output .= '<dd>' . t("Language negotiation allows your site to automatically change language based on the domain or path used for each request. Users may (optionally) select their preferred language on their <em>My account</em> page, and your site can be configured to honor a web browser's preferred language settings. Site content can be translated using the <a href='@content-help'>Content translation module</a>.", array('@content-help' => url('admin/help/translation'))) . '</dd>';
       
    37       $output .= '</dl>';
       
    38       return $output;
       
    39     case 'admin/config/regional/language':
       
    40       $output = '<p>' . t('With multiple languages enabled, interface text can be translated, registered users may select their preferred language, and authors can assign a specific language to content. <a href="@translations">Download contributed translations</a> from Drupal.org.', array('@translations' => 'http://localize.drupal.org')) . '</p>';
       
    41       return $output;
       
    42     case 'admin/config/regional/language/add':
       
    43       return '<p>' . t('Add a language to be supported by your site. If your desired language is not available in the <em>Language name</em> drop-down, click <em>Custom language</em> and provide a language code and other details manually. When providing a language code manually, be sure to enter a standardized language code, since this code may be used by browsers to determine an appropriate display language.') . '</p>';
       
    44     case 'admin/config/regional/language/configure':
       
    45       $output = '<p>' . t("Define how to decide which language is used to display page elements (primarily text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '</p>';
       
    46       return $output;
       
    47     case 'admin/config/regional/language/configure/session':
       
    48       $output = '<p>' . t('Determine the language from a request/session parameter. Example: "http://example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.') . '</p>';
       
    49       return $output;
       
    50     case 'admin/config/regional/translate':
       
    51       $output = '<p>' . t('This page provides an overview of available translatable strings. Drupal displays translatable strings in text groups; modules may define additional text groups containing other translatable strings. Because text groups provide a method of grouping related strings, they are often used to focus translation efforts on specific areas of the Drupal interface.') . '</p>';
       
    52       $output .= '<p>' . t('See the <a href="@languages">Languages page</a> for more information on adding support for additional languages.', array('@languages' => url('admin/config/regional/language'))) . '</p>';
       
    53       return $output;
       
    54     case 'admin/config/regional/translate/import':
       
    55       $output = '<p>' . t('This page imports the translated strings contained in an individual Gettext Portable Object (<em>.po</em>) file. Normally distributed as part of a translation package (each translation package may contain several <em>.po</em> files), a <em>.po</em> file may need to be imported after offline editing in a Gettext translation editor. Importing an individual <em>.po</em> file may be a lengthy process.') . '</p>';
       
    56       $output .= '<p>' . t('Note that the <em>.po</em> files within a translation package are imported automatically (if available) when new modules or themes are enabled, or as new languages are added. Since this page only allows the import of one <em>.po</em> file at a time, it may be simpler to download and extract a translation package into your Drupal installation directory and <a href="@language-add">add the language</a> (which automatically imports all <em>.po</em> files within the package). Translation packages are available for download on the <a href="@translations">Drupal translation page</a>.', array('@language-add' => url('admin/config/regional/language/add'), '@translations' => 'http://localize.drupal.org')) . '</p>';
       
    57       return $output;
       
    58     case 'admin/config/regional/translate/export':
       
    59       return '<p>' . t('This page exports the translated strings used by your site. An export file may be in Gettext Portable Object (<em>.po</em>) form, which includes both the original string and the translation (used to share translations with others), or in Gettext Portable Object Template (<em>.pot</em>) form, which includes the original strings only (used to create new translations with a Gettext translation editor).') . '</p>';
       
    60     case 'admin/config/regional/translate/translate':
       
    61       return '<p>' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: For translation tasks involving many strings, it may be more convenient to <a href="@export">export</a> strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings found within a specific text group or in a specific language.', array('@export' => url('admin/config/regional/translate/export'))) . '</p>';
       
    62     case 'admin/structure/block/manage/%/%':
       
    63       if ($arg[4] == 'locale' && $arg[5] == 'language') {
       
    64         return '<p>' . t('This block is only shown if <a href="@languages">at least two languages are enabled</a> and <a href="@configuration">language negotiation</a> is set to <em>URL</em> or <em>Session</em>.', array('@languages' => url('admin/config/regional/language'), '@configuration' => url('admin/config/regional/language/configure'))) . '</p>';
       
    65       }
       
    66       break;
       
    67   }
       
    68 }
       
    69 
       
    70 /**
       
    71  * Implements hook_menu().
       
    72  */
       
    73 function locale_menu() {
       
    74   // Manage languages
       
    75   $items['admin/config/regional/language'] = array(
       
    76     'title' => 'Languages',
       
    77     'description' => 'Configure languages for content and the user interface.',
       
    78     'page callback' => 'drupal_get_form',
       
    79     'page arguments' => array('locale_languages_overview_form'),
       
    80     'access arguments' => array('administer languages'),
       
    81     'file' => 'locale.admin.inc',
       
    82     'weight' => -10,
       
    83   );
       
    84   $items['admin/config/regional/language/overview'] = array(
       
    85     'title' => 'List',
       
    86     'weight' => 0,
       
    87     'type' => MENU_DEFAULT_LOCAL_TASK,
       
    88   );
       
    89   $items['admin/config/regional/language/add'] = array(
       
    90     'title' => 'Add language',
       
    91     'page callback' => 'locale_languages_add_screen', // two forms concatenated
       
    92     'access arguments' => array('administer languages'),
       
    93     'weight' => 5,
       
    94     'type' => MENU_LOCAL_ACTION,
       
    95     'file' => 'locale.admin.inc',
       
    96   );
       
    97   $items['admin/config/regional/language/configure'] = array(
       
    98     'title' => 'Detection and selection',
       
    99     'page callback' => 'drupal_get_form',
       
   100     'page arguments' => array('locale_languages_configure_form'),
       
   101     'access arguments' => array('administer languages'),
       
   102     'weight' => 10,
       
   103     'file' => 'locale.admin.inc',
       
   104     'type' => MENU_LOCAL_TASK,
       
   105   );
       
   106   $items['admin/config/regional/language/configure/url'] = array(
       
   107     'title' => 'URL language detection configuration',
       
   108     'page callback' => 'drupal_get_form',
       
   109     'page arguments' => array('locale_language_providers_url_form'),
       
   110     'access arguments' => array('administer languages'),
       
   111     'file' => 'locale.admin.inc',
       
   112     'type' => MENU_VISIBLE_IN_BREADCRUMB,
       
   113   );
       
   114   $items['admin/config/regional/language/configure/session'] = array(
       
   115     'title' => 'Session language detection configuration',
       
   116     'page callback' => 'drupal_get_form',
       
   117     'page arguments' => array('locale_language_providers_session_form'),
       
   118     'access arguments' => array('administer languages'),
       
   119     'file' => 'locale.admin.inc',
       
   120     'type' => MENU_VISIBLE_IN_BREADCRUMB,
       
   121   );
       
   122   $items['admin/config/regional/language/edit/%'] = array(
       
   123     'title' => 'Edit language',
       
   124     'page callback' => 'drupal_get_form',
       
   125     'page arguments' => array('locale_languages_edit_form', 5),
       
   126     'access arguments' => array('administer languages'),
       
   127     'file' => 'locale.admin.inc',
       
   128   );
       
   129   $items['admin/config/regional/language/delete/%'] = array(
       
   130     'title' => 'Confirm',
       
   131     'page callback' => 'drupal_get_form',
       
   132     'page arguments' => array('locale_languages_delete_form', 5),
       
   133     'access arguments' => array('administer languages'),
       
   134     'file' => 'locale.admin.inc',
       
   135   );
       
   136 
       
   137   // Translation functionality
       
   138   $items['admin/config/regional/translate'] = array(
       
   139     'title' => 'Translate interface',
       
   140     'description' => 'Translate the built in interface and optionally other text.',
       
   141     'page callback' => 'locale_translate_overview_screen',
       
   142     'access arguments' => array('translate interface'),
       
   143     'file' => 'locale.admin.inc',
       
   144     'weight' => -5,
       
   145   );
       
   146   $items['admin/config/regional/translate/overview'] = array(
       
   147     'title' => 'Overview',
       
   148     'weight' => 0,
       
   149     'type' => MENU_DEFAULT_LOCAL_TASK,
       
   150   );
       
   151   $items['admin/config/regional/translate/translate'] = array(
       
   152     'title' => 'Translate',
       
   153     'weight' => 10,
       
   154     'type' => MENU_LOCAL_TASK,
       
   155     'page callback' => 'locale_translate_seek_screen', // search results and form concatenated
       
   156     'access arguments' => array('translate interface'),
       
   157     'file' => 'locale.admin.inc',
       
   158   );
       
   159   $items['admin/config/regional/translate/import'] = array(
       
   160     'title' => 'Import',
       
   161     'page callback' => 'drupal_get_form',
       
   162     'page arguments' => array('locale_translate_import_form'),
       
   163     'access arguments' => array('translate interface'),
       
   164     'weight' => 20,
       
   165     'type' => MENU_LOCAL_TASK,
       
   166     'file' => 'locale.admin.inc',
       
   167   );
       
   168   $items['admin/config/regional/translate/export'] = array(
       
   169     'title' => 'Export',
       
   170     'page callback' => 'locale_translate_export_screen',  // possibly multiple forms concatenated
       
   171     'access arguments' => array('translate interface'),
       
   172     'weight' => 30,
       
   173     'type' => MENU_LOCAL_TASK,
       
   174     'file' => 'locale.admin.inc',
       
   175   );
       
   176   $items['admin/config/regional/translate/edit/%'] = array(
       
   177     'title' => 'Edit string',
       
   178     'page callback' => 'drupal_get_form',
       
   179     'page arguments' => array('locale_translate_edit_form', 5),
       
   180     'access arguments' => array('translate interface'),
       
   181     'file' => 'locale.admin.inc',
       
   182   );
       
   183   $items['admin/config/regional/translate/delete/%'] = array(
       
   184     'title' => 'Delete string',
       
   185     'page callback' => 'locale_translate_delete_page',
       
   186     'page arguments' => array(5),
       
   187     'access arguments' => array('translate interface'),
       
   188     'file' => 'locale.admin.inc',
       
   189   );
       
   190 
       
   191   // Localize date formats.
       
   192   $items['admin/config/regional/date-time/locale'] = array(
       
   193     'title' => 'Localize',
       
   194     'description' => 'Configure date formats for each locale',
       
   195     'page callback' => 'locale_date_format_language_overview_page',
       
   196     'access arguments' => array('administer site configuration'),
       
   197     'type' => MENU_LOCAL_TASK,
       
   198     'weight' => -8,
       
   199     'file' => 'locale.admin.inc',
       
   200   );
       
   201   $items['admin/config/regional/date-time/locale/%/edit'] = array(
       
   202     'title' => 'Localize date formats',
       
   203     'description' => 'Configure date formats for each locale',
       
   204     'page callback' => 'drupal_get_form',
       
   205     'page arguments' => array('locale_date_format_form', 5),
       
   206     'access arguments' => array('administer site configuration'),
       
   207     'file' => 'locale.admin.inc',
       
   208   );
       
   209   $items['admin/config/regional/date-time/locale/%/reset'] = array(
       
   210     'title' => 'Reset date formats',
       
   211     'description' => 'Reset localized date formats to global defaults',
       
   212     'page callback' => 'drupal_get_form',
       
   213     'page arguments' => array('locale_date_format_reset_form', 5),
       
   214     'access arguments' => array('administer site configuration'),
       
   215     'file' => 'locale.admin.inc',
       
   216   );
       
   217 
       
   218   return $items;
       
   219 }
       
   220 
       
   221 /**
       
   222  * Implements hook_init().
       
   223  *
       
   224  * Initialize date formats according to the user's current locale.
       
   225  */
       
   226 function locale_init() {
       
   227   global $conf, $language;
       
   228   include_once DRUPAL_ROOT . '/includes/locale.inc';
       
   229 
       
   230   // For each date type (e.g. long, short), get the localized date format
       
   231   // for the user's current language and override the default setting for it
       
   232   // in $conf. This should happen on all pages except the date and time formats
       
   233   // settings page, where we want to display the site default and not the
       
   234   // localized version.
       
   235   if (strpos($_GET['q'], 'admin/config/regional/date-time/formats') !== 0) {
       
   236     $languages = array($language->language);
       
   237 
       
   238     // Setup appropriate date formats for this locale.
       
   239     $formats = locale_get_localized_date_format($languages);
       
   240     foreach ($formats as $format_type => $format) {
       
   241       $conf[$format_type] = $format;
       
   242     }
       
   243   }
       
   244 }
       
   245 
       
   246 /**
       
   247  * Implements hook_permission().
       
   248  */
       
   249 function locale_permission() {
       
   250   return array(
       
   251     'administer languages' => array(
       
   252       'title' => t('Administer languages'),
       
   253     ),
       
   254     'translate interface' => array(
       
   255       'title' => t('Translate interface texts'),
       
   256       'restrict access' => TRUE,
       
   257     ),
       
   258   );
       
   259 }
       
   260 
       
   261 /**
       
   262  * Implements hook_locale().
       
   263  */
       
   264 function locale_locale($op = 'groups') {
       
   265   switch ($op) {
       
   266     case 'groups':
       
   267       return array('default' => t('Built-in interface'));
       
   268   }
       
   269 }
       
   270 
       
   271 /**
       
   272  * Form builder callback to display language selection widget.
       
   273  *
       
   274  * @ingroup forms
       
   275  * @see locale_form_alter()
       
   276  */
       
   277 function locale_language_selector_form(&$form, &$form_state, $user) {
       
   278   global $language;
       
   279   $languages = language_list('enabled');
       
   280   $languages = $languages[1];
       
   281 
       
   282   // If the user is being created, we set the user language to the page language.
       
   283   $user_preferred_language = $user->uid ? user_preferred_language($user) : $language;
       
   284 
       
   285   $names = array();
       
   286   foreach ($languages as $langcode => $item) {
       
   287     $name = t($item->name);
       
   288     $names[$langcode] = $name . ($item->native != $name ? ' (' . $item->native . ')' : '');
       
   289   }
       
   290   $form['locale'] = array(
       
   291     '#type' => 'fieldset',
       
   292     '#title' => t('Language settings'),
       
   293     '#weight' => 1,
       
   294     '#access' => ($form['#user_category'] == 'account' || ($form['#user_category'] == 'register' && user_access('administer users'))),
       
   295   );
       
   296 
       
   297   // Get language negotiation settings.
       
   298   $mode = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
       
   299   $form['locale']['language'] = array(
       
   300     '#type' => (count($names) <= 5 ? 'radios' : 'select'),
       
   301     '#title' => t('Language'),
       
   302     '#default_value' => $user_preferred_language->language,
       
   303     '#options' => $names,
       
   304     '#description' => $mode ? t("This account's default language for e-mails, and preferred language for site presentation.") : t("This account's default language for e-mails."),
       
   305   );
       
   306 }
       
   307 
       
   308 /**
       
   309  * Implements hook_form_FORM_ID_alter().
       
   310  */
       
   311 function locale_form_path_admin_form_alter(&$form, &$form_state) {
       
   312   $form['language'] = array(
       
   313     '#type' => 'select',
       
   314     '#title' => t('Language'),
       
   315     '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'),
       
   316     '#default_value' => $form['language']['#value'],
       
   317     '#weight' => -10,
       
   318     '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for <em>All languages</em>.'),
       
   319   );
       
   320 }
       
   321 
       
   322 /**
       
   323  * Implements hook_form_FORM_ID_alter().
       
   324  */
       
   325 function locale_form_node_type_form_alter(&$form, &$form_state) {
       
   326   if (isset($form['type'])) {
       
   327     $form['workflow']['language_content_type'] = array(
       
   328       '#type' => 'radios',
       
   329       '#title' => t('Multilingual support'),
       
   330       '#default_value' => variable_get('language_content_type_' . $form['#node_type']->type, 0),
       
   331       '#options' => array(t('Disabled'), t('Enabled')),
       
   332       '#description' => t('Enable multilingual support for this content type. If enabled, a language selection field will be added to the editing form, allowing you to select from one of the <a href="!languages">enabled languages</a>. If disabled, new posts are saved with the default language. Existing content will not be affected by changing this option.', array('!languages' => url('admin/config/regional/language'))),
       
   333     );
       
   334   }
       
   335 }
       
   336 
       
   337 /**
       
   338  * Return whether the given content type has multilingual support.
       
   339  *
       
   340  * @return
       
   341  *   True if multilingual support is enabled.
       
   342  */
       
   343 function locale_multilingual_node_type($type_name) {
       
   344   return (bool) variable_get('language_content_type_' . $type_name, 0);
       
   345 }
       
   346 
       
   347 /**
       
   348  * Implements hook_form_alter().
       
   349  *
       
   350  * Adds language fields to user forms.
       
   351  */
       
   352 function locale_form_alter(&$form, &$form_state, $form_id) {
       
   353   // Only alter user forms if there is more than one language.
       
   354   if (drupal_multilingual()) {
       
   355     // Display language selector when either creating a user on the admin
       
   356     // interface or editing a user account.
       
   357     if ($form_id == 'user_register_form' || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) {
       
   358       locale_language_selector_form($form, $form_state, $form['#user']);
       
   359     }
       
   360   }
       
   361 }
       
   362 
       
   363 /**
       
   364  * Implements hook_form_BASE_FORM_ID_alter().
       
   365  */
       
   366 function locale_form_node_form_alter(&$form, &$form_state) {
       
   367   if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
       
   368     $form['language'] = array(
       
   369       '#type' => 'select',
       
   370       '#title' => t('Language'),
       
   371       '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
       
   372       '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
       
   373     );
       
   374   }
       
   375   // Node type without language selector: assign the default for new nodes
       
   376   elseif (!isset($form['#node']->nid)) {
       
   377     $default = language_default();
       
   378     $form['language'] = array(
       
   379       '#type' => 'value',
       
   380       '#value' => $default->language
       
   381     );
       
   382   }
       
   383   $form['#submit'][] = 'locale_field_node_form_submit';
       
   384 }
       
   385 
       
   386 /**
       
   387  * Form submit handler for node_form().
       
   388  *
       
   389  * This submit handler needs to run before entity_form_submit_build_entity()
       
   390  * is invoked by node_form_submit_build_node(), because it alters the values of
       
   391  * attached fields. Therefore, it cannot be a hook_node_submit() implementation.
       
   392  */
       
   393 function locale_field_node_form_submit($form, &$form_state) {
       
   394   locale_field_entity_form_submit('node', $form, $form_state);
       
   395 }
       
   396 
       
   397 /**
       
   398  * Implements hook_form_FORM_ID_alter().
       
   399  */
       
   400 function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
       
   401   // If a content type has multilingual support we set the content language as
       
   402   // comment language.
       
   403   if ($form['language']['#value'] == LANGUAGE_NONE && locale_multilingual_node_type($form['#node']->type)) {
       
   404     global $language_content;
       
   405     $form['language']['#value'] = $language_content->language;
       
   406     $submit_callback = 'locale_field_comment_form_submit';
       
   407     array_unshift($form['actions']['preview']['#submit'], $submit_callback);
       
   408     array_unshift($form['#submit'], $submit_callback);
       
   409   }
       
   410 }
       
   411 
       
   412 /**
       
   413  * Form submit handler for comment_form().
       
   414  *
       
   415  * This submit handler needs to run before entity_form_submit_build_entity()
       
   416  * is invoked by comment_form_submit_build_comment(), because it alters the
       
   417  * values of attached fields.
       
   418  */
       
   419 function locale_field_comment_form_submit($form, &$form_state) {
       
   420   locale_field_entity_form_submit('comment', $form, $form_state);
       
   421 }
       
   422 
       
   423 /**
       
   424  * Handles field language on submit for the given entity type.
       
   425  *
       
   426  * Checks if Locale is registered as a translation handler and handle possible
       
   427  * language changes.
       
   428  */
       
   429 function locale_field_entity_form_submit($entity_type, $form, &$form_state ) {
       
   430   if (field_has_translation_handler($entity_type, 'locale')) {
       
   431     $entity = (object) $form_state['values'];
       
   432     $current_language = entity_language($entity_type, $entity);
       
   433     list(, , $bundle) = entity_extract_ids($entity_type, $entity);
       
   434 
       
   435     foreach (field_info_instances($entity_type, $bundle) as $instance) {
       
   436       $field_name = $instance['field_name'];
       
   437       $field = field_info_field($field_name);
       
   438       $previous_language = $form[$field_name]['#language'];
       
   439 
       
   440       // Handle a possible language change: new language values are inserted,
       
   441       // previous ones are deleted.
       
   442       if ($field['translatable'] && $previous_language != $current_language) {
       
   443         $form_state['values'][$field_name][$current_language] = $entity->{$field_name}[$previous_language];
       
   444         $form_state['values'][$field_name][$previous_language] = array();
       
   445       }
       
   446     }
       
   447   }
       
   448 }
       
   449 
       
   450 /**
       
   451  * Implements hook_theme().
       
   452  */
       
   453 function locale_theme() {
       
   454   return array(
       
   455     'locale_languages_overview_form' => array(
       
   456       'render element' => 'form',
       
   457     ),
       
   458     'locale_languages_configure_form' => array(
       
   459       'render element' => 'form',
       
   460     ),
       
   461     'locale_date_format_form' => array(
       
   462       'render element' => 'form',
       
   463     ),
       
   464   );
       
   465 }
       
   466 
       
   467 /**
       
   468  * Implements hook_field_language_alter().
       
   469  */
       
   470 function locale_field_language_alter(&$display_language, $context) {
       
   471   // Do not apply core language fallback rules if they are disabled or if Locale
       
   472   // is not registered as a translation handler.
       
   473   if (variable_get('locale_field_language_fallback', TRUE) && field_has_translation_handler($context['entity_type'], 'locale')) {
       
   474     locale_field_language_fallback($display_language, $context['entity'], $context['language']);
       
   475   }
       
   476 }
       
   477 
       
   478 /**
       
   479  * Applies language fallback rules to the fields attached to the given entity.
       
   480  *
       
   481  * Core language fallback rules simply check if fields have a field translation
       
   482  * for the requested language code. If so the requested language is returned,
       
   483  * otherwise all the fallback candidates are inspected to see if there is a
       
   484  * field translation available in another language.
       
   485  * By default this is called by locale_field_language_alter(), but this
       
   486  * behavior can be disabled by setting the 'locale_field_language_fallback'
       
   487  * variable to FALSE.
       
   488  *
       
   489  * @param $display_language
       
   490  *   A reference to an array of language codes keyed by field name.
       
   491  * @param $entity
       
   492  *   The entity to be displayed.
       
   493  * @param $langcode
       
   494  *   The language code $entity has to be displayed in.
       
   495  */
       
   496 function locale_field_language_fallback(&$display_language, $entity, $langcode) {
       
   497   // Lazily init fallback candidates to avoid unnecessary calls.
       
   498   $fallback_candidates = NULL;
       
   499   $field_languages = array();
       
   500 
       
   501   foreach ($display_language as $field_name => $field_language) {
       
   502     // If the requested language is defined for the current field use it,
       
   503     // otherwise search for a fallback value among the fallback candidates.
       
   504     if (isset($entity->{$field_name}[$langcode])) {
       
   505       $display_language[$field_name] = $langcode;
       
   506     }
       
   507     elseif (!empty($entity->{$field_name})) {
       
   508       if (!isset($fallback_candidates)) {
       
   509         require_once DRUPAL_ROOT . '/includes/language.inc';
       
   510         $fallback_candidates = language_fallback_get_candidates();
       
   511       }
       
   512       foreach ($fallback_candidates as $fallback_language) {
       
   513         if (isset($entity->{$field_name}[$fallback_language])) {
       
   514           $display_language[$field_name] = $fallback_language;
       
   515           break;
       
   516         }
       
   517       }
       
   518     }
       
   519   }
       
   520 }
       
   521 
       
   522 /**
       
   523  * Implements hook_entity_info_alter().
       
   524  */
       
   525 function locale_entity_info_alter(&$entity_info) {
       
   526   $entity_info['node']['translation']['locale'] = TRUE;
       
   527   if (isset($entity_info['comment'])) {
       
   528     $entity_info['comment']['translation']['locale'] = TRUE;
       
   529   }
       
   530 }
       
   531 
       
   532 /**
       
   533  * Implements hook_language_types_info().
       
   534  *
       
   535  * Defines the three core language types:
       
   536  * - Interface language is the only configurable language type in core. It is
       
   537  *   used by t() as the default language if none is specified.
       
   538  * - Content language is by default non-configurable and inherits the interface
       
   539  *   language negotiated value. It is used by the Field API to determine the
       
   540  *   display language for fields if no explicit value is specified.
       
   541  * - URL language is by default non-configurable and is determined through the
       
   542  *   URL language provider or the URL fallback provider if no language can be
       
   543  *   detected. It is used by l() as the default language if none is specified.
       
   544  */
       
   545 function locale_language_types_info() {
       
   546   require_once DRUPAL_ROOT . '/includes/locale.inc';
       
   547   return array(
       
   548     LANGUAGE_TYPE_INTERFACE => array(
       
   549       'name' => t('User interface text'),
       
   550       'description' => t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'),
       
   551     ),
       
   552     LANGUAGE_TYPE_CONTENT => array(
       
   553       'name' => t('Content'),
       
   554       'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
       
   555       'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE),
       
   556     ),
       
   557     LANGUAGE_TYPE_URL => array(
       
   558       'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK),
       
   559     ),
       
   560   );
       
   561 }
       
   562 
       
   563 /**
       
   564  * Implements hook_language_negotiation_info().
       
   565  */
       
   566 function locale_language_negotiation_info() {
       
   567   $file = 'includes/locale.inc';
       
   568   $providers = array();
       
   569 
       
   570   $providers[LOCALE_LANGUAGE_NEGOTIATION_URL] = array(
       
   571     'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL),
       
   572     'callbacks' => array(
       
   573       'language' => 'locale_language_from_url',
       
   574       'switcher' => 'locale_language_switcher_url',
       
   575       'url_rewrite' => 'locale_language_url_rewrite_url',
       
   576     ),
       
   577     'file' => $file,
       
   578     'weight' => -8,
       
   579     'name' => t('URL'),
       
   580     'description' => t('Determine the language from the URL (Path prefix or domain).'),
       
   581     'config' => 'admin/config/regional/language/configure/url',
       
   582   );
       
   583 
       
   584   $providers[LOCALE_LANGUAGE_NEGOTIATION_SESSION] = array(
       
   585     'callbacks' => array(
       
   586       'language' => 'locale_language_from_session',
       
   587       'switcher' => 'locale_language_switcher_session',
       
   588       'url_rewrite' => 'locale_language_url_rewrite_session',
       
   589     ),
       
   590     'file' => $file,
       
   591     'weight' => -6,
       
   592     'name' => t('Session'),
       
   593     'description' => t('Determine the language from a request/session parameter.'),
       
   594     'config' => 'admin/config/regional/language/configure/session',
       
   595   );
       
   596 
       
   597   $providers[LOCALE_LANGUAGE_NEGOTIATION_USER] = array(
       
   598     'callbacks' => array('language' => 'locale_language_from_user'),
       
   599     'file' => $file,
       
   600     'weight' => -4,
       
   601     'name' => t('User'),
       
   602     'description' => t("Follow the user's language preference."),
       
   603   );
       
   604 
       
   605   $providers[LOCALE_LANGUAGE_NEGOTIATION_BROWSER] = array(
       
   606     'callbacks' => array('language' => 'locale_language_from_browser'),
       
   607     'file' => $file,
       
   608     'weight' => -2,
       
   609     'cache' => 0,
       
   610     'name' => t('Browser'),
       
   611     'description' => t("Determine the language from the browser's language settings."),
       
   612   );
       
   613 
       
   614   $providers[LOCALE_LANGUAGE_NEGOTIATION_INTERFACE] = array(
       
   615     'types' => array(LANGUAGE_TYPE_CONTENT),
       
   616     'callbacks' => array('language' => 'locale_language_from_interface'),
       
   617     'file' => $file,
       
   618     'weight' => 8,
       
   619     'name' => t('Interface'),
       
   620     'description' => t('Use the detected interface language.'),
       
   621   );
       
   622 
       
   623   $providers[LOCALE_LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
       
   624     'types' => array(LANGUAGE_TYPE_URL),
       
   625     'callbacks' => array('language' => 'locale_language_url_fallback'),
       
   626     'file' => $file,
       
   627     'weight' => 8,
       
   628     'name' => t('URL fallback'),
       
   629     'description' => t('Use an already detected language for URLs if none is found.'),
       
   630   );
       
   631 
       
   632   return $providers;
       
   633 }
       
   634 
       
   635 /**
       
   636  * Implements hook_modules_enabled().
       
   637  */
       
   638 function locale_modules_enabled($modules) {
       
   639   include_once DRUPAL_ROOT . '/includes/language.inc';
       
   640   language_types_set();
       
   641   language_negotiation_purge();
       
   642 }
       
   643 
       
   644 /**
       
   645  * Implements hook_modules_disabled().
       
   646  */
       
   647 function locale_modules_disabled($modules) {
       
   648   locale_modules_enabled($modules);
       
   649 }
       
   650 
       
   651 // ---------------------------------------------------------------------------------
       
   652 // Locale core functionality
       
   653 
       
   654 /**
       
   655  * Provides interface translation services.
       
   656  *
       
   657  * This function is called from t() to translate a string if needed.
       
   658  *
       
   659  * @param $string
       
   660  *   A string to look up translation for. If omitted, all the
       
   661  *   cached strings will be returned in all languages already
       
   662  *   used on the page.
       
   663  * @param $context
       
   664  *   The context of this string.
       
   665  * @param $langcode
       
   666  *   Language code to use for the lookup.
       
   667  */
       
   668 function locale($string = NULL, $context = NULL, $langcode = NULL) {
       
   669   global $language;
       
   670 
       
   671   // Use the advanced drupal_static() pattern, since this is called very often.
       
   672   static $drupal_static_fast;
       
   673   if (!isset($drupal_static_fast)) {
       
   674     $drupal_static_fast['locale'] = &drupal_static(__FUNCTION__);
       
   675   }
       
   676   $locale_t = &$drupal_static_fast['locale'];
       
   677 
       
   678 
       
   679   if (!isset($string)) {
       
   680     // Return all cached strings if no string was specified
       
   681     return $locale_t;
       
   682   }
       
   683 
       
   684   $langcode = isset($langcode) ? $langcode : $language->language;
       
   685 
       
   686   // Store database cached translations in a static variable. Only build the
       
   687   // cache after $language has been set to avoid an unnecessary cache rebuild.
       
   688   if (!isset($locale_t[$langcode]) && isset($language)) {
       
   689     $locale_t[$langcode] = array();
       
   690     // Disabling the usage of string caching allows a module to watch for
       
   691     // the exact list of strings used on a page. From a performance
       
   692     // perspective that is a really bad idea, so we have no user
       
   693     // interface for this. Be careful when turning this option off!
       
   694     if (variable_get('locale_cache_strings', 1) == 1) {
       
   695       if ($cache = cache_get('locale:' . $langcode, 'cache')) {
       
   696         $locale_t[$langcode] = $cache->data;
       
   697       }
       
   698       elseif (lock_acquire('locale_cache_' . $langcode)) {
       
   699         // Refresh database stored cache of translations for given language.
       
   700         // We only store short strings used in current version, to improve
       
   701         // performance and consume less memory.
       
   702         $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < :length", array(':language' => $langcode, ':version' => VERSION, ':length' => variable_get('locale_cache_length', 75)));
       
   703         foreach ($result as $data) {
       
   704           $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation);
       
   705         }
       
   706         cache_set('locale:' . $langcode, $locale_t[$langcode]);
       
   707         lock_release('locale_cache_' . $langcode);
       
   708       }
       
   709     }
       
   710   }
       
   711 
       
   712   // If we have the translation cached, skip checking the database
       
   713   if (!isset($locale_t[$langcode][$context][$string])) {
       
   714 
       
   715     // We do not have this translation cached, so get it from the DB.
       
   716     $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default'", array(
       
   717       ':language' => $langcode,
       
   718       ':source' => $string,
       
   719       ':context' => (string) $context,
       
   720     ))->fetchObject();
       
   721     if ($translation) {
       
   722       // We have the source string at least.
       
   723       // Cache translation string or TRUE if no translation exists.
       
   724       $locale_t[$langcode][$context][$string] = (empty($translation->translation) ? TRUE : $translation->translation);
       
   725 
       
   726       if ($translation->version != VERSION) {
       
   727         // This is the first use of this string under current Drupal version. Save version
       
   728         // and clear cache, to include the string into caching next time. Saved version is
       
   729         // also a string-history information for later pruning of the tables.
       
   730         db_update('locales_source')
       
   731           ->fields(array('version' => VERSION))
       
   732           ->condition('lid', $translation->lid)
       
   733           ->execute();
       
   734         cache_clear_all('locale:', 'cache', TRUE);
       
   735       }
       
   736     }
       
   737     else {
       
   738       // We don't have the source string, cache this as untranslated.
       
   739       db_merge('locales_source')
       
   740         ->insertFields(array(
       
   741           'location' => request_uri(),
       
   742           'version' => VERSION,
       
   743         ))
       
   744         ->key(array(
       
   745           'source' => $string,
       
   746           'context' => (string) $context,
       
   747           'textgroup' => 'default',
       
   748         ))
       
   749         ->execute();
       
   750       $locale_t[$langcode][$context][$string] = TRUE;
       
   751       // Clear locale cache so this string can be added in a later request.
       
   752       cache_clear_all('locale:', 'cache', TRUE);
       
   753     }
       
   754   }
       
   755 
       
   756   return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]);
       
   757 }
       
   758 
       
   759 /**
       
   760  * Reset static variables used by locale().
       
   761  */
       
   762 function locale_reset() {
       
   763   drupal_static_reset('locale');
       
   764 }
       
   765 
       
   766 /**
       
   767  * Returns plural form index for a specific number.
       
   768  *
       
   769  * The index is computed from the formula of this language.
       
   770  *
       
   771  * @param $count
       
   772  *   Number to return plural for.
       
   773  * @param $langcode
       
   774  *   Optional language code to translate to a language other than
       
   775  *   what is used to display the page.
       
   776  *
       
   777  * @return
       
   778  *   The numeric index of the plural variant to use for this $langcode and
       
   779  *   $count combination or -1 if the language was not found or does not have a
       
   780  *   plural formula.
       
   781  */
       
   782 function locale_get_plural($count, $langcode = NULL) {
       
   783   global $language;
       
   784 
       
   785   // Used to locally cache the plural formulas for all languages.
       
   786   $plural_formulas = &drupal_static(__FUNCTION__, array());
       
   787 
       
   788   // Used to store precomputed plural indexes corresponding to numbers
       
   789   // individually for each language.
       
   790   $plural_indexes = &drupal_static(__FUNCTION__ . ':plurals', array());
       
   791 
       
   792   $langcode = $langcode ? $langcode : $language->language;
       
   793 
       
   794   if (!isset($plural_indexes[$langcode][$count])) {
       
   795     // Retrieve and statically cache the plural formulas for all languages.
       
   796     if (empty($plural_formulas)) {
       
   797       foreach (language_list() as $installed_language) {
       
   798         $plural_formulas[$installed_language->language] = $installed_language->formula;
       
   799       }
       
   800     }
       
   801     // If there is a plural formula for the language, evaluate it for the given
       
   802     // $count and statically cache the result for the combination of language
       
   803     // and count, since the result will always be identical.
       
   804     if (!empty($plural_formulas[$langcode])) {
       
   805       // $n is used inside the expression in the eval().
       
   806       $n = $count;
       
   807       $plural_indexes[$langcode][$count] = @eval('return intval(' . $plural_formulas[$langcode] . ');');
       
   808     }
       
   809     // In case there is no plural formula for English (no imported translation
       
   810     // for English), use a default formula.
       
   811     elseif ($langcode == 'en') {
       
   812       $plural_indexes[$langcode][$count] = (int) ($count != 1);
       
   813     }
       
   814     // Otherwise, return -1 (unknown).
       
   815     else {
       
   816       $plural_indexes[$langcode][$count] = -1;
       
   817     }
       
   818   }
       
   819   return $plural_indexes[$langcode][$count];
       
   820 }
       
   821 
       
   822 
       
   823 /**
       
   824  * Returns a language name
       
   825  */
       
   826 function locale_language_name($lang) {
       
   827   $list = &drupal_static(__FUNCTION__);
       
   828   if (!isset($list)) {
       
   829     $list = locale_language_list();
       
   830   }
       
   831   return ($lang && isset($list[$lang])) ? $list[$lang] : t('All');
       
   832 }
       
   833 
       
   834 /**
       
   835  * Returns array of language names
       
   836  *
       
   837  * @param $field
       
   838  *   'name' => names in current language, localized
       
   839  *   'native' => native names
       
   840  * @param $all
       
   841  *   Boolean to return all languages or only enabled ones
       
   842  */
       
   843 function locale_language_list($field = 'name', $all = FALSE) {
       
   844   if ($all) {
       
   845     $languages = language_list();
       
   846   }
       
   847   else {
       
   848     $languages = language_list('enabled');
       
   849     $languages = $languages[1];
       
   850   }
       
   851   $list = array();
       
   852   foreach ($languages as $language) {
       
   853     $list[$language->language] = ($field == 'name') ? t($language->name) : $language->$field;
       
   854   }
       
   855   return $list;
       
   856 }
       
   857 
       
   858 /**
       
   859  * Implements hook_modules_installed().
       
   860  */
       
   861 function locale_modules_installed($modules) {
       
   862   locale_system_update($modules);
       
   863 }
       
   864 
       
   865 /**
       
   866  * Implements hook_themes_enabled().
       
   867  *
       
   868  * @todo This is technically wrong. We must not import upon enabling, but upon
       
   869  *   initial installation. The theme system is missing an installation hook.
       
   870  */
       
   871 function locale_themes_enabled($themes) {
       
   872   locale_system_update($themes);
       
   873 }
       
   874 
       
   875 /**
       
   876  * Imports translations when new modules or themes are installed.
       
   877  *
       
   878  * This function will either import translation for the component change
       
   879  * right away, or start a batch if more files need to be imported.
       
   880  *
       
   881  * @param $components
       
   882  *   An array of component (theme and/or module) names to import
       
   883  *   translations for.
       
   884  */
       
   885 function locale_system_update($components) {
       
   886   include_once DRUPAL_ROOT . '/includes/locale.inc';
       
   887   if ($batch = locale_batch_by_component($components)) {
       
   888     batch_set($batch);
       
   889   }
       
   890 }
       
   891 
       
   892 /**
       
   893  * Implements hook_js_alter().
       
   894  *
       
   895  * This function checks all JavaScript files currently added via drupal_add_js()
       
   896  * and invokes parsing if they have not yet been parsed for Drupal.t()
       
   897  * and Drupal.formatPlural() calls. Also refreshes the JavaScript translation
       
   898  * file if necessary, and adds it to the page.
       
   899  */
       
   900 function locale_js_alter(&$javascript) {
       
   901   global $language;
       
   902 
       
   903   $dir = 'public://' . variable_get('locale_js_directory', 'languages');
       
   904   $parsed = variable_get('javascript_parsed', array());
       
   905   $files = $new_files = FALSE;
       
   906 
       
   907   // Require because locale_js_alter() could be called without locale_init().
       
   908   require_once DRUPAL_ROOT . '/includes/locale.inc';
       
   909 
       
   910   foreach ($javascript as $item) {
       
   911     if ($item['type'] == 'file') {
       
   912       $files = TRUE;
       
   913       $filepath = $item['data'];
       
   914       if (!in_array($filepath, $parsed)) {
       
   915         // Don't parse our own translations files.
       
   916         if (substr($filepath, 0, strlen($dir)) != $dir) {
       
   917           _locale_parse_js_file($filepath);
       
   918           $parsed[] = $filepath;
       
   919           $new_files = TRUE;
       
   920         }
       
   921       }
       
   922     }
       
   923   }
       
   924 
       
   925   // If there are any new source files we parsed, invalidate existing
       
   926   // JavaScript translation files for all languages, adding the refresh
       
   927   // flags into the existing array.
       
   928   if ($new_files) {
       
   929     $parsed += _locale_invalidate_js();
       
   930   }
       
   931 
       
   932   // If necessary, rebuild the translation file for the current language.
       
   933   if (!empty($parsed['refresh:' . $language->language])) {
       
   934     // Don't clear the refresh flag on failure, so that another try will
       
   935     // be performed later.
       
   936     if (_locale_rebuild_js()) {
       
   937       unset($parsed['refresh:' . $language->language]);
       
   938     }
       
   939     // Store any changes after refresh was attempted.
       
   940     variable_set('javascript_parsed', $parsed);
       
   941   }
       
   942   // If no refresh was attempted, but we have new source files, we need
       
   943   // to store them too. This occurs if current page is in English.
       
   944   elseif ($new_files) {
       
   945     variable_set('javascript_parsed', $parsed);
       
   946   }
       
   947 
       
   948   // Add the translation JavaScript file to the page.
       
   949   if ($files && !empty($language->javascript)) {
       
   950     // Add the translation JavaScript file to the page.
       
   951     $file = $dir . '/' . $language->language . '_' . $language->javascript . '.js';
       
   952     $javascript[$file] = drupal_js_defaults($file);
       
   953   }
       
   954 }
       
   955 
       
   956 /**
       
   957  * Implements hook_css_alter().
       
   958  *
       
   959  * This function checks all CSS files currently added via drupal_add_css() and
       
   960  * and checks to see if a related right to left CSS file should be included.
       
   961  */
       
   962 function locale_css_alter(&$css) {
       
   963   global $language;
       
   964 
       
   965   // If the current language is RTL, add the CSS file with the RTL overrides.
       
   966   if ($language->direction == LANGUAGE_RTL) {
       
   967     foreach ($css as $data => $item) {
       
   968       // Only provide RTL overrides for files.
       
   969       if ($item['type'] == 'file') {
       
   970         $rtl_path = str_replace('.css', '-rtl.css', $item['data']);
       
   971         if (file_exists($rtl_path) && !isset($css[$rtl_path])) {
       
   972           // Replicate the same item, but with the RTL path and a little larger
       
   973           // weight so that it appears directly after the original CSS file.
       
   974           $item['data'] = $rtl_path;
       
   975           $item['weight'] += 0.0001;
       
   976           $css[$rtl_path] = $item;
       
   977         }
       
   978       }
       
   979     }
       
   980   }
       
   981 }
       
   982 
       
   983  /**
       
   984  * Implement hook_library_alter().
       
   985  *
       
   986  * Provides the language support for the jQuery UI Date Picker.
       
   987  */
       
   988 function locale_library_alter(&$libraries, $module) {
       
   989   if ($module == 'system' && isset($libraries['ui.datepicker'])) {
       
   990     global $language;
       
   991     // locale.datepicker.js should be added in the JS_LIBRARY group, so that
       
   992     // this attach behavior will execute early. JS_LIBRARY is the default for
       
   993     // hook_library_info_alter(), thus does not have to be specified explicitly.
       
   994     $datepicker = drupal_get_path('module', 'locale') . '/locale.datepicker.js';
       
   995     $libraries['ui.datepicker']['js'][$datepicker] = array();
       
   996     $libraries['ui.datepicker']['js'][] = array(
       
   997       'data' => array(
       
   998         'jquery' => array(
       
   999           'ui' => array(
       
  1000             'datepicker' => array(
       
  1001               'isRTL' => $language->direction == LANGUAGE_RTL,
       
  1002               'firstDay' => variable_get('date_first_day', 0),
       
  1003             ),
       
  1004           ),
       
  1005         ),
       
  1006       ),
       
  1007       'type' => 'setting',
       
  1008     );
       
  1009   }
       
  1010 }
       
  1011 
       
  1012 // ---------------------------------------------------------------------------------
       
  1013 // Language switcher block
       
  1014 
       
  1015 /**
       
  1016  * Implements hook_block_info().
       
  1017  */
       
  1018 function locale_block_info() {
       
  1019   include_once DRUPAL_ROOT . '/includes/language.inc';
       
  1020   $block = array();
       
  1021   $info = language_types_info();
       
  1022   foreach (language_types_configurable(FALSE) as $type) {
       
  1023     $block[$type] = array(
       
  1024       'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])),
       
  1025       // Not worth caching.
       
  1026       'cache' => DRUPAL_NO_CACHE,
       
  1027     );
       
  1028   }
       
  1029   return $block;
       
  1030 }
       
  1031 
       
  1032 /**
       
  1033  * Implements hook_block_view().
       
  1034  *
       
  1035  * Displays a language switcher. Only show if we have at least two languages.
       
  1036  */
       
  1037 function locale_block_view($type) {
       
  1038   if (drupal_multilingual()) {
       
  1039     $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
       
  1040     $links = language_negotiation_get_switch_links($type, $path);
       
  1041 
       
  1042     if (isset($links->links)) {
       
  1043       drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
       
  1044       $class = "language-switcher-{$links->provider}";
       
  1045       $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
       
  1046       $block['content'] = theme('links__locale_block', $variables);
       
  1047       $block['subject'] = t('Languages');
       
  1048       return $block;
       
  1049     }
       
  1050   }
       
  1051 }
       
  1052 
       
  1053 /**
       
  1054  * Implements hook_url_outbound_alter().
       
  1055  *
       
  1056  * Rewrite outbound URLs with language based prefixes.
       
  1057  */
       
  1058 function locale_url_outbound_alter(&$path, &$options, $original_path) {
       
  1059   // Only modify internal URLs.
       
  1060   if (!$options['external'] && drupal_multilingual()) {
       
  1061     static $drupal_static_fast;
       
  1062     if (!isset($drupal_static_fast)) {
       
  1063       $drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__);
       
  1064     }
       
  1065     $callbacks = &$drupal_static_fast['callbacks'];
       
  1066 
       
  1067     if (!isset($callbacks)) {
       
  1068       $callbacks = array();
       
  1069       include_once DRUPAL_ROOT . '/includes/language.inc';
       
  1070 
       
  1071       foreach (language_types_configurable() as $type) {
       
  1072         // Get URL rewriter callbacks only from enabled language providers.
       
  1073         $negotiation = variable_get("language_negotiation_$type", array());
       
  1074 
       
  1075         foreach ($negotiation as $id => $provider) {
       
  1076           if (isset($provider['callbacks']['url_rewrite'])) {
       
  1077             if (isset($provider['file'])) {
       
  1078               require_once DRUPAL_ROOT . '/' . $provider['file'];
       
  1079             }
       
  1080             // Avoid duplicate callback entries.
       
  1081             $callbacks[$provider['callbacks']['url_rewrite']] = TRUE;
       
  1082           }
       
  1083         }
       
  1084       }
       
  1085 
       
  1086       $callbacks = array_keys($callbacks);
       
  1087     }
       
  1088 
       
  1089     foreach ($callbacks as $callback) {
       
  1090       $callback($path, $options);
       
  1091     }
       
  1092 
       
  1093     // No language dependent path allowed in this mode.
       
  1094     if (empty($callbacks)) {
       
  1095       unset($options['language']);
       
  1096     }
       
  1097   }
       
  1098 }