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