drupal/modules/locale/locale.module

1126 lines
44 KiB
Plaintext

<?php
// $Id$
/**
* @file
* Add language handling functionality and enables the translation of the
* user interface to languages other than English.
*
* When enabled, multiple languages can be set up. The site interface
* can be displayed in different languages, as well as nodes can have languages
* assigned. The setup of languages and translations is completely web based.
* Gettext portable object files are supported.
*/
/**
* The language is determined using a URL language indicator:
* path prefix or domain according to the configuration.
*/
define('LOCALE_LANGUAGE_NEGOTIATION_URL', 'locale-url');
/**
* The language is set based on the browser language settings.
*/
define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser');
/**
* The language is determined using the current content language.
*/
define('LOCALE_LANGUAGE_NEGOTIATION_CONTENT', 'locale-content');
/**
* The language is set based on the user language settings.
*/
define('LOCALE_LANGUAGE_NEGOTIATION_USER', 'locale-user');
/**
* The language is set based on the request/session parameters.
*/
define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
// ---------------------------------------------------------------------------------
// Hook implementations
/**
* Implements hook_help().
*/
function locale_help($path, $arg) {
switch ($path) {
case 'admin/help#locale':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$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/handbook/modules/locale/')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Translating interface text') . '</dt>';
$output .= '<dd>' . t('Translations of text in the Drupal interface may be provided by:');
$output .= '<ul>';
$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>';
$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://drupal.org/project/translations')) . '</li>';
$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>';
$output .= '</ul></dd>';
$output .= '<dt>' . t('Configuring a multilingual site') . '</dt>';
$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>';
$output .= '</dl>';
return $output;
case 'admin/config/regional/language':
$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://drupal.org/project/translations')) . '</p>';
return $output;
case 'admin/config/regional/language/add':
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>';
case 'admin/config/regional/language/configure':
$output = '<p>' . t("Set which languages to use for content and for the administrative interface. Drag the detection methods into the order they should test for languages. The first method that gets a result will set the language for the relevant part of the site. <strong>Changing these settings may break all incoming URLs, use with caution in a production environment.</strong>") . '</p>';
return $output;
case 'admin/config/regional/language/configure/url':
$output = '<p>' . t('Determine the language by examining the URL. Example: "http://example.com/de/contact" sets language to German based on the use of "de" as the path prefix. "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.') . '</p>';
return $output;
case 'admin/config/regional/language/configure/session':
$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>';
return $output;
case 'admin/config/regional/translate':
$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>';
$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>';
return $output;
case 'admin/config/regional/translate/import':
$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>';
$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://drupal.org/project/translations')) . '</p>';
return $output;
case 'admin/config/regional/translate/export':
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>';
case 'admin/config/regional/translate/translate':
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>';
case 'admin/structure/block/manage':
if ($arg[4] == 'locale' && $arg[5] == 0) {
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/config/regional/language'), '@configuration' => url('admin/config/regional/language/configure'))) . '</p>';
}
break;
}
}
/**
* Implements hook_menu().
*/
function locale_menu() {
// Manage languages
$items['admin/config/regional/language'] = array(
'title' => 'Languages',
'description' => 'Configure languages for content and the user interface.',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_languages_overview_form'),
'access arguments' => array('administer languages'),
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/language/overview'] = array(
'title' => 'List',
'weight' => 0,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/regional/language/add'] = array(
'title' => 'Add language',
'page callback' => 'locale_languages_add_screen', // two forms concatenated
'access arguments' => array('administer languages'),
'weight' => 5,
'type' => MENU_LOCAL_ACTION,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/language/configure'] = array(
'title' => 'Configure',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_languages_configure_form'),
'access arguments' => array('administer languages'),
'weight' => 10,
'file' => 'locale.inc',
'file path' => 'includes',
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/regional/language/configure/url'] = array(
'title' => 'URL language provider configuration',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_language_providers_url_form'),
'access arguments' => array('administer languages'),
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/language/configure/session'] = array(
'title' => 'Session language provider configuration',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_language_providers_session_form'),
'access arguments' => array('administer languages'),
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/language/edit/%'] = array(
'title' => 'Edit language',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_languages_edit_form', 5),
'access arguments' => array('administer languages'),
'file' => 'locale.inc',
'file path' => 'includes',
'type' => MENU_CALLBACK,
);
$items['admin/config/regional/language/delete/%'] = array(
'title' => 'Confirm',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_languages_delete_form', 5),
'access arguments' => array('administer languages'),
'file' => 'locale.inc',
'file path' => 'includes',
'type' => MENU_CALLBACK,
);
// Translation functionality
$items['admin/config/regional/translate'] = array(
'title' => 'Translate interface',
'description' => 'Translate the built in interface and optionally other text.',
'page callback' => 'locale_inc_callback',
'page arguments' => array('locale_translate_overview_screen'), // not a form, just a table
'access arguments' => array('translate interface'),
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/translate/overview'] = array(
'title' => 'Overview',
'weight' => 0,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/regional/translate/translate'] = array(
'title' => 'Translate',
'weight' => 10,
'type' => MENU_LOCAL_TASK,
'page callback' => 'locale_translate_seek_screen', // search results and form concatenated
'access arguments' => array('translate interface'),
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/translate/import'] = array(
'title' => 'Import',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_translate_import_form'),
'access arguments' => array('translate interface'),
'weight' => 20,
'type' => MENU_LOCAL_TASK,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/translate/export'] = array(
'title' => 'Export',
'page callback' => 'locale_translate_export_screen', // possibly multiple forms concatenated
'access arguments' => array('translate interface'),
'weight' => 30,
'type' => MENU_LOCAL_TASK,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/translate/edit/%'] = array(
'title' => 'Edit string',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_translate_edit_form', 5),
'access arguments' => array('translate interface'),
'type' => MENU_CALLBACK,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/translate/delete/%'] = array(
'title' => 'Delete string',
'page callback' => 'locale_translate_delete_page',
'page arguments' => array(5),
'access arguments' => array('translate interface'),
'type' => MENU_CALLBACK,
'file' => 'locale.inc',
'file path' => 'includes',
);
// Localize date formats.
$items['admin/config/regional/date-time/locale'] = array(
'title' => 'Localize',
'description' => 'Configure date formats for each locale',
'page callback' => 'locale_date_format_language_overview_page',
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
'weight' => -8,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/date-time/locale/%/edit'] = array(
'title' => 'Localize date formats',
'description' => 'Configure date formats for each locale',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_date_format_form', 5),
'access arguments' => array('administer site configuration'),
'type' => MENU_CALLBACK,
'file' => 'locale.inc',
'file path' => 'includes',
);
$items['admin/config/regional/date-time/locale/%/reset'] = array(
'title' => 'Reset date formats',
'description' => 'Reset localized date formats to global defaults',
'page callback' => 'drupal_get_form',
'page arguments' => array('locale_date_format_reset_form', 5),
'access arguments' => array('administer site configuration'),
'type' => MENU_CALLBACK,
'file' => 'locale.inc',
'file path' => 'includes',
);
return $items;
}
/**
* Implements hook_init().
*
* Initialize date formats according to the user's current locale.
*/
function locale_init() {
global $conf, $language;
include_once DRUPAL_ROOT . '/includes/locale.inc';
// For each date type (e.g. long, short), get the localized date format
// for the user's current language and override the default setting for it
// in $conf. This should happen on all pages except the date and time formats
// settings page, where we want to display the site default and not the
// localized version.
if (strpos($_GET['q'], 'admin/config/regional/date-time/formats') !== 0) {
$languages = array($language->language);
// Setup appropriate date formats for this locale.
$formats = locale_get_localized_date_format($languages);
foreach ($formats as $format_type => $format) {
$conf[$format_type] = $format;
}
}
}
/**
* Wrapper function to be able to set callbacks in locale.inc
*/
function locale_inc_callback() {
$args = func_get_args();
$function = array_shift($args);
include_once DRUPAL_ROOT . '/includes/locale.inc';
return call_user_func_array($function, $args);
}
/**
* Implements hook_permission().
*/
function locale_permission() {
return array(
'administer languages' => array(
'title' => t('Administer languages'),
),
'translate interface' => array(
'title' => t('Translate interface texts'),
),
);
}
/**
* Implements hook_locale().
*/
function locale_locale($op = 'groups') {
switch ($op) {
case 'groups':
return array('default' => t('Built-in interface'));
}
}
/**
* Form builder callback to display language selection widget.
*
* @ingroup forms
* @see locale_form_alter()
*/
function locale_language_selector_form(&$form, &$form_state, $user) {
global $language;
$languages = language_list('enabled');
$languages = $languages[1];
// If the user is being created, we set the user language to the page language.
$user_preferred_language = $user->uid ? user_preferred_language($user) : $language;
$names = array();
foreach ($languages as $langcode => $item) {
$name = t($item->name);
$names[$langcode] = $name . ($item->native != $name ? ' (' . $item->native . ')' : '');
}
$form['locale'] = array(
'#type' => 'fieldset',
'#title' => t('Language settings'),
'#weight' => 1,
);
// Get language negotiation settings.
$mode = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
$form['locale']['language'] = array(
'#type' => (count($names) <= 5 ? 'radios' : 'select'),
'#title' => t('Language'),
'#default_value' => $user_preferred_language->language,
'#options' => $names,
'#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."),
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function locale_form_path_admin_form_alter(&$form, &$form_state) {
$form['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#options' => array('' => t('All languages')) + locale_language_list('name'),
'#default_value' => $form['language']['#value'],
'#weight' => -10,
'#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>.'),
);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function locale_form_node_type_form_alter(&$form, &$form_state) {
if (isset($form['type'])) {
$form['workflow']['language_content_type'] = array(
'#type' => 'radios',
'#title' => t('Multilingual support'),
'#default_value' => variable_get('language_content_type_' . $form['#node_type']->type, 0),
'#options' => array(t('Disabled'), t('Enabled')),
'#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'))),
);
}
}
/**
* Return whether the given content type has multilingual support.
*
* @return
* True if multilingual support is enabled.
*/
function locale_multilingual_node_type($type_name) {
return (bool) variable_get('language_content_type_' . $type_name, 0);
}
/**
* Implements hook_form_alter().
*
* Adds language fields to forms.
*/
function locale_form_alter(&$form, &$form_state, $form_id) {
// Only alter user forms if there is more than one language.
if (drupal_multilingual()) {
// Display language selector when either creating a user on the admin
// interface or editing a user account.
if (($form_id == 'user_register_form' && user_access('administer users')) || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) {
locale_language_selector_form($form, $form_state, $form['#user']);
}
}
if (isset($form['#id']) && $form['#id'] == 'node-form') {
if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
$form['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
'#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
);
}
// Node type without language selector: assign the default for new nodes
elseif (!isset($form['#node']->nid)) {
$default = language_default();
$form['language'] = array(
'#type' => 'value',
'#value' => $default->language
);
}
$form['#submit'][] = 'locale_field_node_form_submit';
}
}
/**
* Form submit handler for node_form().
*
* Check if Locale is registered as a translation handler and handle possible
* node language changes.
*/
function locale_field_node_form_submit($form, &$form_state) {
if (field_multilingual_check_translation_handlers('node', 'locale')) {
module_load_include('inc', 'locale', 'locale.field');
locale_field_node_form_update_field_language($form, $form_state);
}
}
/**
* Implements hook_theme().
*/
function locale_theme() {
return array(
'locale_languages_overview_form' => array(
'render element' => 'form',
),
'locale_languages_configure_form' => array(
'render element' => 'form',
),
'locale_translation_filters' => array(
'render element' => 'form',
),
'locale_date_format_form' => array(
'render element' => 'form',
),
);
}
/**
* Implements hook_field_attach_view_alter().
*/
function locale_field_attach_view_alter(&$output, $context) {
// In locale_field_fallback_view() we might call field_attach_view(). The
// static variable avoids unnecessary recursion.
static $recursion;
// Do not apply fallback rules if disabled or if Locale is not registered as a
// translation handler.
if (!$recursion && variable_get('locale_field_fallback_view', TRUE) && field_multilingual_check_translation_handlers($context['obj_type'], 'locale')) {
$recursion = TRUE;
module_load_include('inc', 'locale', 'locale.field');
locale_field_fallback_view($output, $context);
$recursion = FALSE;
}
}
/**
* Implements hook_entity_info_alter().
*/
function locale_entity_info_alter(&$entity_info) {
$enabled = drupal_multilingual();
foreach ($entity_info as $type => $info) {
$entity_info[$type]['translation']['locale'] = $enabled;
}
}
/**
* Implements hook_language_types_info().
*/
function locale_language_types_info() {
return array(
LANGUAGE_TYPE_CONTENT => array(
'name' => t('Content'),
'description' => t('If a piece of content is available in multiple languages, the one matching the <em>content</em> language will be used.'),
),
LANGUAGE_TYPE_INTERFACE => array(
'name' => t('Interface'),
'description' => t('The interface labels will be displayed in the <em>interface</em> language.'),
),
LANGUAGE_TYPE_URL => array(
'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL),
),
);
}
/**
* Implements hook_language_negotiation_info().
*/
function locale_language_negotiation_info() {
$file = 'includes/locale.inc';
$providers = array();
$providers[LOCALE_LANGUAGE_NEGOTIATION_URL] = array(
'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL),
'callbacks' => array(
'language' => 'locale_language_from_url',
'switcher' => 'locale_language_switcher_url',
'url_rewrite' => 'locale_language_url_rewrite_url',
),
'file' => $file,
'weight' => -8,
'name' => t('URL'),
'description' => t('Determine the language from the URL (Path prefix or domain).'),
'config' => 'admin/config/regional/language/configure/url',
);
$providers[LOCALE_LANGUAGE_NEGOTIATION_SESSION] = array(
'callbacks' => array(
'language' => 'locale_language_from_session',
'switcher' => 'locale_language_switcher_session',
'url_rewrite' => 'locale_language_url_rewrite_session',
),
'file' => $file,
'weight' => -6,
'name' => t('Session'),
'description' => t('The language is determined from a request/session parameter.'),
'config' => 'admin/config/regional/language/configure/session',
);
$providers[LOCALE_LANGUAGE_NEGOTIATION_USER] = array(
'callbacks' => array('language' => 'locale_language_from_user'),
'file' => $file,
'weight' => -4,
'name' => t('User'),
'description' => t("Show in this user's language preference."),
);
$providers[LOCALE_LANGUAGE_NEGOTIATION_BROWSER] = array(
'callbacks' => array('language' => 'locale_language_from_browser'),
'name' => $file,
'weight' => -2,
'cache' => CACHE_DISABLED,
'name' => t('Browser'),
'description' => t('The language is determined from the browser\'s language settings.'),
);
$providers[LOCALE_LANGUAGE_NEGOTIATION_CONTENT] = array(
'types' => array(LANGUAGE_TYPE_INTERFACE),
'callbacks' => array('language' => 'locale_language_from_content'),
'file' => $file,
'weight' => 8,
'name' => t('Content'),
'description' => t('The interface language is the same as the negotiated content language.'),
);
return $providers;
}
// ---------------------------------------------------------------------------------
// Locale core functionality
/**
* Provides interface translation services.
*
* This function is called from t() to translate a string if needed.
*
* @param $string
* A string to look up translation for. If omitted, all the
* cached strings will be returned in all languages already
* used on the page.
* @param $context
* The context of this string.
* @param $langcode
* Language code to use for the lookup.
* @param $reset
* Set to TRUE to reset the in-memory cache.
*/
function locale($string = NULL, $context = NULL, $langcode = NULL, $reset = FALSE) {
global $language;
static $locale_t;
if ($reset) {
// Reset in-memory cache.
$locale_t = NULL;
}
if (!isset($string)) {
// Return all cached strings if no string was specified
return $locale_t;
}
$langcode = isset($langcode) ? $langcode : $language->language;
// Store database cached translations in a static var.
if (!isset($locale_t[$langcode])) {
$locale_t[$langcode] = array();
// Disabling the usage of string caching allows a module to watch for
// the exact list of strings used on a page. From a performance
// perspective that is a really bad idea, so we have no user
// interface for this. Be careful when turning this option off!
if (variable_get('locale_cache_strings', 1) == 1) {
if ($cache = cache_get('locale:' . $langcode, 'cache')) {
$locale_t[$langcode] = $cache->data;
}
elseif (lock_acquire('locale_cache_' . $langcode)) {
// Refresh database stored cache of translations for given language.
// We only store short strings used in current version, to improve
// performance and consume less memory.
$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) < 75", array(':language' => $langcode, ':version' => VERSION));
foreach ($result as $data) {
$locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation);
}
cache_set('locale:' . $langcode, $locale_t[$langcode]);
lock_release('locale_cache_' . $langcode);
}
}
}
// If we have the translation cached, skip checking the database
if (!isset($locale_t[$langcode][$context][$string])) {
// We do not have this translation cached, so get it from the DB.
$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(
':language' => $langcode,
':source' => $string,
':context' => (string) $context,
))->fetchObject();
if ($translation) {
// We have the source string at least.
// Cache translation string or TRUE if no translation exists.
$locale_t[$langcode][$context][$string] = (empty($translation->translation) ? TRUE : $translation->translation);
if ($translation->version != VERSION) {
// This is the first use of this string under current Drupal version. Save version
// and clear cache, to include the string into caching next time. Saved version is
// also a string-history information for later pruning of the tables.
db_update('locales_source')
->fields(array('version' => VERSION))
->condition('lid', $translation->lid)
->execute();
cache_clear_all('locale:', 'cache', TRUE);
}
}
else {
// We don't have the source string, cache this as untranslated.
db_insert('locales_source')
->fields(array(
'location' => request_uri(),
'source' => $string,
'context' => (string) $context,
'textgroup' => 'default',
'version' => VERSION,
))
->execute();
$locale_t[$langcode][$context][$string] = TRUE;
// Clear locale cache so this string can be added in a later request.
cache_clear_all('locale:', 'cache', TRUE);
}
}
return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]);
}
/**
* Returns plural form index for a specific number.
*
* The index is computed from the formula of this language.
*
* @param $count
* Number to return plural for.
* @param $langcode
* Optional language code to translate to a language other than
* what is used to display the page.
*/
function locale_get_plural($count, $langcode = NULL) {
global $language;
static $locale_formula, $plurals = array();
$langcode = $langcode ? $langcode : $language->language;
if (!isset($plurals[$langcode][$count])) {
if (!isset($locale_formula)) {
$language_list = language_list();
$locale_formula[$langcode] = $language_list[$langcode]->formula;
}
if ($locale_formula[$langcode]) {
$n = $count;
$plurals[$langcode][$count] = @eval('return intval(' . $locale_formula[$langcode] . ');');
return $plurals[$langcode][$count];
}
else {
$plurals[$langcode][$count] = -1;
return -1;
}
}
return $plurals[$langcode][$count];
}
/**
* Returns a language name
*/
function locale_language_name($lang) {
static $list = NULL;
if (!isset($list)) {
$list = locale_language_list();
}
return ($lang && isset($list[$lang])) ? $list[$lang] : t('All');
}
/**
* Returns array of language names
*
* @param $field
* 'name' => names in current language, localized
* 'native' => native names
* @param $all
* Boolean to return all languages or only enabled ones
*/
function locale_language_list($field = 'name', $all = FALSE) {
if ($all) {
$languages = language_list();
}
else {
$languages = language_list('enabled');
$languages = $languages[1];
}
$list = array();
foreach ($languages as $language) {
$list[$language->language] = ($field == 'name') ? t($language->name) : $language->$field;
}
return $list;
}
/**
* Imports translations when new modules or themes are installed or enabled.
*
* This function will either import translation for the component change
* right away, or start a batch if more files need to be imported.
*
* @param $components
* An array of component (theme and/or module) names to import
* translations for.
*/
function locale_system_update($components) {
include_once DRUPAL_ROOT . '/includes/locale.inc';
if ($batch = locale_batch_by_component($components)) {
batch_set($batch);
}
}
/**
* Implements hook_js_alter().
*
* This function checks all JavaScript files currently added via drupal_add_js()
* and invokes parsing if they have not yet been parsed for Drupal.t()
* and Drupal.formatPlural() calls. Also refreshes the JavaScript translation
* file if necessary, and adds it to the page.
*/
function locale_js_alter(&$javascript) {
global $language;
$dir = 'public://' . variable_get('locale_js_directory', 'languages');
$parsed = variable_get('javascript_parsed', array());
$files = $new_files = FALSE;
foreach ($javascript as $item) {
if ($item['type'] == 'file') {
$files = TRUE;
$filepath = $item['data'];
if (!in_array($filepath, $parsed)) {
// Don't parse our own translations files.
if (substr($filepath, 0, strlen($dir)) != $dir) {
locale_inc_callback('_locale_parse_js_file', $filepath);
watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath));
$parsed[] = $filepath;
$new_files = TRUE;
}
}
}
}
// If there are any new source files we parsed, invalidate existing
// JavaScript translation files for all languages, adding the refresh
// flags into the existing array.
if ($new_files) {
$parsed += locale_inc_callback('_locale_invalidate_js');
}
// If necessary, rebuild the translation file for the current language.
if (!empty($parsed['refresh:' . $language->language])) {
// Don't clear the refresh flag on failure, so that another try will
// be performed later.
if (locale_inc_callback('_locale_rebuild_js')) {
unset($parsed['refresh:' . $language->language]);
}
// Store any changes after refresh was attempted.
variable_set('javascript_parsed', $parsed);
}
// If no refresh was attempted, but we have new source files, we need
// to store them too. This occurs if current page is in English.
elseif ($new_files) {
variable_set('javascript_parsed', $parsed);
}
// Add the translation JavaScript file to the page.
if ($files && !empty($language->javascript)) {
// Add the translation JavaScript file to the page.
$file = $dir . '/' . $language->language . '_' . $language->javascript . '.js';
$javascript[$file] = drupal_js_defaults($file);
}
}
/*
* Implements hook_css_alter().
*
* This function checks all CSS files currently added via drupal_add_css() and
* and checks to see if a related right to left CSS file should be included.
*/
function locale_css_alter(&$css) {
global $language;
// If the current language is RTL, add the CSS file with the RTL overrides.
if ($language->direction == LANGUAGE_RTL) {
foreach ($css as $data => $item) {
// Only provide RTL overrides for files.
if ($item['type'] == 'file') {
$rtl_path = str_replace('.css', '-rtl.css', $item['data']);
if (file_exists($rtl_path) && !isset($css[$rtl_path])) {
// Replicate the same item, but with the RTL path and a little larger
// weight so that it appears directly after the original CSS file.
$item['data'] = $rtl_path;
$item['weight'] += 0.01;
$css[$rtl_path] = $item;
}
}
}
}
}
// ---------------------------------------------------------------------------------
// Language switcher block
/**
* Implements hook_block_info().
*/
function locale_block_info() {
include_once DRUPAL_ROOT . '/includes/language.inc';
$block = array();
$info = language_types_info();
foreach (language_types_configurable() as $type) {
$block[$type] = array(
'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])),
// Not worth caching.
'cache' => DRUPAL_NO_CACHE,
);
}
return $block;
}
/**
* Implements hook_block_view().
*
* Displays a language switcher. Only show if we have at least two languages.
*/
function locale_block_view($type) {
if (drupal_multilingual()) {
$path = drupal_is_front_page() ? '<front>' : $_GET['q'];
$links = language_negotiation_get_switch_links($type, $path);
if (isset($links->links) && count($links->links > 1)) {
$class = "language-switcher-{$links->provider}";
$variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
$block['content'] = theme('links', $variables);
$block['subject'] = t('Languages');
return $block;
}
}
}
/**
* Theme locale translation filter selector.
*
* @ingroup themeable
*/
function theme_locale_translation_filters($variables) {
$form = $variables['form'];
$output = '';
foreach (element_children($form['status']) as $key) {
$output .= drupal_render($form['status'][$key]);
}
$output .= '<div id="locale-translation-buttons">' . drupal_render($form['buttons']) . '</div>';
return $output;
}
/**
* Theme locale date format form.
*
* @ingroup themeable
*/
function theme_locale_date_format_form($variables) {
$form = $variables['form'];
$header = array(
t('Date type'),
t('Format'),
);
foreach (element_children($form['date_formats']) as $key) {
$row = array();
$row[] = $form['date_formats'][$key]['#title'];
unset($form['date_formats'][$key]['#title']);
$row[] = array('data' => drupal_render($form['date_formats'][$key]));
$rows[] = $row;
}
$output = drupal_render($form['language']);
$output .= theme('table', array('header' => $header, 'rows' => $rows));
$output .= drupal_render_children($form);
return $output;
}
/**
* Display edit date format links for each language.
*/
function locale_date_format_language_overview_page() {
$header = array(
t('Language'),
array('data' => t('Operations'), 'colspan' => '2'),
);
// Get list of languages.
$languages = locale_language_list('native');
foreach ($languages as $langcode => $info) {
$row = array();
$row[] = $languages[$langcode];
$row[] = l(t('edit'), 'admin/config/regional/date-time/locale/' . $langcode . '/edit');
$row[] = l(t('reset'), 'admin/config/regional/date-time/locale/' . $langcode . '/reset');
$rows[] = $row;
}
return theme('table', array('header' => $header, 'rows' => $rows));
}
/**
* Provide date localization configuration options to users.
*/
function locale_date_format_form($form, &$form_state, $langcode) {
$languages = locale_language_list('native');
$language_name = $languages[$langcode];
// Display the current language name.
$form['language'] = array(
'#type' => 'item',
'#title' => t('Language'),
'#markup' => check_plain($language_name),
'#weight' => -10,
);
$form['langcode'] = array(
'#type' => 'value',
'#value' => $langcode,
);
// Get list of date format types.
$types = system_get_date_types();
// Get list of available formats.
$formats = system_get_date_formats();
$choices = array();
foreach ($formats as $type => $list) {
foreach ($list as $f => $format) {
$choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
}
}
// Get configured formats for each language.
$locale_formats = system_date_format_locale($langcode);
// Display a form field for each format type.
foreach ($types as $type => $type_info) {
if (!empty($locale_formats) && in_array($type, array_keys($locale_formats))) {
$default = $locale_formats[$type];
}
else {
$default = variable_get('date_format_' . $type, array_shift(array_keys($formats)));
}
// Show date format select list.
$form['date_formats']['date_format_' . $type] = array(
'#type' => 'select',
'#title' => check_plain($type_info['title']),
'#attributes' => array('class' => array('date-format')),
'#default_value' => (isset($choices[$default]) ? $default : 'custom'),
'#options' => $choices,
);
}
$form['buttons']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Submit handler for configuring localized date formats on the locale_date_format_form.
*/
function locale_date_format_form_submit($form, &$form_state) {
include_once DRUPAL_ROOT . '/includes/locale.inc';
$langcode = $form_state['values']['langcode'];
// Get list of date format types.
$types = system_get_date_types();
foreach ($types as $type => $type_info) {
$format = $form_state['values']['date_format_' . $type];
if ($format == 'custom') {
$format = $form_state['values']['date_format_' . $type . '_custom'];
}
locale_date_format_save($langcode, $type, $format);
}
drupal_set_message(t('Configuration saved.'));
$form_state['redirect'] = 'admin/config/regional/date-time/locale';
}
/**
* Reset locale specific date formats to the global defaults.
*
* @param $langcode
* Language code, e.g. 'en'.
*/
function locale_date_format_reset_form($form, &$form_state, $langcode) {
$form['langcode'] = array('#type' => 'value', '#value' => $langcode);
$languages = language_list();
return confirm_form($form,
t('Are you sure you want to reset the date formats for %language to the global defaults?', array('%language' => $languages[$langcode]->name)),
'admin/config/regional/date-time/locale',
t('Resetting will remove all localized date formats for this language. This action cannot be undone.'),
t('Reset'), t('Cancel'));
}
/**
* Reset date formats for a specific language to global defaults.
*/
function locale_date_format_reset_form_submit($form, &$form_state) {
db_delete('date_format_locale')
->condition('language', $form_state['values']['langcode'])
->execute();
$form_state['redirect'] = 'admin/config/regional/date-time/locale';
}
/**
* Implements hook_url_outbound_alter().
*
* Rewrite outbound URLs with language based prefixes.
*/
function locale_url_outbound_alter(&$path, &$options, $original_path) {
// Only modify internal URLs.
if (!$options['external']) {
static $callbacks;
if (!isset($callbacks)) {
$callbacks = array();
include_once DRUPAL_ROOT . '/includes/language.inc';
foreach (language_types_configurable() as $type) {
// Get url rewriter callbacks only from enabled language providers.
$negotiation = variable_get("language_negotiation_$type", array());
foreach ($negotiation as $id => $provider) {
if (isset($provider['file'])) {
require_once DRUPAL_ROOT . '/' . $provider['file'];
}
// Avoid duplicate callback entries.
if (isset($provider['callbacks']['url_rewrite'])) {
$callbacks[$provider['callbacks']['url_rewrite']] = NULL;
}
}
}
$callbacks = array_keys($callbacks);
}
foreach ($callbacks as $callback) {
$callback($path, $options);
}
}
}
/*
* Implements hook_form_FORM_ID_alter().
*/
function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
// If a content type has multilingual support we set the content language as
// comment language.
if (empty($form['language']['#value']) && locale_multilingual_node_type($form['#node']->type)) {
global $language;
$form['language']['#value'] = $language->language;
}
}