/[drupal]/drupal/includes/locale.inc
ViewVC logotype

Contents of /drupal/includes/locale.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.234 - (show annotations) (download) (as text)
Tue Nov 3 05:27:18 2009 UTC (3 weeks, 2 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10, HEAD
Changes since 1.233: +4 -4 lines
File MIME type: text/x-php
#602522 by effulgentsia, sun, and moshe weitzman: Make links in renderable arrays and forms (e.g. 'Operations') alterable.
1 <?php
2 // $Id: locale.inc,v 1.233 2009/10/16 02:04:42 webchick Exp $
3
4 /**
5 * @file
6 * Administration functions for locale.module.
7 */
8
9 /**
10 * Regular expression pattern used to localize JavaScript strings.
11 */
12 define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+');
13
14 /**
15 * Translation import mode overwriting all existing translations
16 * if new translated version available.
17 */
18 define('LOCALE_IMPORT_OVERWRITE', 0);
19
20 /**
21 * Translation import mode keeping existing translations and only
22 * inserting new strings.
23 */
24 define('LOCALE_IMPORT_KEEP', 1);
25
26 /**
27 * URL language negotiation: use the path prefix as URL language
28 * indicator.
29 */
30 define('LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX', 0);
31
32 /**
33 * URL language negotiation: use the domain as URL language
34 * indicator.
35 */
36 define('LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN', 1);
37
38 /**
39 * @defgroup locale-language-overview Language overview functionality
40 * @{
41 */
42
43 /**
44 * User interface for the language overview screen.
45 */
46 function locale_languages_overview_form() {
47 drupal_static_reset('language');
48 $languages = language_list('language');
49
50 $options = array();
51 $form['weight'] = array('#tree' => TRUE);
52 foreach ($languages as $langcode => $language) {
53
54 $options[$langcode] = '';
55 if ($language->enabled) {
56 $enabled[] = $langcode;
57 }
58 $form['weight'][$langcode] = array(
59 '#type' => 'weight',
60 '#default_value' => $language->weight,
61 '#attributes' => array('class' => array('language-order-weight')),
62 );
63 $form['name'][$langcode] = array('#markup' => check_plain($language->name));
64 $form['native'][$langcode] = array('#markup' => check_plain($language->native));
65 $form['direction'][$langcode] = array('#markup' => ($language->direction == LANGUAGE_RTL ? t('Right to left') : t('Left to right')));
66 }
67 $form['enabled'] = array('#type' => 'checkboxes',
68 '#options' => $options,
69 '#default_value' => $enabled,
70 );
71 $form['site_default'] = array('#type' => 'radios',
72 '#options' => $options,
73 '#default_value' => language_default('language'),
74 );
75 $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
76 $form['#theme'] = 'locale_languages_overview_form';
77
78 return $form;
79 }
80
81 /**
82 * Theme the language overview form.
83 *
84 * @param $variables
85 * An associative array containing:
86 * - form: @todo: document
87 *
88 * @return
89 * A themed HTML string representing the form.
90 *
91 * @ingroup themeable
92 */
93 function theme_locale_languages_overview_form($variables) {
94 $form = $variables['form'];
95 $default = language_default();
96 foreach ($form['name'] as $key => $element) {
97 // Do not take form control structures.
98 if (is_array($element) && element_child($key)) {
99 // Disable checkbox for the default language, because it cannot be disabled.
100 if ($key == $default->language) {
101 $form['enabled'][$key]['#attributes']['disabled'] = 'disabled';
102 }
103 $rows[] = array(
104 'data' => array(
105 '<strong>' . drupal_render($form['name'][$key]) . '</strong>',
106 drupal_render($form['native'][$key]),
107 check_plain($key),
108 drupal_render($form['direction'][$key]),
109 array('data' => drupal_render($form['enabled'][$key]), 'align' => 'center'),
110 drupal_render($form['site_default'][$key]),
111 drupal_render($form['weight'][$key]),
112 l(t('edit'), 'admin/config/regional/language/edit/' . $key) . (($key != 'en' && $key != $default->language) ? ' ' . l(t('delete'), 'admin/config/regional/language/delete/' . $key) : '')
113 ),
114 'class' => array('draggable'),
115 );
116 }
117 }
118 $header = array(array('data' => t('English name')), array('data' => t('Native name')), array('data' => t('Code')), array('data' => t('Direction')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Weight')), array('data' => t('Operations')));
119 $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'language-order')));
120 $output .= drupal_render_children($form);
121
122 drupal_add_tabledrag('language-order', 'order', 'sibling', 'language-order-weight');
123
124 return $output;
125 }
126
127 /**
128 * Process language overview form submissions, updating existing languages.
129 */
130 function locale_languages_overview_form_submit($form, &$form_state) {
131 $languages = language_list();
132 $default = language_default();
133 $enabled_count = 0;
134 foreach ($languages as $langcode => $language) {
135 if ($form_state['values']['site_default'] == $langcode || $default->language == $langcode) {
136 // Automatically enable the default language and the language
137 // which was default previously (because we will not get the
138 // value from that disabled checkox).
139 $form_state['values']['enabled'][$langcode] = 1;
140 }
141 if ($form_state['values']['enabled'][$langcode]) {
142 $enabled_count++;
143 $language->enabled = 1;
144 }
145 else {
146 $language->enabled = 0;
147 }
148 $language->weight = $form_state['values']['weight'][$langcode];
149 db_update('languages')
150 ->fields(array(
151 'enabled' => $language->enabled,
152 'weight' => $language->weight,
153 ))
154 ->condition('language', $langcode)
155 ->execute();
156 $languages[$langcode] = $language;
157 }
158 drupal_set_message(t('Configuration saved.'));
159 variable_set('language_default', $languages[$form_state['values']['site_default']]);
160 variable_set('language_count', $enabled_count);
161
162 // Changing the language settings impacts the interface.
163 cache_clear_all('*', 'cache_page', TRUE);
164 module_invoke_all('multilingual_settings_changed');
165
166 $form_state['redirect'] = 'admin/config/regional/language';
167 return;
168 }
169 /**
170 * @} End of "locale-language-overview"
171 */
172
173 /**
174 * @defgroup locale-language-add-edit Language addition and editing functionality
175 * @{
176 */
177
178 /**
179 * User interface for the language addition screen.
180 */
181 function locale_languages_add_screen() {
182 $build['predefined'] = drupal_get_form('locale_languages_predefined_form');
183 $build['custom'] = drupal_get_form('locale_languages_custom_form');
184 return $build;
185 }
186
187 /**
188 * Predefined language setup form.
189 */
190 function locale_languages_predefined_form($form) {
191 $predefined = _locale_prepare_predefined_list();
192 $form['language list'] = array('#type' => 'fieldset',
193 '#title' => t('Predefined language'),
194 '#collapsible' => TRUE,
195 );
196 $form['language list']['langcode'] = array('#type' => 'select',
197 '#title' => t('Language name'),
198 '#default_value' => key($predefined),
199 '#options' => $predefined,
200 '#description' => t('Select the desired language and click the <em>Add language</em> button. (Use the <em>Custom language</em> options if your desired language does not appear in this list.)'),
201 );
202 $form['language list']['submit'] = array('#type' => 'submit', '#value' => t('Add language'));
203 return $form;
204 }
205
206 /**
207 * Custom language addition form.
208 */
209 function locale_languages_custom_form($form) {
210 $form['custom language'] = array('#type' => 'fieldset',
211 '#title' => t('Custom language'),
212 '#collapsible' => TRUE,
213 '#collapsed' => TRUE,
214 );
215 _locale_languages_common_controls($form['custom language']);
216 $form['custom language']['submit'] = array(
217 '#type' => 'submit',
218 '#value' => t('Add custom language')
219 );
220 // Reuse the validation and submit functions of the predefined language setup form.
221 $form['#submit'][] = 'locale_languages_predefined_form_submit';
222 $form['#validate'][] = 'locale_languages_predefined_form_validate';
223 return $form;
224 }
225
226 /**
227 * Editing screen for a particular language.
228 *
229 * @param $langcode
230 * Language code of the language to edit.
231 */
232 function locale_languages_edit_form($form, &$form_state, $langcode) {
233 if ($language = db_query("SELECT * FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchObject()) {
234 _locale_languages_common_controls($form, $language);
235 $form['submit'] = array(
236 '#type' => 'submit',
237 '#value' => t('Save language')
238 );
239 $form['#submit'][] = 'locale_languages_edit_form_submit';
240 $form['#validate'][] = 'locale_languages_edit_form_validate';
241 return $form;
242 }
243 else {
244 drupal_not_found();
245 }
246 }
247
248 /**
249 * Common elements of the language addition and editing form.
250 *
251 * @param $form
252 * A parent form item (or empty array) to add items below.
253 * @param $language
254 * Language object to edit.
255 */
256 function _locale_languages_common_controls(&$form, $language = NULL) {
257 if (!is_object($language)) {
258 $language = new stdClass();
259 }
260 if (isset($language->language)) {
261 $form['langcode_view'] = array(
262 '#type' => 'item',
263 '#title' => t('Language code'),
264 '#markup' => $language->language
265 );
266 $form['langcode'] = array(
267 '#type' => 'value',
268 '#value' => $language->language
269 );
270 }
271 else {
272 $form['langcode'] = array('#type' => 'textfield',
273 '#title' => t('Language code'),
274 '#size' => 12,
275 '#maxlength' => 60,
276 '#required' => TRUE,
277 '#default_value' => @$language->language,
278 '#disabled' => (isset($language->language)),
279 '#description' => t('<a href="@rfc4646">RFC 4646</a> compliant language identifier. Language codes typically use a country code, and optionally, a script or regional variant name. <em>Examples: "en", "en-US" and "zh-Hant".</em>', array('@rfc4646' => 'http://www.ietf.org/rfc/rfc4646.txt')),
280 );
281 }
282 $form['name'] = array('#type' => 'textfield',
283 '#title' => t('Language name in English'),
284 '#maxlength' => 64,
285 '#default_value' => @$language->name,
286 '#required' => TRUE,
287 '#description' => t('Name of the language in English. Will be available for translation in all languages.'),
288 );
289 $form['native'] = array('#type' => 'textfield',
290 '#title' => t('Native language name'),
291 '#maxlength' => 64,
292 '#default_value' => @$language->native,
293 '#required' => TRUE,
294 '#description' => t('Name of the language in the language being added.'),
295 );
296 $form['prefix'] = array('#type' => 'textfield',
297 '#title' => t('Path prefix'),
298 '#maxlength' => 64,
299 '#default_value' => @$language->prefix,
300 '#description' => t('Language code or other custom string for pattern matching within the path. With language negotiation set to <em>Path prefix only</em> or <em>Path prefix with language fallback</em>, this site is presented in this language when the Path prefix value matches an element in the path. For the default language, this value may be left blank. <strong>Modifying this value will break existing URLs and should be used with caution in a production environment.</strong> <em>Example: Specifying "deutsch" as the path prefix for German results in URLs in the form "www.example.com/deutsch/node".</em>')
301 );
302 $form['domain'] = array('#type' => 'textfield',
303 '#title' => t('Language domain'),
304 '#maxlength' => 128,
305 '#default_value' => @$language->domain,
306 '#description' => t('Language-specific URL, with protocol. With language negotiation set to <em>Domain name only</em>, the site is presented in this language when the URL accessing the site references this domain. For the default language, this value may be left blank. <strong>This value must include a protocol as part of the string.</strong> <em>Example: Specifying "http://example.de" or "http://de.example.com" as language domains for German results in URLs in the forms "http://example.de/node" and "http://de.example.com/node", respectively.</em>'),
307 );
308 $form['direction'] = array('#type' => 'radios',
309 '#title' => t('Direction'),
310 '#required' => TRUE,
311 '#description' => t('Direction that text in this language is presented.'),
312 '#default_value' => @$language->direction,
313 '#options' => array(LANGUAGE_LTR => t('Left to right'), LANGUAGE_RTL => t('Right to left'))
314 );
315 return $form;
316 }
317
318 /**
319 * Validate the language addition form.
320 */
321 function locale_languages_predefined_form_validate($form, &$form_state) {
322 $langcode = $form_state['values']['langcode'];
323
324 if (($duplicate = db_query("SELECT COUNT(*) FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchField()) != 0) {
325 form_set_error('langcode', t('The language %language (%code) already exists.', array('%language' => $form_state['values']['name'], '%code' => $langcode)));
326 }
327
328 if (!isset($form_state['values']['name'])) {
329 // Predefined language selection.
330 include_once DRUPAL_ROOT . '/includes/iso.inc';
331 $predefined = _locale_get_predefined_list();
332 if (!isset($predefined[$langcode])) {
333 form_set_error('langcode', t('Invalid language code.'));
334 }
335 }
336 else {
337 // Reuse the editing form validation routine if we add a custom language.
338 locale_languages_edit_form_validate($form, $form_state);
339 }
340 }
341
342 /**
343 * Process the language addition form submission.
344 */
345 function locale_languages_predefined_form_submit($form, &$form_state) {
346 $langcode = $form_state['values']['langcode'];
347 if (isset($form_state['values']['name'])) {
348 // Custom language form.
349 locale_add_language($langcode, $form_state['values']['name'], $form_state['values']['native'], $form_state['values']['direction'], $form_state['values']['domain'], $form_state['values']['prefix']);
350 drupal_set_message(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => t($form_state['values']['name']), '@locale-help' => url('admin/help/locale'))));
351 }
352 else {
353 // Predefined language selection.
354 include_once DRUPAL_ROOT . '/includes/iso.inc';
355 $predefined = _locale_get_predefined_list();
356 locale_add_language($langcode);
357 drupal_set_message(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => t($predefined[$langcode][0]), '@locale-help' => url('admin/help/locale'))));
358 }
359
360 // See if we have language files to import for the newly added
361 // language, collect and import them.
362 if ($batch = locale_batch_by_language($langcode, '_locale_batch_language_finished')) {
363 batch_set($batch);
364 }
365
366 module_invoke_all('multilingual_settings_changed');
367
368 $form_state['redirect'] = 'admin/config/regional/language';
369 return;
370 }
371
372 /**
373 * Validate the language editing form. Reused for custom language addition too.
374 */
375 function locale_languages_edit_form_validate($form, &$form_state) {
376 if (!empty($form_state['values']['domain']) && !empty($form_state['values']['prefix'])) {
377 form_set_error('prefix', t('Domain and path prefix values should not be set at the same time.'));
378 }
379 if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) {
380 form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
381 }
382 if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
383 form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.'));
384 }
385 if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) {
386 form_set_error('prefix', t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate->language)));
387 }
388 }
389
390 /**
391 * Process the language editing form submission.
392 */
393 function locale_languages_edit_form_submit($form, &$form_state) {
394 db_update('languages')
395 ->fields(array(
396 'name' => $form_state['values']['name'],
397 'native' => $form_state['values']['native'],
398 'domain' => $form_state['values']['domain'],
399 'prefix' => $form_state['values']['prefix'],
400 'direction' => $form_state['values']['direction'],
401 ))
402 ->condition('language', $form_state['values']['langcode'])
403 ->execute();
404 $default = language_default();
405 if ($default->language == $form_state['values']['langcode']) {
406 $properties = array('name', 'native', 'direction', 'enabled', 'plurals', 'formula', 'domain', 'prefix', 'weight');
407 foreach ($properties as $keyname) {
408 if (isset($form_state['values'][$keyname])) {
409 $default->$keyname = $form_state['values'][$keyname];
410 }
411 }
412 variable_set('language_default', $default);
413 }
414 $form_state['redirect'] = 'admin/config/regional/language';
415 return;
416 }
417 /**
418 * @} End of "locale-language-add-edit"
419 */
420
421 /**
422 * @defgroup locale-language-delete Language deletion functionality
423 * @{
424 */
425
426 /**
427 * User interface for the language deletion confirmation screen.
428 */
429 function locale_languages_delete_form($form, &$form_state, $langcode) {
430
431 // Do not allow deletion of English locale.
432 if ($langcode == 'en') {
433 drupal_set_message(t('The English language cannot be deleted.'));
434 drupal_goto('admin/config/regional/language');
435 }
436
437 if (language_default('language') == $langcode) {
438 drupal_set_message(t('The default language cannot be deleted.'));
439 drupal_goto('admin/config/regional/language');
440 }
441
442 // For other languages, warn user that data loss is ahead.
443 $languages = language_list();
444
445 if (!isset($languages[$langcode])) {
446 drupal_not_found();
447 }
448 else {
449 $form['langcode'] = array('#type' => 'value', '#value' => $langcode);
450 return confirm_form($form, t('Are you sure you want to delete the language %name?', array('%name' => t($languages[$langcode]->name))), 'admin/config/regional/language', t('Deleting a language will remove all interface translations associated with it, and posts in this language will be set to be language neutral. This action cannot be undone.'), t('Delete'), t('Cancel'));
451 }
452 }
453
454 /**
455 * Process language deletion submissions.
456 */
457 function locale_languages_delete_form_submit($form, &$form_state) {
458 $languages = language_list();
459 if (isset($languages[$form_state['values']['langcode']])) {
460 // Remove translations first.
461 db_delete('locales_target')
462 ->condition('language', $form_state['values']['langcode'])
463 ->execute();
464 cache_clear_all('locale:' . $form_state['values']['langcode'], 'cache');
465 // With no translations, this removes existing JavaScript translations file.
466 _locale_rebuild_js($form_state['values']['langcode']);
467 // Remove the language.
468 db_delete('languages')
469 ->condition('language', $form_state['values']['langcode'])
470 ->execute();
471 db_update('node')
472 ->fields(array('language' => ''))
473 ->condition('language', $form_state['values']['langcode'])
474 ->execute();
475 module_invoke_all('multilingual_settings_changed');
476 $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name);
477 drupal_set_message(t('The language %locale has been removed.', $variables));
478 watchdog('locale', 'The language %locale has been removed.', $variables);
479 }
480
481 // Changing the language settings impacts the interface:
482 cache_clear_all('*', 'cache_page', TRUE);
483
484 $form_state['redirect'] = 'admin/config/regional/language';
485 return;
486 }
487 /**
488 * @} End of "locale-language-add-edit"
489 */
490
491 /**
492 * @defgroup locale-languages-negotiation Language negotiation options
493 * @{
494 */
495
496 /**
497 * Setting for language negotiation options
498 */
499 function locale_languages_configure_form() {
500 include_once DRUPAL_ROOT . '/includes/language.inc';
501
502 $form = array(
503 '#submit' => array('locale_languages_configure_form_submit'),
504 '#theme' => 'locale_languages_configure_form',
505 '#language_types' => language_types_configurable(),
506 '#language_types_info' => language_types_info(),
507 '#language_providers' => language_negotiation_info(),
508 );
509
510 foreach ($form['#language_types'] as $type) {
511 _locale_languages_configure_form_language_table($form, $type);
512 }
513
514 $form['submit'] = array(
515 '#type' => 'submit',
516 '#value' => t('Save settings'),
517 );
518
519 return $form;
520 }
521
522 /**
523 * Helper function to build a language provider table.
524 */
525 function _locale_languages_configure_form_language_table(&$form, $type) {
526 $info = $form['#language_types_info'][$type];
527
528 $table_form = array(
529 '#title' => t('@type language', array('@type' => $info['name'])),
530 '#tree' => TRUE,
531 '#description' => $info['description'],
532 '#language_providers' => array(),
533 '#show_operations' => FALSE,
534 'weight' => array('#tree' => TRUE),
535 'enabled' => array('#tree' => TRUE),
536 );
537
538 $language_providers = $form['#language_providers'];
539 $enabled_providers = variable_get("locale_language_providers_enabled_$type", array());
540 $providers_weight = variable_get("locale_language_providers_weight_$type", array());
541
542 // Add missing data to the providers lists.
543 foreach ($language_providers as $id => $provider) {
544 if (!isset($providers_weight[$id])) {
545 $providers_weight[$id] = language_provider_weight($provider);
546 }
547 if (!isset($enabled_providers[$id])) {
548 $enabled_providers[$id] = FALSE;
549 }
550 }
551
552 // Order providers list by weight.
553 asort($providers_weight);
554
555 foreach ($providers_weight as $id => $weight) {
556 $enabled = $enabled_providers[$id];
557 $provider = $language_providers[$id];
558
559 // List the provider only if the current type is defined in its 'types' key.
560 // If it is not defined default to all the configurabe language types.
561 $types = array_flip(isset($provider['types']) ? $provider['types'] : $form['#language_types']);
562
563 if (isset($types[$type])) {
564 $table_form['#language_providers'][$id] = $provider;
565
566 $table_form['weight'][$id] = array(
567 '#type' => 'weight',
568 '#default_value' => $weight,
569 '#attributes' => array('class' => array("language-provider-weight-$type")),
570 );
571
572 $table_form['title'][$id] = array('#markup' => check_plain($provider['name']));
573
574 $table_form['enabled'][$id] = array('#type' => 'checkbox', '#default_value' => $enabled);
575 if ($id === LANGUAGE_NEGOTIATION_DEFAULT) {
576 $table_form['enabled'][$id]['#default_value'] = TRUE;
577 $table_form['enabled'][$id]['#attributes'] = array('disabled' => 'disabled');
578 }
579
580 $table_form['description'][$id] = array('#markup' => filter_xss_admin($provider['description']));
581
582 $config_op = array();
583 if (isset($provider['config'])) {
584 $config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $provider['config']);
585 // If there is at least one operation enabled show the operation column.
586 $table_form['#show_operations'] = TRUE;
587 }
588 $table_form['operation'][$id] = $config_op;
589 }
590 }
591
592 $form[$type] = $table_form;
593 }
594
595 /**
596 * Theme the language configure form.
597 *
598 * @ingroup themeable
599 */
600 function theme_locale_languages_configure_form($variables) {
601 $form = $variables['form'];
602 $output = '';
603
604 foreach ($form['#language_types'] as $type) {
605 $rows = array();
606 $info = $form['#language_types_info'][$type];
607 $title = '<label>' . $form[$type]['#title'] . '</label>';
608 $description = '<div class="description">' . $form[$type]['#description'] . '</div>';
609
610 foreach ($form[$type]['title'] as $id => $element) {
611 // Do not take form control structures.
612 if (is_array($element) && element_child($id)) {
613 $row = array(
614 'data' => array(
615 '<strong>' . drupal_render($form[$type]['title'][$id]) . '</strong>',
616 drupal_render($form[$type]['description'][$id]),
617 drupal_render($form[$type]['enabled'][$id]),
618 drupal_render($form[$type]['weight'][$id]),
619 ),
620 'class' => array('draggable'),
621 );
622 if ($form[$type]['#show_operations']) {
623 $row['data'][] = drupal_render($form[$type]['operation'][$id]);
624 }
625 $rows[] = $row;
626 }
627 }
628
629 $header = array(
630 array('data' => t('Detection method')),
631 array('data' => t('Description')),
632 array('data' => t('Enabled')),
633 array('data' => t('Weight')),
634 );
635
636 // If there is at least one operation enabled show the operation column.
637 if ($form[$type]['#show_operations']) {
638 $header[] = array('data' => t('Operations'));
639 }
640
641 $variables = array(
642 'header' => $header,
643 'rows' => $rows,
644 'attributes' => array('id' => "language-negotiation-providers-$type"),
645 );
646 $table = theme('table', $variables);
647 $table .= drupal_render_children($form[$type]);
648
649 drupal_add_tabledrag("language-negotiation-providers-$type", 'order', 'sibling', "language-provider-weight-$type");
650
651 $output .= '<div class="form-item">' . $title . $description . $table . '</div>';
652 }
653
654 $output .= drupal_render_children($form);
655 return $output;
656 }
657
658 /**
659 * Submit handler for language negotiation settings.
660 */
661 function locale_languages_configure_form_submit($form, &$form_state) {
662 $language_types = array();
663 $configurable_types = $form['#language_types'];
664
665 foreach ($configurable_types as $type) {
666 $negotiation = array();
667 $enabled_providers = $form_state['values'][$type]['enabled'];
668 $enabled_providers[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE;
669 $providers_weight = $form_state['values'][$type]['weight'];
670 $language_types[$type] = TRUE;
671
672 foreach ($providers_weight as $id => $weight) {
673 if ($enabled_providers[$id]) {
674 $provider = $form[$type]['#language_providers'][$id];
675 $provider['weight'] = $weight;
676 $negotiation[$id] = $provider;
677 }
678 }
679
680 language_negotiation_set($type, $negotiation);
681 variable_set("locale_language_providers_enabled_$type", $enabled_providers);
682 variable_set("locale_language_providers_weight_$type", $providers_weight);
683 }
684
685 // Save non-configurable language types negotiation.
686 $language_types_info = language_types_info();
687 $defined_providers = $form['#language_providers'];
688 foreach ($language_types_info as $type => $info) {
689 if (isset($info['fixed'])) {
690 $language_types[$type] = FALSE;
691 $negotiation = array();
692 foreach ($info['fixed'] as $id) {
693 if (isset($defined_providers[$id])) {
694 $negotiation[$id] = $defined_providers[$id];
695 }
696 }
697 language_negotiation_set($type, $negotiation);
698 }
699 }
700
701 // Save language types.
702 variable_set('language_types', $language_types);
703
704 $form_state['redirect'] = 'admin/config/regional/language';
705 drupal_set_message(t('Language negotiation configuration saved.'));
706 }
707
708 /**
709 * The URL language provider configuration form.
710 */
711 function locale_language_providers_url_form() {
712 $form = array();
713
714 $form['locale_language_negotiation_url_part'] = array(
715 '#title' => t('URL language indicator'),
716 '#type' => 'radios',
717 '#options' => array(
718 LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
719 LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
720 ),
721 '#default_value' => variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX),
722 '#description' => t('Select which part of the URL will determine the language.'),
723 );
724
725 $form['#redirect'] = 'admin/config/regional/language/configure';
726
727 return system_settings_form($form);
728 }
729
730 /**
731 * The URL language provider configuration form.
732 */
733 function locale_language_providers_session_form() {
734 $form = array();
735
736 $form['locale_language_negotiation_session_param'] = array(
737 '#title' => t('Request/session parameter'),
738 '#type' => 'textfield',
739 '#default_value' => variable_get('locale_language_negotiation_session_param', 'language'),
740 '#description' => t('This value will be the name of the request/session parameter which will be used to determine the desired language.'),
741 );
742
743 $form['#redirect'] = 'admin/config/regional/language/configure';
744
745 return system_settings_form($form);
746 }
747
748 /**
749 * Identify the language from the current content language.
750 *
751 * @return
752 * The current content language code.
753 */
754 function locale_language_from_content() {
755 global $language;
756 return isset($language->language) ? $language->language : FALSE;
757 }
758
759 /**
760 * Identify language from the Accept-language HTTP header we got.
761 *
762 * We perform browser accept-language parsing only if page cache is disabled,
763 * otherwise we would cache a user-specific preference.
764 *
765 * @param $languages
766 * An array of valid language objects.
767 *
768 * @return
769 * A valid language code on success, FALSE otherwise.
770 */
771 function locale_language_from_browser($languages) {
772 // Specified by the user via the browser's Accept Language setting
773 // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
774 $browser_langs = array();
775
776 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
777 $browser_accept = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
778 foreach ($browser_accept as $langpart) {
779 // The language part is either a code or a code with a quality.
780 // We cannot do anything with a * code, so it is skipped.
781 // If the quality is missing, it is assumed to be 1 according to the RFC.
782 if (preg_match("!([a-z-]+)(;q=([0-9\\.]+))?!", trim($langpart), $found)) {
783 $browser_langs[$found[1]] = (isset($found[3]) ? (float) $found[3] : 1.0);
784 }
785 }
786 }
787
788 // Order the codes by quality
789 arsort($browser_langs);
790
791 // Try to find the first preferred language we have
792 foreach ($browser_langs as $langcode => $q) {
793 if (isset($languages[$langcode])) {
794 return $langcode;
795 }
796 }
797
798 return FALSE;
799 }
800
801 /**
802 * Identify language from the user preferences.
803 *
804 * @param $languages
805 * An array of valid language objects.
806 *
807 * @return
808 * A valid language code on succes, FALSE otherwise.
809 */
810 function locale_language_from_user($languages) {
811 // User preference (only for logged users).
812 global $user;
813
814 if ($user->uid) {
815 return $user->language;
816 }
817
818 // No language preference from the user.
819 return FALSE;
820 }
821
822 /**
823 * Identify language from a request/session parameter.
824 *
825 * @param $languages
826 * An array of valid language objects.
827 *
828 * @return
829 * A valid language code on succes, FALSE otherwise.
830 */
831 function locale_language_from_session($languages) {
832 $param = variable_get('locale_language_negotiation_session_param', 'language');
833
834 // Request parameter.
835 if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) {
836 return $_SESSION[$param] = $langcode;
837 }
838
839 // Session parameter.
840 if (isset($_SESSION[$param])) {
841 return $_SESSION[$param];
842 }
843
844 return FALSE;
845 }
846
847 /**
848 * Identify language via URL prefix or domain.
849 *
850 * @param $languages
851 * An array of valid language objects.
852 *
853 * @return
854 * A valid language code on succes, FALSE otherwise.
855 */
856 function locale_language_from_url($languages) {
857 $language_url = FALSE;
858
859 switch (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX)) {
860 case LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX:
861 // $_GET['q'] might not be available at this time, because
862 // path initialization runs after the language bootstrap phase.
863 list($language, $_GET['q']) = language_url_split_prefix(isset($_GET['q']) ? $_GET['q'] : NULL, $languages);
864 if ($language !== FALSE) {
865 $language_url = $language->language;
866 }
867 break;
868
869 case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
870 foreach ($languages as $language) {
871 $host = parse_url($language->domain, PHP_URL_HOST);
872 if ($host && ($_SERVER['HTTP_HOST'] == $host)) {
873 $language_url = $language->language;
874 break;
875 }
876 }
877 break;
878 }
879
880 return $language_url;
881 }
882
883 /**
884 * Return the URL language switcher block. Translation links may be provided by
885 * other modules.
886 */
887 function locale_language_switcher_url($type, $path) {
888 $languages = language_list('enabled');
889 $links = array();
890
891 foreach ($languages[1] as $language) {
892 $links[$language->language] = array(
893 'href' => $path,
894 'title' => $language->native,
895 'language' => $language,
896 'attributes' => array('class' => array('language-link')),
897 );
898 }
899
900 return $links;
901 }
902
903 /**
904 * Return the session language switcher block.
905 */
906 function locale_language_switcher_session($type, $path) {
907 drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
908
909 $param = variable_get('locale_language_negotiation_session_param', 'language');
910 $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $GLOBALS[$type]->language;
911
912 $languages = language_list('enabled');
913 $links = array();
914
915 $query = $_GET;
916 unset($query['q']);
917
918 foreach ($languages[1] as $language) {
919 $langcode = $language->language;
920 $links[$langcode] = array(
921 'href' => $path,
922 'title' => $language->native,
923 'attributes' => array('class' => array('language-link')),
924 'query' => $query,
925 );
926 if ($language_query != $langcode) {
927 $links[$langcode]['query'][$param] = $langcode;
928 }
929 else {
930 $links[$langcode]['attributes']['class'][] = ' session-active';
931 }
932 }
933
934 return $links;
935 }
936
937 /**
938 * Rewrite URLs for the URL language provider.
939 */
940 function locale_language_url_rewrite_url(&$path, &$options) {
941 // Language can be passed as an option, or we go for current URL language.
942 if (!isset($options['language'])) {
943 global $language_url;
944 $options['language'] = $language_url;
945 }
946
947 if (isset($options['language'])) {
948 switch (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX)) {
949 case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
950 if ($options['language']->domain) {
951 // Ask for an absolute URL with our modified base_url.
952 $options['absolute'] = TRUE;
953 $options['base_url'] = $options['language']->domain;
954 }
955 break;
956
957 case LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX:
958 if (!empty($options['language']->prefix)) {
959 $options['prefix'] = $options['language']->prefix . '/';
960 }
961 break;
962 }
963 }
964 }
965
966 /**
967 * Rewrite URLs for the Session language provider.
968 */
969 function locale_language_url_rewrite_session(&$path, &$options) {
970 static $query_rewrite, $query_param, $query_value;
971
972 // The following values are not supposed to change during a single page
973 // request processing.
974 if (!isset($query_rewrite)) {
975 global $user;
976 if (!$user->uid) {
977 $languages = language_list('enabled');
978 $languages = $languages[1];
979 $query_param = check_plain(variable_get('locale_language_negotiation_session_param', 'language'));
980 $query_value = isset($_GET[$query_param]) ? check_plain($_GET[$query_param]) : NULL;
981 $query_rewrite = isset($languages[$query_value]) && language_negotiation_get_any(LOCALE_LANGUAGE_NEGOTIATION_SESSION);
982 }
983 else {
984 $query_rewrite = FALSE;
985 }
986 }
987
988 // If the user is anonymous, the user language provider is enabled, and the
989 // corresponding option has been set, we must preserve any explicit user
990 // language preference even with cookies disabled.
991 if ($query_rewrite) {
992 if (is_string($options['query'])) {
993 $options['query'] = drupal_get_query_array($options['query']);
994 }
995 if (!isset($options['query'][$query_param])) {
996 $options['query'][$query_param] = $query_value;
997 }
998 }
999 }
1000
1001 /**
1002 * @} End of "locale-languages-negotiation"
1003 */
1004
1005 /**
1006 * @defgroup locale-translate-overview Translation overview screen.
1007 * @{
1008 */
1009
1010 /**
1011 * Overview screen for translations.
1012 */
1013 function locale_translate_overview_screen() {
1014 drupal_static_reset('language_list');
1015 $languages = language_list('language');
1016 $groups = module_invoke_all('locale', 'groups');
1017
1018 // Build headers with all groups in order.
1019 $headers = array_merge(array(t('Language')), array_values($groups));
1020
1021 // Collect summaries of all source strings in all groups.
1022 $sums = db_query("SELECT COUNT(*) AS strings, textgroup FROM {locales_source} GROUP BY textgroup");
1023 $groupsums = array();
1024 foreach ($sums as $group) {
1025 $groupsums[$group->textgroup] = $group->strings;
1026 }
1027
1028 // Set up overview table with default values, ensuring common order for values.
1029 $rows = array();
1030 foreach ($languages as $langcode => $language) {
1031 $rows[$langcode] = array('name' => ($langcode == 'en' ? t('English (built-in)') : t($language->name)));
1032 foreach ($groups as $group => $name) {
1033 $rows[$langcode][$group] = ($langcode == 'en' ? t('n/a') : '0/' . (isset($groupsums[$group]) ? $groupsums[$group] : 0) . ' (0%)');
1034 }
1035 }
1036
1037 // Languages with at least one record in the locale table.
1038 $translations = db_query("SELECT COUNT(*) AS translation, t.language, s.textgroup FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid GROUP BY textgroup, language");
1039 foreach ($translations as $data) {
1040 $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation > 0) ? round(($data->translation/$groupsums[$data->textgroup]) * 100.0, 2) : 0;
1041 $rows[$data->language][$data->textgroup] = $data->translation . '/' . $groupsums[$data->textgroup] . " ($ratio%)";
1042 }
1043
1044 return theme('table', array('header' => $headers, 'rows' => $rows));
1045 }
1046 /**
1047 * @} End of "locale-translate-overview"
1048 */
1049
1050 /**
1051 * @defgroup locale-translate-seek Translation search screen.
1052 * @{
1053 */
1054
1055 /**
1056 * String search screen.
1057 */
1058 function locale_translate_seek_screen() {
1059 // Add CSS.
1060 drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css', array('preprocess' => FALSE));
1061
1062 $output = drupal_render(drupal_get_form('locale_translation_filter_form'));
1063 $output .= _locale_translate_seek();
1064 return $output;
1065 }
1066 /**
1067 * @} End of "locale-translate-seek"
1068 */
1069
1070 /**
1071 * List locale translation filters that can be applied.
1072 */
1073 function locale_translation_filters() {
1074 $filters = array();
1075
1076 // Get all languages, except English
1077 drupal_static_reset('language_list');
1078 $languages = locale_language_list('name');
1079 unset($languages['en']);
1080
1081 $filters['string'] = array(
1082 'title' => t('String contains'),
1083 'description' => t('Leave blank to show all strings. The search is case sensitive.'),
1084 );
1085
1086 $filters['language'] = array(
1087 'title' => t('Language'),
1088 'options' => array_merge(array('all' => t('All languages'), 'en' => t('English (provided by Drupal)')), $languages),
1089 );
1090
1091 $filters['translation'] = array(
1092 'title' => t('Search in'),
1093 'options' => array('all' => t('Both translated and untranslated strings'), 'translated' => t('Only translated strings'), 'untranslated' => t('Only untranslated strings')),
1094 );
1095
1096 $groups = module_invoke_all('locale', 'groups');
1097 $filters['group'] = array(
1098 'title' => t('Limit search to'),
1099 'options' => array_merge(array('all' => t('All text groups')), $groups),
1100 );
1101
1102 return $filters;
1103 }
1104
1105 /**
1106 * Return form for locale translation filters.
1107 *
1108 * @ingroup forms
1109 */
1110 function locale_translation_filter_form() {
1111 $filters = locale_translation_filters();
1112
1113 $form['filters'] = array(
1114 '#type' => 'fieldset',
1115 '#title' => t('Filter translatable strings'),
1116 '#theme' => 'locale_translation_filters',
1117 '#collapsible' => TRUE,
1118 '#collapsed' => FALSE,
1119 );
1120 foreach ($filters as $key => $filter) {
1121 // Special case for 'string' filter.
1122 if ($key == 'string') {
1123 $form['filters']['status']['string'] = array(
1124 '#type' => 'textfield',
1125 '#title' => $filter['title'],
1126 '#description' => $filter['description'],
1127 );
1128 }
1129 else {
1130 $form['filters']['status'][$key] = array(
1131 '#title' => $filter['title'],
1132 '#type' => 'select',
1133 '#multiple' => FALSE,
1134 '#size' => 0,
1135 '#options' => $filter['options'],
1136 );
1137 }
1138 if (!empty($_SESSION['locale_translation_filter'][$key])) {
1139 $form['filters'