Code coverage for /20081101/modules/locale/locale.module

Line #Times calledCode
1
<?php
2
// $Id: locale.module,v 1.230 2008/10/22 19:39:36 dries Exp $
3
4
/**
5
 * @file
6
 *   Add language handling functionality and enables the translation of the
7
 *   user interface to languages other than English.
8
 *
9
 *   When enabled, multiple languages can be set up. The site interface
10
 *   can be displayed in different languages, as well as nodes can have
languages
11
 *   assigned. The setup of languages and translations is completely web
based.
12
 *   Gettext portable object files are supported.
13
 */
14
15
/**
16
 * Language written left to right. Possible value of $language->direction.
17
 */
1899
define('LANGUAGE_LTR', 0);
19
20
/**
21
 * Language written right to left. Possible value of $language->direction.
22
 */
2399
define('LANGUAGE_RTL', 1);
24
25
26
//
---------------------------------------------------------------------------------
27
// Hook implementations
28
29
/**
30
 * Implementation of hook_help().
31
 */
3299
function locale_help($path, $arg) {
33
  switch ($path) {
3467
    case 'admin/help#locale':
351
      $output = '<p>' . t('The locale module allows your Drupal site to be
presented in languages other than the default English, a defining feature
of multi-lingual websites. The locale module works by 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 later review by a translator.') . '</p>';
361
      $output .= '<p>' . t('Beyond translation of the Drupal interface, the
locale module provides a feature set tailored to the needs of a
multi-lingual site. 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. Your site content can be created in
(and translated to) any enabled language, and each post may have a
language-appropriate alias for each of its translations. The locale module
works in concert with the <a href="@content-help">content translation
module</a> to manage translated content.', array('@content-help' =>
url('admin/help/translation'))) . '</p>';
371
      $output .= '<p>' . t('Translations may be provided by:') . '</p>';
381
      $output .= '<ul><li>' . t("translating the original text via the
locale module's integrated web interface, or") . '</li>';
391
      $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
contain 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 translation page</a>.',
array('@translations' => 'http://drupal.org/project/translations')) .
'</li></ul>';
401
      $output .= '<p>' . 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/build/translate/import'), '@export' =>
url('admin/build/translate/export'))) . '</p>';
411
      $output .= '<p>' . t('For more information, see the online handbook
entry for <a href="@locale">Locale module</a>.', array('@locale' =>
'http://drupal.org/handbook/modules/locale/')) . '</p>';
421
      return $output;
4367
    case 'admin/settings/language':
