1126 lines
44 KiB
Plaintext
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;
|
|
}
|
|
}
|