Skip to content
locale.module 18.1 KiB
Newer Older
Dries Buytaert's avatar
 
Dries Buytaert committed
<?php
Dries Buytaert's avatar
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * @file
 * Enables administrators to manage the site interface languages.
 *
 * When enabled, the site interface can be displayed in different
 * languages. The setup of languages and translations is completely
 * web based. Gettext portable object files are supported.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */

// ---------------------------------------------------------------------------------
// Hook implementations (needed on all page loads)
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Implementation of hook_help().
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
  switch ($section) {
      $output = '<p>'. t('The locale module allows you to present your Drupal site in a language other than the default English. You can use it to set up a multi-lingual web site or replace given <em>built-in</em> text with text which has been customized for your site. Whenever the locale module encounters text which needs to be displayed, it tries to translate it into the currently selected language. If a translation is not available, then the string is remembered, so you can look up untranslated strings easily.') .'</p>';
      $output .= '<p>'. t('The locale module provides two options for providing translations. The first is the integrated web interface, via which you can search for untranslated strings, and specify their translations. An easier and less time-consuming method is to import existing translations for your language. These translations are available as <em>GNU gettext Portable Object files</em> (<em>.po</em> files for short). Translations for many languages are available for download from the translation page.') .'</p>';
      $output .= '<p>'. t("If an existing translation does not meet your needs, the <em>.po</em> files are easily edited with special editing tools. The locale module's import feature allows you to add strings from such files into your site's database. The export functionality enables you to share your translations with others, generating Portable Object files from your site strings.") .'</p>';
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@locale">Locale page</a>.', array('@locale' => 'http://drupal.org/handbook/modules/locale/')) .'</p>';
    case 'admin/settings/locale':
    case 'admin/settings/locale/language/overview':
      return t("<p>Drupal provides support for the translation of its interface text into different languages. This page provides an overview of the installed languages. You can add a language on the <a href=\"@add-language\">add language page</a>, or directly by <a href=\"@import\">importing a translation</a>. If multiple languages are enabled, registered users will be able to set their preferred language. The site default will be used for anonymous visitors and for users without their own settings.</p><p>Drupal interface translations may be added or extended by several courses: by <a href=\"@import\">importing</a> an existing translation, by <a href=\"@search\">translating everything</a> from scratch, or by a combination of these approaches.</p>", array("@search" => url("admin/settings/locale/string/search"), "@import" => url("admin/settings/locale/language/import"), "@add-language" => url("admin/settings/locale/language/add")));
    case 'admin/settings/locale/language/add':
      return '<p>'. t("You need to add all languages in which you would like to display the site interface. If you can't find the desired language in the quick-add dropdown, then you will need to provide the proper language code yourself. The language code may be used to negotiate with browsers and to present flags, etc., so it is important to pick a code that is standardised for the desired language. You can also add a language by <a href=\"@import\">importing a translation</a>.", array("@import" => url("admin/settings/locale/language/import"))) .'</p>';
    case 'admin/settings/locale/language/import':
      return '<p>'. t("This page allows you to import a translation provided in the gettext Portable Object (.po) format. The easiest way to get your site translated is to obtain an existing Drupal translation and to import it. You can find existing translations on the <a href=\"@url\">Drupal translation page</a>. Note that importing a translation file might take a while.", array('@url' => 'http://drupal.org/project/translations')) .'</p>';
    case 'admin/settings/locale/language/export':
      return '<p>'. t("This page allows you to export Drupal strings. The first option is to export a translation so it can be shared. The second option generates a translation template, which contains all Drupal strings, but without their translations. You can use this template to start a new translation using various software packages designed for this task.") .'</p>';
    case 'admin/settings/locale/string/search':
      return '<p>'. t("It is often convenient to get the strings from your setup on the <a href=\"@export\">export page</a>, and use a desktop Gettext translation editor to edit the translations. On this page you can search in the translated and untranslated strings, and the default English texts provided by Drupal.", array("@export" => url("admin/settings/locale/language/export"))) .'</p>';
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 * Implementation of hook_menu().
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function locale_menu($may_cache) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  $items = array();
  $access = user_access('administer locales');
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  if ($may_cache) {
    // Main admin menu item
    $items[] = array('path' => 'admin/settings/locale',
      'description' => t('Configure site localization and user interface translation.'),
      'callback' => 'locale_admin_manage',
      'access' => $access);
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
    // Top level tabs
    $items[] = array('path' => 'admin/settings/locale/language',
      'access' => $access,
      'weight' => -10,
      'type' => MENU_DEFAULT_LOCAL_TASK);
    $items[] = array('path' => 'admin/settings/locale/string/search',
      'callback' => 'locale_string_search',
      'access' => $access,
      'weight' => 10,
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_LOCAL_TASK);
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
    // Manage languages subtabs
    $items[] = array('path' => 'admin/settings/locale/language/overview',
      'callback' => 'locale_admin_manage',
      'access' => $access,
      'weight' => 0,
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_DEFAULT_LOCAL_TASK);
    $items[] = array('path' => 'admin/settings/locale/language/add',
      'callback' => 'locale_admin_manage_add',
      'access' => $access,
      'weight' => 5,
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_LOCAL_TASK);
    $items[] = array('path' => 'admin/settings/locale/language/import',
      'callback' => 'locale_admin_import',
      'access' => $access,
      'weight' => 10,
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_LOCAL_TASK);
    $items[] = array('path' => 'admin/settings/locale/language/export',
      'callback' => 'locale_admin_export',
      'access' => $access,
      'weight' => 20,
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_LOCAL_TASK);
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
    // Language related callbacks
    $items[] = array('path' => 'admin/settings/locale/language/delete',
      'callback' => 'drupal_get_form',
      'callback arguments' => array('locale_admin_manage_delete_form'),
Dries Buytaert's avatar
 
Dries Buytaert committed
      'type' => MENU_CALLBACK);
      $items[] = array('path' => 'admin/settings/locale/string/edit/'. arg(5),
        'callback' => 'drupal_get_form',
        'callback arguments' => array('locale_admin_string_edit', arg(5)),
        'access' => $access,
        'type' => MENU_CALLBACK);
      $items[] = array('path' => 'admin/settings/locale/string/delete/'. arg(5),
Neil Drumm's avatar
Neil Drumm committed
        'callback' => 'locale_admin_string_delete_page',
        'callback arguments' => array(arg(5)),
        'access' => $access,
        'type' => MENU_CALLBACK);
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  return $items;
Dries Buytaert's avatar
 
Dries Buytaert committed
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
Dries Buytaert's avatar
 
Dries Buytaert committed
 * Implementation of hook_perm().
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function locale_perm() {
  return array('administer locales');
}

/**
 * Implementation of hook_user().
 */
function locale_user($type, $edit, &$user, $category = NULL) {
  $languages = locale_supported_languages();
  if ($type == 'form' && $category == 'account' && count($languages['name']) > 1) {
    if ($user->language == '') {
      $user->language = key($languages['name']);
    }
Neil Drumm's avatar
Neil Drumm committed
    foreach (array_map('t', $languages['name']) as $key => $value) {
      $languages_name[check_plain($key)] = check_plain($value);
    }
    $form['locale'] = array('#type' => 'fieldset',
      '#title' => t('Interface language settings'),
      '#weight' => 1,
    );
    $form['locale']['language'] = array('#type' => 'radios',
      '#title' => t('Language'),
Neil Drumm's avatar
Neil Drumm committed
      '#default_value' => check_plain($user->language),
      '#options' => $languages_name,
      '#description' => t('Selecting a different locale will change the interface language of the site.'),
    );
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

Dries Buytaert's avatar
 
Dries Buytaert committed
// ---------------------------------------------------------------------------------
// Locale core functionality (needed on all page loads)
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Provides interface translation services.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * This function is called from t() to translate a string if needed.
 */
function locale($string) {
  global $locale;
  static $locale_t;
Dries Buytaert's avatar
 
Dries Buytaert committed

  // Store database cached translations in a static var.
Dries Buytaert's avatar
 
Dries Buytaert committed
  if (!isset($locale_t)) {
    $cache = cache_get("locale:$locale", 'cache');
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
      locale_refresh_cache();
      $cache = cache_get("locale:$locale", 'cache');
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
    $locale_t = unserialize($cache->data);
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

Dries Buytaert's avatar
 
Dries Buytaert committed
  // We have the translation cached (if it is TRUE, then there is no
  // translation, so there is no point in checking the database)
  if (isset($locale_t[$string])) {
    $string = ($locale_t[$string] === TRUE ? $string : $locale_t[$string]);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  // We do not have this translation cached, so get it from the DB.
Dries Buytaert's avatar
 
Dries Buytaert committed
  else {
    $result = db_query("SELECT s.lid, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.source = '%s' AND t.locale = '%s'", $string, $locale);
    // Translation found
    if ($trans = db_fetch_object($result)) {
      if (!empty($trans->translation)) {
        $locale_t[$string] = $trans->translation;
        $string = $trans->translation;
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
    // Either we have no such source string, or no translation
    else {
      $result = db_query("SELECT lid, source FROM {locales_source} WHERE source = '%s'", $string);
      // We have no such translation
      if ($obj = db_fetch_object($result)) {
        if ($locale) {
          db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '')", $obj->lid, $locale);
Dries Buytaert's avatar
 
Dries Buytaert committed
        }
      }
      // We have no such source string
Dries Buytaert's avatar
 
Dries Buytaert committed
        db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", request_uri(), $string);
        if ($locale) {
          $lid = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $string));
          db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '')", $lid->lid, $locale);
Dries Buytaert's avatar
 
Dries Buytaert committed
        }
Dries Buytaert's avatar
 
Dries Buytaert committed
      // Clear locale cache in DB
      cache_clear_all("locale:$locale", 'cache');
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

Dries Buytaert's avatar
 
Dries Buytaert committed
  return $string;
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Refreshes database stored cache of translations.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * We only store short strings to improve performance and consume less memory.
 */
function locale_refresh_cache() {
  $languages = locale_supported_languages();
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  foreach (array_keys($languages['name']) as $locale) {
    $result = db_query("SELECT s.source, t.translation, t.locale FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.locale = '%s' AND LENGTH(s.source) < 75", $locale);
Dries Buytaert's avatar
 
Dries Buytaert committed
    while ($data = db_fetch_object($result)) {
      $t[$data->source] = (empty($data->translation) ? TRUE : $data->translation);
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
    cache_set("locale:$locale", 'cache', serialize($t));
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Returns list of languages supported on this site.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * @param $reset Refresh cached language list.
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @param $getall Return all languages (even disabled ones)
 */
function locale_supported_languages($reset = FALSE, $getall = FALSE) {
  static $enabled = NULL;
  static $all = NULL;
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  if ($reset) {
    unset($enabled); unset($all);
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

Dries Buytaert's avatar
 
Dries Buytaert committed
  if (is_null($enabled)) {
    $enabled = $all = array();
    $all['name'] = $all['formula'] = $enabled['name'] = $enabled['formula'] = array();
    $result = db_query('SELECT locale, name, formula, enabled FROM {locales_meta} ORDER BY isdefault DESC, enabled DESC, name ASC');
    while ($row = db_fetch_object($result)) {
      $all['name'][$row->locale] = $row->name;
Dries Buytaert's avatar
 
Dries Buytaert committed
      $all['formula'][$row->locale] = $row->formula;
Dries Buytaert's avatar
 
Dries Buytaert committed
      if ($row->enabled) {
        $enabled['name'][$row->locale] = $row->name;
        $enabled['formula'][$row->locale] = $row->formula;
Kjartan Mannes's avatar
Kjartan Mannes committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
  return $getall ? $all : $enabled;
}

/**
 * Returns plural form index for a specific number.
Dries Buytaert's avatar
 
Dries Buytaert committed
 *
 * The index is computed from the formula of this language.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function locale_get_plural($count) {
  global $locale;
  static $locale_formula, $plurals = array();

  if (!isset($plurals[$count])) {
    if (!isset($locale_formula)) {
      $languages = locale_supported_languages();
      $locale_formula = $languages['formula'][$locale];
    }
    if ($locale_formula) {
      $n = $count;
      $plurals[$count] = @eval("return intval($locale_formula);");
      return $plurals[$count];
    }
    else {
      $plurals[$count] = -1;
      return -1;
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
  return $plurals[$count];
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
// ---------------------------------------------------------------------------------
// Language management functionality (administration only)
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Page handler for the language management screen.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function locale_admin_manage() {
  include_once './includes/locale.inc';
  return drupal_get_form('_locale_admin_manage_screen');
Dries Buytaert's avatar
 
Dries Buytaert committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * User interface for the language deletion confirmation screen.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function locale_admin_manage_delete_form($langcode) {
  include_once './includes/locale.inc';
  // Do not allow deletion of English locale.
  if ($langcode == 'en') {
    drupal_set_message(t('The English locale cannot be deleted.'));
    drupal_goto('admin/settings/locale/language/overview');
Dries Buytaert's avatar
 
Dries Buytaert committed

  // For other locales, warn user that data loss is ahead.
  $languages = locale_supported_languages(FALSE, TRUE);

  if (!isset($languages['name'][$langcode])) {
    drupal_not_found();
  }
  else {
    $form['langcode'] = array('#type' => 'value', '#value' => $langcode);
    return confirm_form($form, t('Are you sure you want to delete the language %name?', array('%name' => t($languages['name'][$langcode]))), 'admin/settings/locale/language/overview', t('Deleting a language will remove all data associated with it. This action cannot be undone.'), t('Delete'), t('Cancel'));
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

/**
 * Process language deletion submissions.
 */
function locale_admin_manage_delete_form_submit($form_id, $form_values) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  $languages = locale_supported_languages(FALSE, TRUE);
  if (isset($languages['name'][$form_values['langcode']])) {
    db_query("DELETE FROM {locales_meta} WHERE locale = '%s'", $form_values['langcode']);
    db_query("DELETE FROM {locales_target} WHERE locale = '%s'", $form_values['langcode']);
    $message = t('The language %locale has been removed.', array('%locale' => t($languages['name'][$form_values['langcode']])));
    drupal_set_message($message);
    watchdog('locale', $message);
  }

  // Changing the locale settings impacts the interface:
  cache_clear_all('*', 'cache_menu', TRUE);
  cache_clear_all('*', 'cache_page', TRUE);
  return 'admin/settings/locale/language/overview';
Dries Buytaert's avatar
 
Dries Buytaert committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Page handler for the language addition screen
 */
function locale_admin_manage_add() {
  include_once './includes/locale.inc';
Dries Buytaert's avatar
 
Dries Buytaert committed
  return _locale_admin_manage_add_screen();
Dries Buytaert's avatar
 
Dries Buytaert committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed
// ---------------------------------------------------------------------------------
// Gettext Portable Object import functionality (administration only)
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Page handler for the translation import screen
 */
function locale_admin_import() {
  include_once './includes/locale.inc';
  return drupal_get_form('_locale_admin_import');
Dries Buytaert's avatar
 
Dries Buytaert committed
}
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
// ---------------------------------------------------------------------------------
// Gettext Portable Object export functionality (administration only)
Kjartan Mannes's avatar
Kjartan Mannes committed

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Page handler for the translation export screen
 */
function locale_admin_export() {
  include_once './includes/locale.inc';
Dries Buytaert's avatar
 
Dries Buytaert committed
  return _locale_admin_export_screen();
Kjartan Mannes's avatar
Kjartan Mannes committed
}

Dries Buytaert's avatar
 
Dries Buytaert committed
// ---------------------------------------------------------------------------------
// String search and editing functionality (administration only)
Dries Buytaert's avatar
 
Dries Buytaert committed

/**
 * Page handler for the string search.
Dries Buytaert's avatar
 
Dries Buytaert committed
 */
function locale_string_search() {
  include_once './includes/locale.inc';
  $output = _locale_string_seek();
  $output .= drupal_get_form('_locale_string_seek_form');
Dries Buytaert's avatar
 
Dries Buytaert committed
  return $output;
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Display the string edit form.
 */
function locale_admin_string_edit($lid) {
  include_once './includes/locale.inc';
  return _locale_string_edit($lid);
}
/**
 * Process the string edit form.
 */
function locale_admin_string_edit_submit($form_id, $form_values) {
  include_once './includes/locale.inc';
  return _locale_string_edit_submit($form_id, $form_values);
}

Neil Drumm's avatar
Neil Drumm committed
 * String deletion confirmation page.
Neil Drumm's avatar
Neil Drumm committed
function locale_admin_string_delete_page($lid) {
  if ($source = db_fetch_object(db_query('SELECT * FROM {locales_source} WHERE lid = %d', $lid))) {
    return drupal_get_form('locale_string_delete_form', $source);
  }
  else {
    return drupal_not_found();
  }
}

/**
 * User interface for the string deletion confirmation screen.
 */
function locale_string_delete_form($source) {
  $form['lid'] = array('#type' => 'value', '#value' => $source->lid);
  return confirm_form($form, t('Are you sure you want to delete the string "%source"?', array('%source' => $source->source)), 'admin/build/translate/search', t('Deleting the string will remove all translations of this string in all languages. This action cannot be undone.'), t('Delete'), t('Cancel'));
}

/**
 * Process string deletion submissions.
 */
function locale_string_delete_form_submit($form_id, $form_values) {
  include_once './includes/locale.inc';
Neil Drumm's avatar
Neil Drumm committed
  _locale_string_delete($form_values['lid']);