447
      $output = '<p>' . t("This page provides an overview of your site's
enabled languages. If multiple languages are available and enabled, the
text on your site interface may be translated, registered users may select
their preferred language on the <em>My account</em> page, and site authors
may indicate a specific language when creating posts. Languages will be
displayed in the order you specify in places such as the language switcher
block, or the language dropdown when creating or editing posts. The site's
default language is used for anonymous visitors and for users who have not
selected a preferred language.") . '</p>';
457
      $output .= '<p>' . t('For each language available on the site, use
the <em>edit</em> link to configure language details, including name, an
optional language-specific path or domain, and whether the language is
natively presented either left-to-right or right-to-left. These languages
also appear in the <em>Language</em> selection when creating a post of a
content type with multilingual support.') . '</p>';
467
      $output .= '<p>' . t('Use the <a href="@add-language">add language
page</a> to enable additional languages (and automatically import files
from a translation package, if available), the <a href="@search">translate
interface page</a> to locate strings for manual translation, or the <a
href="@import">import page</a> to add translations from individual
<em>.po</em> files. A number of contributed translation packages containing
<em>.po</em> files are available on the <a href="@translations">Drupal.org
translations page</a>.', array('@add-language' =>
url('admin/settings/language/add'), '@search' =>
url('admin/build/translate/search'), '@import' =>
url('admin/build/translate/import'), '@translations' =>
'http://drupal.org/project/translations')) . '</p>';
477
      $output .= '<p>' . t('To rearrange languages, grab a drag-and-drop
handle under the <em>English name</em> column and drag the item to a new
location in the list. (Grab a handle by clicking and holding the mouse
while hovering over a handle icon.) Remember that your changes will not be
saved until you click the <em>Save configuration</em> button at the bottom
of the page.') . '</p>';
487
      return $output;
4967
    case 'admin/settings/language/add':
503
      return '<p>' . t('Add all languages 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>';
5167
    case 'admin/settings/language/configure':
520
      $output = '<p>' . t("Language negotiation settings determine the
site's presentation language. Available options include:") . '</p>';
530
      $output .= '<ul><li>' . t('<strong>None.</strong> The default
language is used for site presentation, though users may (optionally)
select a preferred language on the <em>My Account</em> page. (User language
preferences will be used for site e-mails, if available.)') . '</li>';
540
      $output .= '<li>' . t('<strong>Path prefix only.</strong> The
presentation language is determined by examining the path for a language
code or other custom string that matches the path prefix (if any) specified
for each language. If a suitable prefix is not identified, the default
language is used. <em>Example: "example.com/de/contact" sets presentation
language to German based on the use of "de" within the path.</em>') .
'</li>';
550
      $output .= '<li>' . t("<strong>Path prefix with language
fallback.</strong> The presentation language is determined by examining the
path for a language code or other custom string that matches the path
prefix (if any) specified for each language. If a suitable prefix is not
identified, the display language is determined by the user's language
preferences from the <em>My Account</em> page, or by the browser's language
settings. If a presentation language cannot be determined, the default
language is used.") . '</li>';
560
      $output .= '<li>' . t('<strong>Domain name only.</strong> The
presentation language is determined by examining the domain used to access
the site, and comparing it to the language domain (if any) specified for
each language. If a match is not identified, the default language is used.
<em>Example: "http://de.example.com/contact" sets presentation language to
German based on the use of "http://de.example.com" in the domain.</em>') .
'</li></ul>';
570
      $output .= '<p>' . t('The path prefix or domain name for a language
may be set by editing the <a href="@languages">available languages</a>. In
the absence of an appropriate match, the site is displayed in the <a
href="@languages">default language</a>.', array('@languages' =>
url('admin/settings/language'))) . '</p>';
580
      return $output;
5967
    case 'admin/build/translate':
600
      $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>';
610
      $output .= '<p>' . t('Review the <a href="@languages">languages
page</a> for more information on adding support for additional languages.',
array('@languages' => url('admin/settings/language'))) . '</p>';
620
      return $output;
6367
    case 'admin/build/translate/import':
640
      $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>';
650
      $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/settings/language/add'), '@translations' =>
'http://drupal.org/project/translations')) . '</p>';
660
      return $output;
6767
    case 'admin/build/translate/export':
680
      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>';
6967
    case 'admin/build/translate/search':
708
      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/build/translate/export'))) .
'</p>';
7167
    case 'admin/build/block/configure':
720
      if ($arg[4] == 'locale' && $arg[5] == 0) {
730
        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 something other
than <em>None</em>.', array('@languages' => url('admin/settings/language'),
'@configuration' => url('admin/settings/language/configure'))) . '</p>';
740
      }
750
      break;
760
  }
7767
}
78
79
/**
80
 * Implementation of hook_menu().
81
 */
8299
function locale_menu() {
83
  // Manage languages
845
  $items['admin/settings/language'] = array(
855
    'title' => 'Languages',
865
    'description' => 'Configure languages for content and the user
interface.',
875
    'page callback' => 'drupal_get_form',
885
    'page arguments' => array('locale_languages_overview_form'),
895
    'access arguments' => array('administer languages'),
90
  );
915
  $items['admin/settings/language/overview'] = array(
925
    'title' => 'List',
935
    'weight' => 0,
945
    'type' => MENU_DEFAULT_LOCAL_TASK,
95
  );
965
  $items['admin/settings/language/add'] = array(
975
    'title' => 'Add language',
985
    'page callback' => 'locale_languages_add_screen', // two forms
concatenated
995
    'access arguments' => array('administer languages'),
1005
    'weight' => 5,
1015
    'type' => MENU_LOCAL_TASK,
102
  );
1035
  $items['admin/settings/language/configure'] = array(
1045
    'title' => 'Configure',
1055
    'page callback' => 'drupal_get_form',
1065
    'page arguments' => array('locale_languages_configure_form'),
1075
    'access arguments' => array('administer languages'),
1085
    'weight' => 10,
1095
    'type' => MENU_LOCAL_TASK,
110
  );
1115
  $items['admin/settings/language/edit/%'] = array(
1125
    'title' => 'Edit language',
1135
    'page callback' => 'drupal_get_form',
1145
    'page arguments' => array('locale_languages_edit_form', 4),
1155
    'access arguments' => array('administer languages'),
1165
    'type' => MENU_CALLBACK,
117
  );
1185
  $items['admin/settings/language/delete/%'] = array(
1195
    'title' => 'Confirm',
1205
    'page callback' => 'drupal_get_form',
1215
    'page arguments' => array('locale_languages_delete_form', 4),
1225
    'access arguments' => array('administer languages'),
1235
    'type' => MENU_CALLBACK,
124
  );
125
126
  // Translation functionality
1275
  $items['admin/build/translate'] = array(
1285
    'title' => 'Translate interface',
1295
    'description' => 'Translate the built in interface and optionally other
text.',
1305
    'page callback' => 'locale_translate_overview_screen', // not a form,
just a table
1315
    'access arguments' => array('translate interface'),
132
  );
1335
  $items['admin/build/translate/overview'] = array(
1345
    'title' => 'Overview',
1355
    'weight' => 0,
1365
    'type' => MENU_DEFAULT_LOCAL_TASK,
137
  );
1385
  $items['admin/build/translate/search'] = array(
1395
    'title' => 'Search',
1405
    'weight' => 10,
1415
    'type' => MENU_LOCAL_TASK,
1425
    'page callback' => 'locale_translate_seek_screen', // search results
and form concatenated
1435
    'access arguments' => array('translate interface'),
144
  );
1455
  $items['admin/build/translate/import'] = array(
1465
    'title' => 'Import',
1475
    'page callback' => 'drupal_get_form',
1485
    'page arguments' => array('locale_translate_import_form'),
1495
    'access arguments' => array('translate interface'),
1505
    'weight' => 20,
1515
    'type' => MENU_LOCAL_TASK,
152
  );
1535
  $items['admin/build/translate/export'] = array(
1545
    'title' => 'Export',
1555
    'page callback' => 'locale_translate_export_screen',  // possibly
multiple forms concatenated
1565
    'access arguments' => array('translate interface'),
1575
    'weight' => 30,
1585
    'type' => MENU_LOCAL_TASK,
159
  );
1605
  $items['admin/build/translate/edit/%'] = array(
1615
    'title' => 'Edit string',
1625
    'page callback' => 'drupal_get_form',
1635
    'page arguments' => array('locale_translate_edit_form', 4),
1645
    'access arguments' => array('translate interface'),
1655
    'type' => MENU_CALLBACK,
166
  );
1675
  $items['admin/build/translate/delete/%'] = array(
1685
    'title' => 'Delete string',
1695
    'page callback' => 'locale_translate_delete_page',
1705
    'page arguments' => array(4),
1715
    'access arguments' => array('translate interface'),
1725
    'type' => MENU_CALLBACK,
173
  );
174
1755
  return $items;
1760
}
177
178
/**
179
 * Wrapper function to be able to set callbacks in locale.inc
180
 */
18199
function locale_inc_callback() {
18212
  $args = func_get_args();
18312
  $function = array_shift($args);
18412
  include_once DRUPAL_ROOT . '/includes/locale.inc';
18512
  return call_user_func_array($function, $args);
1860
}
187
188
/**
189
 * Implementation of hook_perm().
190
 */
19199
function locale_perm() {
192
  return array(
193
    'administer languages' => array(
1943
      'title' => t('Administer languages'),
1953
      'description' => t('Manage the languages in which the website content
and interface text may be displayed.'),
1963
    ),
197
    'translate interface' => array(
1983
      'title' => t('Translate the interface'),
1993
      'description' => t('Translate the text of the website interface.'),
2003
    ),
2013
  );
2020
}
203
204
/**
205
 * Implementation of hook_locale().
206
 */
20799
function locale_locale($op = 'groups') {
208
  switch ($op) {
2098
    case 'groups':
2108
      return array('default' => t('Built-in interface'));
2110
  }
2120
}
213
214
/**
215
 * Implementation of hook_user_register().
216
 */
21799
function locale_user_register(&$edit, &$user, $category = NULL) {
218
  // If we have more then one language and either creating a user on the
219
  // admin interface or edit the user, show the language selector.
2200
  if (variable_get('language_count', 1) > 1 && user_access('administer
users')) {
2210
    return locale_language_selector_form($user);
2220
  }
2230
}
224
225
/**
226
 * Implementation of hook_user_form().
227
 */
22899
function locale_user_form(&$edit, &$user, $category = NULL) {
229
  // If we have more then one language and either creating a user on the
230
  // admin interface or edit the user, show the language selector.
2310
  if (variable_get('language_count', 1) > 1 && $category == 'account') {
2320
    return locale_language_selector_form($user);
2330
  }
2340
}
235
23699
function locale_language_selector_form($user) {
2370
  global $language;
2380
  $languages = language_list('enabled');
2390
  $languages = $languages[1];
240
241
  // If the user is being created, we set the user language to the page
language.
2420
  $user_preferred_language = $user ? user_preferred_language($user) :
$language;
243
2440
  $names = array();
2450
  foreach ($languages as $langcode => $item) {
2460
    $name = t($item->name);
2470
    $names[$langcode] = $name . ($item->native != $name ? ' (' .
$item->native . ')' : '');
2480
  }
2490
  $form['locale'] = array(
2500
    '#type' => 'fieldset',
2510
    '#title' => t('Language settings'),
2520
    '#weight' => 1,
253
  );
254
255
  // Get language negotiation settings.
2560
  $mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
2570
  $form['locale']['language'] = array(
2580
    '#type' => (count($names) <= 5 ? 'radios' : 'select'),
2590
    '#title' => t('Language'),
2600
    '#default_value' => $user_preferred_language->language,
2610
    '#options' => $names,
2620
    '#description' => ($mode == LANGUAGE_NEGOTIATION_PATH) ? t("This
account's default language for e-mails, and preferred language for site
presentation.") : t("This account's default language for e-mails."),
263
  );
2640
  return $form;
2650
}
266
267
/**
268
 * Implementation of hook_form_alter(). Adds language fields to forms.
269
 */
27099
function locale_form_alter(&$form, $form_state, $form_id) {
271
  switch ($form_id) {
272
273
    // Language field for paths
27467
    case 'path_admin_form':
2750
      $form['language'] = array(
2760
        '#type' => 'select',
2770
        '#title' => t('Language'),
2780
        '#options' => array('' => t('All languages')) +
locale_language_list('name'),
2790
        '#default_value' => $form['language']['#value'],
2800
        '#weight' => -10,
2810
        '#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>.'),
282
      );
2830
      break;
284
285
    // Language setting for content types
28667
    case 'node_type_form':
2873
      if (isset($form['identity']['type'])) {
2883
        $form['workflow']['language_content_type'] = array(
2893
          '#type' => 'radios',
2903
          '#title' => t('Multilingual support'),
2913
          '#default_value' => variable_get('language_content_type_' .
$form['#node_type']->type, 0),
2923
          '#options' => array(t('Disabled'), t('Enabled')),
2933
          '#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/settings/language'))),
294
        );
2953
      }
2963
      break;
297
298
    // Language field for nodes
29964
    default:
30064
      if (isset($form['#id']) && $form['#id'] == 'node-form') {
3016
        if (isset($form['#node']->type) &&
variable_get('language_content_type_' . $form['#node']->type, 0)) {
3026
          $form['language'] = array(
3036
            '#type' => 'select',
3046
            '#title' => t('Language'),
3056
            '#default_value' => (isset($form['#node']->language) ?
$form['#node']->language : ''),
3066
            '#options' => array('' => t('Language neutral')) +
locale_language_list('name'),
307
          );
3086
        }
309
        // Node type without language selector: assign the default for new
nodes
3100
        elseif (!isset($form['#node']->nid)) {
3110
          $default = language_default();
3120
          $form['language'] = array(
3130
            '#type' => 'value',
3140
            '#value' => $default->language
3150
          );
3160
        }
3176
      }
31864
  }
31967
}
320
321
/**
322
 * Implementation of hook_theme().
323
 */
32499
function locale_theme() {
325
  return array(
326
    'locale_languages_overview_form' => array(
3274
      'arguments' => array('form' => array()),
3284
    ),
3294
  );
3300
}
331
332
//
---------------------------------------------------------------------------------
333
// Locale core functionality
334
335
/**
336
 * Provides interface translation services.
337
 *
338
 * This function is called from t() to translate a string if needed.
339
 *
340
 * @param $string
341
 *   A string to look up translation for. If omitted, all the
342
 *   cached strings will be returned in all languages already
343
 *   used on the page.
344
 * @param $langcode
345
 *   Language code to use for the lookup.
346
 * @param $reset
347
 *   Set to TRUE to reset the in-memory cache.
348
 */
34999
function locale($string = NULL, $langcode = NULL, $reset = FALSE) {
3502
  global $language;
3512
  static $locale_t;
352
3532
  if ($reset) {
354
    // Reset in-memory cache.
3551
    $locale_t = NULL;
3561
  }
357
3582
  if (!isset($string)) {
359
    // Return all cached strings if no string was specified
3601
    return $locale_t;
3610
  }
362
3632
  $langcode = isset($langcode) ? $langcode : $language->language;
364
365
  // Store database cached translations in a static var.
3662
  if (!isset($locale_t[$langcode])) {
3672
    $locale_t[$langcode] = array();
368
    // Disabling the usage of string caching allows a module to watch for
369
    // the exact list of strings used on a page. From a performance
370
    // perspective that is a really bad idea, so we have no user
371
    // interface for this. Be careful when turning this option off!
3722
    if (variable_get('locale_cache_strings', 1) == 1) {
3732
      if ($cache = cache_get('locale:' . $langcode, 'cache')) {
3740
        $locale_t[$langcode] = $cache->data;
3750
      }
376
      else {
377
        // Refresh database stored cache of translations for given
language.
378
        // We only store short strings used in current version, to improve
379
        // performance and consume less memory.
3802
        $result = db_query("SELECT s.source, t.translation, t.language FROM
{locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND
t.language = '%s' WHERE s.textgroup = 'default' AND s.version = '%s' AND
LENGTH(s.source) < 75", $langcode, VERSION);
3812
        while ($data = db_fetch_object($result)) {
3821
          $locale_t[$langcode][$data->source] = (empty($data->translation)
? TRUE : $data->translation);
3831
        }
3842
        cache_set('locale:' . $langcode, $locale_t[$langcode]);
385
      }
3862
    }
3872
  }
388
389
  // If we have the translation cached, skip checking the database
3902
  if (!isset($locale_t[$langcode][$string])) {
391
392
    // We do not have this translation cached, so get it from the DB.
3932
    $translation = db_fetch_object(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 = '%s' WHERE s.source = '%s' AND s.textgroup =
'default'", $langcode, $string));
3942
    if ($translation) {
395
      // We have the source string at least.
396
      // Cache translation string or TRUE if no translation exists.
3970
      $locale_t[$langcode][$string] = (empty($translation->translation) ?
TRUE : $translation->translation);
398
3990
      if ($translation->version != VERSION) {
400
        // This is the first use of this string under current Drupal
version. Save version
401
        // and clear cache, to include the string into caching next time.
Saved version is
402
        // also a string-history information for later pruning of the
tables.
4030
        db_query("UPDATE {locales_source} SET version = '%s' WHERE lid =
%d", VERSION, $translation->lid);
4040
        cache_clear_all('locale:', 'cache', TRUE);
4050
      }
4060
    }
407
    else {
408
      // We don't have the source string, cache this as untranslated.
4092
      db_query("INSERT INTO {locales_source} (location, source, textgroup,
version) VALUES ('%s', '%s', 'default', '%s')", request_uri(), $string,
VERSION);
4102
      $locale_t[$langcode][$string] = TRUE;
411
      // Clear locale cache so this string can be added in a later request.
4122
      cache_clear_all('locale:', 'cache', TRUE);
413
    }
4142
  }
415
4162
  return ($locale_t[$langcode][$string] === TRUE ? $string :
$locale_t[$langcode][$string]);
4170
}
418
419
/**
420
 * Returns plural form index for a specific number.
421
 *
422
 * The index is computed from the formula of this language.
423
 *
424
 * @param $count
425
 *   Number to return plural for.
426
 * @param $langcode
427
 *   Optional language code to translate to a language other than
428
 *   what is used to display the page.
429
 */
43099
function locale_get_plural($count, $langcode = NULL) {
4317
  global $language;
4327
  static $locale_formula, $plurals = array();
433
4347
  $langcode = $langcode ? $langcode : $language->language;
435
4367
  if (!isset($plurals[$langcode][$count])) {
4377
    if (!isset($locale_formula)) {
4387
      $language_list = language_list();
4397
      $locale_formula[$langcode] = $language_list[$langcode]->formula;
4407
    }
4417
    if ($locale_formula[$langcode]) {
4420
      $n = $count;
4430
      $plurals[$langcode][$count] = @eval('return intval(' .
$locale_formula[$langcode] . ');');
4440
      return $plurals[$langcode][$count];
4450
    }
446
    else {
4477
      $plurals[$langcode][$count] = -1;
4487
      return -1;
449
    }
4500
  }
4510
  return $plurals[$langcode][$count];
4520
}
453
454
455
/**
456
 * Returns a language name
457
 */
45899
function locale_language_name($lang) {
4590
  static $list = NULL;
4600
  if (!isset($list)) {
4610
    $list = locale_language_list();
4620
  }
4630
  return ($lang && isset($list[$lang])) ? $list[$lang] : t('All');
4640
}
465
466
/**
467
 * Returns array of language names
468
 *
469
 * @param $field
470
 *   'name' => names in current language, localized
471
 *   'native' => native names
472
 * @param $all
473
 *   Boolean to return all languages or only enabled ones
474
 */
47599
function locale_language_list($field = 'name', $all = FALSE) {
47614
  if ($all) {
4778
    $languages = language_list();
4788
  }
479
  else {
4806
    $languages = language_list('enabled');
4816
    $languages = $languages[1];
482
  }
48314
  $list = array();
48414
  foreach ($languages as $language) {
48514
    $list[$language->language] = ($field == 'name') ? t($language->name) :
$language->$field;
48614
  }
48714
  return $list;
4880
}
489
490
/**
491
 * Imports translations when new modules or themes are installed or
enabled.
492
 *
493
 * This function will either import translation for the component change
494
 * right away, or start a batch if more files need to be imported.
495
 *
496
 * @param $components
497
 *   An array of component (theme and/or module) names to import
498
 *   translations for.
499
 */
50099
function locale_system_update($components) {
5011
  include_once DRUPAL_ROOT . '/includes/locale.inc';
5021
  if ($batch = locale_batch_by_component($components)) {
5030
    batch_set($batch);
5040
  }
5051
}
506
507
/**
508
 * Update JavaScript translation file, if required, and add it to the page.
509
 *
510
 * This function checks all JavaScript files currently added via
drupal_add_js()
511
 * and invokes parsing if they have not yet been parsed for Drupal.t()
512
 * and Drupal.formatPlural() calls. Also refreshes the JavaScript
translation
513
 * file if necessary, and adds it to the page.
514
 */
51599
function locale_update_js_files() {
51670
  global $language;
517
51870
  $dir = file_create_path(variable_get('locale_js_directory',
'languages'));
51970
  $parsed = variable_get('javascript_parsed', array());
520
521
  // Get an array of all the JavaScript added so far.
52270
  $javascript = drupal_add_js();
52370
  $files = $new_files = FALSE;
524
52570
  foreach ($javascript as $scope) {
52625
    foreach ($scope as $type => $data) {
52725
      if ($type != 'setting' && $type != 'inline') {
52825
        foreach ($data as $filepath => $info) {
52925
          $files = TRUE;
53025
          if (!in_array($filepath, $parsed)) {
531
            // Don't parse our own translations files.
53211
            if (substr($filepath, 0, strlen($dir)) != $dir) {
53311
              locale_inc_callback('_locale_parse_js_file', $filepath);
53411
              watchdog('locale', 'Parsed JavaScript file %file.',
array('%file' => $filepath));
53511
              $parsed[] = $filepath;
53611
              $new_files = TRUE;
53711
            }
53811
          }
53925
        }
54025
      }
54125
    }
54225
  }
543
544
  // If there are any new source files we parsed, invalidate existing
545
  // JavaScript translation files for all languages, adding the refresh
546
  // flags into the existing array.
54770
  if ($new_files) {
54811
    $parsed += locale_inc_callback('_locale_invalidate_js');
54911
  }
550
551
  // If necessary, rebuild the translation file for the current language.
55270
  if (!empty($parsed['refresh:' . $language->language])) {
553
    // Don't clear the refresh flag on failure, so that another try will
554
    // be performed later.
5551
    if (locale_inc_callback('_locale_rebuild_js')) {
5561
      unset($parsed['refresh:' . $language->language]);
5571
    }
558
    // Store any changes after refresh was attempted.
5591
    variable_set('javascript_parsed', $parsed);
5601
  }
561
  // If no refresh was attempted, but we have new source files, we need
562
  // to store them too. This occurs if current page is in English.
56370
  elseif ($new_files) {
56411
    variable_set('javascript_parsed', $parsed);
56511
  }
566
567
  // Add the translation JavaScript file to the page.
56870
  if ($files && !empty($language->javascript)) {
5690
    drupal_add_js($dir . '/' . $language->language . '_' .
$language->javascript . '.js', 'core');
5700
  }
57170
}
572
573
//
---------------------------------------------------------------------------------
574
// Language switcher block
575
576
/**
577
 * Implementation of hook_block().
578
 * Displays a language switcher. Translation links may be provided by other
modules.
579
 */
58099
function locale_block($op = 'list', $delta = '') {
5810
  if ($op == 'list') {
5820
    $block['language-switcher']['info'] = t('Language switcher');
583
    // Not worth caching.
5840
    $block['language-switcher']['cache'] = BLOCK_NO_CACHE;
5850
    return $block;
5860
  }
587
588
  // Only show if we have at least two languages and language dependent
589
  // web addresses, so we can actually link to other language versions.
5900
  elseif ($op == 'view' && variable_get('language_count', 1) > 1 &&
variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) !=
LANGUAGE_NEGOTIATION_NONE) {
5910
    $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
5920
    $languages = language_list('enabled');
5930
    $links = array();
5940
    foreach ($languages[1] as $language) {
5950
      $links[$language->language] = array(
5960
        'href'       => $path,
5970
        'title'      => $language->native,
5980
        'language'   => $language,
5990
        'attributes' => array('class' => 'language-link'),
600
      );
6010
    }
602
603
    // Allow modules to provide translations for specific links.
604
    // A translation link may need to point to a different path or use
605
    // a translated link text before going through l(), which will just
606
    // handle the path aliases.
6070
    drupal_alter('translation_link', $links, $path);
608
6090
    $block['subject'] = t('Languages');
6100
    $block['content'] = theme('links', $links, array());
6110
    return $block;
6120
  }
6130
}
61499