/[drupal]/drupal/modules/locale/locale.module
ViewVC logotype

Contents of /drupal/modules/locale/locale.module

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


Revision 1.267 - (show annotations) (download) (as text)
Sat Oct 24 05:13:44 2009 UTC (4 weeks, 6 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.266: +39 -1 lines
File MIME type: text/x-php
#320331 by Dave Reid, dww, John Morahan, cwgordon7, moshe weitzman, c960657, and smoothify: Turn custom_url_rewrite_inbound() and custom_url_rewrite_outbound() into hooks.
1 <?php
2 // $Id: locale.module,v 1.266 2009/10/23 22:24:16 webchick Exp $
3
4 /**
5 * @file
6 * Add language handling functionality and enables the translation of the
7 * user interface to languages other than English.
8 *
9 * When enabled, multiple languages can be set up. The site interface
10 * can be displayed in different languages, as well as nodes can have languages
11 * assigned. The setup of languages and translations is completely web based.
12 * Gettext portable object files are supported.
13 */
14
15 /**
16 * The language is determined using a URL language indicator:
17 * path prefix or domain according to the configuration.
18 */
19 define('LOCALE_LANGUAGE_NEGOTIATION_URL', 'locale-url');
20
21 /**
22 * The language is set based on the browser language settings.
23 */
24 define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser');
25
26 /**
27 * The language is determined using the current content language.
28 */
29 define('LOCALE_LANGUAGE_NEGOTIATION_CONTENT', 'locale-content');
30
31 /**
32 * The language is set based on the user language settings.
33 */
34 define('LOCALE_LANGUAGE_NEGOTIATION_USER', 'locale-user');
35
36 /**
37 * The language is set based on the request/session parameters.
38 */
39 define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
40
41 // ---------------------------------------------------------------------------------
42 // Hook implementations
43
44 /**
45 * Implement hook_help().
46 */
47 function locale_help($path, $arg) {
48 switch ($path) {
49 case 'admin/help#locale':
50 $output = '<p>' . t('The locale module allows your Drupal site to be presented in languages other than the default English, a defining feature of multi-lingual websites. The locale module works by 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 later review by a translator.') . '</p>';
51 $output .= '<p>' . t('Beyond translation of the Drupal interface, the locale module provides a feature set tailored to the needs of a multi-lingual site. 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. Your site content can be created in (and translated to) any enabled language, and each post may have a language-appropriate alias for each of its translations. The locale module works in concert with the <a href="@content-help">content translation module</a> to manage translated content.', array('@content-help' => url('admin/help/translation'))) . '</p>';
52 $output .= '<p>' . t('Translations may be provided by:') . '</p>';
53 $output .= '<ul><li>' . t("translating the original text via the locale module's integrated web interface, or") . '</li>';
54 $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 contain 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 translation page</a>.', array('@translations' => 'http://drupal.org/project/translations')) . '</li></ul>';
55 $output .= '<p>' . 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'))) . '</p>';
56 $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@locale">Locale module</a>.', array('@locale' => 'http://drupal.org/handbook/modules/locale/')) . '</p>';
57 return $output;
58 case 'admin/config/regional/language':
59 $output = '<p>' . t("This page provides an overview of your site's enabled languages. If multiple languages are available and enabled, the text on your site interface may be translated, registered users may select their preferred language on the <em>My account</em> page, and site authors may indicate a specific language when creating posts. Languages will be displayed in the order you specify in places such as the language switcher block, or the language dropdown when creating or editing posts. The site's default language is used for anonymous visitors and for users who have not selected a preferred language.") . '</p>';
60 $output .= '<p>' . t('For each language available on the site, use the <em>edit</em> link to configure language details, including name, an optional language-specific path or domain, and whether the language is natively presented either left-to-right or right-to-left. These languages also appear in the <em>Language</em> selection when creating a post of a content type with multilingual support.') . '</p>';
61 $output .= '<p>' . t('Use the <a href="@add-language">add language page</a> to enable additional languages (and automatically import files from a translation package, if available), the <a href="@search">translate interface page</a> to locate strings for manual translation, or the <a href="@import">import page</a> to add translations from individual <em>.po</em> files. A number of contributed translation packages containing <em>.po</em> files are available on the <a href="@translations">Drupal.org translations page</a>.', array('@add-language' => url('admin/config/regional/language/add'), '@search' => url('admin/config/regional/translate/translate'), '@import' => url('admin/config/regional/translate/import'), '@translations' => 'http://drupal.org/project/translations')) . '</p>';
62 $output .= '<p>' . t('Remember that your changes will not be saved until you click the <em>Save configuration</em> button at the bottom of the page.') . '</p>';
63 return $output;
64 case 'admin/config/regional/language/add':
65 return '<p>' . t('Add all languages to be supported by your site. If your desired language is not available in the <em>Language name</em> drop-down, click <em>Custom language</em> and provide a language code and other details manually. When providing a language code manually, be sure to enter a standardized language code, since this code may be used by browsers to determine an appropriate display language.') . '</p>';
66 case 'admin/config/regional/language/configure':
67 $output = '<p>' . t("Language negotiation settings determine the site's content and presentation languages. For both <em>language types</em> there is a list of <em>language detection methods</em> which can be used to configure the desired language negotiation logic. Each detection method can be <em>dragged</em> to gain a higher priority, but it must be <em>enabled</em> to affect the language negotiation process. If a language detection method is applied then all the lower ones are <em>ignored</em>, otherwise the following one will be taken into account. Some lanaguage detection methods provide a configuration page to further specify their behavior. The <em>default</em> detection method is always applied, so anything below it is always ignored. <strong>Modifying this setting may break all incoming URLs and should be used with caution in a production environment.</strong>") . '</p>';
68 $output .= '<p>' . t('Available options include:') .'</p>';
69 $output .= '<ul><li>' . t('<strong>URL.</strong> The language is determined by examining the URL for a language code, a custom string, or a domain, that matches the ones (if any) specified for each language. The path prefix or domain name for a language may be set by editing the <a href="@languages">available languages</a>. In the absence of an appropriate match, the site is displayed in the <a href="@languages">default language</a>. A configuration is available to choose whether use the path prefix or the domain. <em>Example: "example.com/de/contact" sets language to German based on the use of "de" within the path. "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.</em>', array('@languages' => url('admin/config/regional/language'))) . '</li>';
70 $output .= '<li>' . t('<strong>Session.</strong> The language is determined from a request/session parameter. A configuration is available to choose the URL parameter name to be used. <em>Example: "example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.</em>') . '</li></ul>';
71 return $output;
72 case 'admin/config/regional/translate':
73 $output = '<p>' . t('This page provides an overview of available translatable strings. Drupal displays translatable strings in text groups; modules may define additional text groups containing other translatable strings. Because text groups provide a method of grouping related strings, they are often used to focus translation efforts on specific areas of the Drupal interface.') . '</p>';
74 $output .= '<p>' . t('Review the <a href="@languages">languages page</a> for more information on adding support for additional languages.', array('@languages' => url('admin/config/regional/language'))) . '</p>';
75 return $output;
76 case 'admin/config/regional/translate/import':
77 $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>';
78 $output .= '<p>' . t('Note that the <em>.po</em> files within a translation package are imported automatically (if available) when new modules or themes are enabled, or as new languages are added. Since this page only allows the import of one <em>.po</em> file at a time, it may be simpler to download and extract a translation package into your Drupal installation directory and <a href="@language-add">add the language</a> (which automatically imports all <em>.po</em> files within the package). Translation packages are available for download on the <a href="@translations">Drupal translation page</a>.', array('@language-add' => url('admin/config/regional/language/add'), '@translations' => 'http://drupal.org/project/translations')) . '</p>';
79 return $output;
80 case 'admin/config/regional/translate/export':
81 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>';
82 case 'admin/config/regional/translate/translate':
83 return '<p>' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: For translation tasks involving many strings, it may be more convenient to <a href="@export">export</a> strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings found within a specific text group or in a specific language.', array('@export' => url('admin/config/regional/translate/export'))) . '</p>';
84 case 'admin/structure/block/manage':
85 if ($arg[4] == 'locale' && $arg[5] == 0) {
86 return '<p>' . t('This block is only shown if <a href="@languages">at least two languages are enabled</a> and <a href="@configuration">language negotiation</a> is set to something other than <em>None</em>.', array('@languages' => url('admin/config/regional/language'), '@configuration' => url('admin/config/regional/language/configure'))) . '</p>';
87 }
88 break;
89 }
90 }
91
92 /**
93 * Implement hook_menu().
94 */
95 function locale_menu() {
96 // Manage languages
97 $items['admin/config/regional/language'] = array(
98 'title' => 'Languages',
99 'description' => 'Configure languages for content and the user interface.',
100 'page callback' => 'drupal_get_form',
101 'page arguments' => array('locale_languages_overview_form'),
102 'access arguments' => array('administer languages'),
103 'file' => 'locale.inc',
104 'file path' => 'includes',
105 );
106 $items['admin/config/regional/language/overview'] = array(
107 'title' => 'List',
108 'weight' => 0,
109 'type' => MENU_DEFAULT_LOCAL_TASK,
110 );
111 $items['admin/config/regional/language/add'] = array(
112 'title' => 'Add language',
113 'page callback' => 'locale_languages_add_screen', // two forms concatenated
114 'access arguments' => array('administer languages'),
115 'weight' => 5,
116 'type' => MENU_LOCAL_ACTION,
117 'file' => 'locale.inc',
118 'file path' => 'includes',
119 );
120 $items['admin/config/regional/language/configure'] = array(
121 'title' => 'Configure',
122 'page callback' => 'drupal_get_form',
123 'page arguments' => array('locale_languages_configure_form'),
124 'access arguments' => array('administer languages'),
125 'weight' => 10,
126 'file' => 'locale.inc',
127 'file path' => 'includes',
128 'type' => MENU_LOCAL_TASK,
129 );
130 $items['admin/config/regional/language/configure/url'] = array(
131 'title' => 'URL language provider configuration',
132 'page callback' => 'drupal_get_form',
133 'page arguments' => array('locale_language_providers_url_form'),
134 'access arguments' => array('administer languages'),
135 'file' => 'locale.inc',
136 'file path' => 'includes',
137 );
138 $items['admin/config/regional/language/configure/session'] = array(
139 'title' => 'Session language provider configuration',
140 'page callback' => 'drupal_get_form',
141 'page arguments' => array('locale_language_providers_session_form'),
142 'access arguments' => array('administer languages'),
143 'file' => 'locale.inc',
144 'file path' => 'includes',
145 );
146 $items['admin/config/regional/language/edit/%'] = array(
147 'title' => 'Edit language',
148 'page callback' => 'drupal_get_form',
149 'page arguments' => array('locale_languages_edit_form', 5),
150 'access arguments' => array('administer languages'),
151 'file' => 'locale.inc',
152 'file path' => 'includes',
153 'type' => MENU_CALLBACK,
154 );
155 $items['admin/config/regional/language/delete/%'] = array(
156 'title' => 'Confirm',
157 'page callback' => 'drupal_get_form',
158 'page arguments' => array('locale_languages_delete_form', 5),
159 'access arguments' => array('administer languages'),
160 'file' => 'locale.inc',
161 'file path' => 'includes',
162 'type' => MENU_CALLBACK,
163 );
164
165 // Translation functionality
166 $items['admin/config/regional/translate'] = array(
167 'title' => 'Translate interface',
168 'description' => 'Translate the built in interface and optionally other text.',
169 'page callback' => 'locale_inc_callback',
170 'page arguments' => array('locale_translate_overview_screen'), // not a form, just a table
171 'access arguments' => array('translate interface'),
172 'file' => 'locale.inc',
173 'file path' => 'includes',
174 );
175 $items['admin/config/regional/translate/overview'] = array(
176 'title' => 'Overview',
177 'weight' => 0,
178 'type' => MENU_DEFAULT_LOCAL_TASK,
179 );
180 $items['admin/config/regional/translate/translate'] = array(
181 'title' => 'Translate',
182 'weight' => 10,
183 'type' => MENU_LOCAL_TASK,
184 'page callback' => 'locale_translate_seek_screen', // search results and form concatenated
185 'access arguments' => array('translate interface'),
186 'file' => 'locale.inc',
187 'file path' => 'includes',
188 );
189 $items['admin/config/regional/translate/import'] = array(
190 'title' => 'Import',
191 'page callback' => 'drupal_get_form',
192 'page arguments' => array('locale_translate_import_form'),
193 'access arguments' => array('translate interface'),
194 'weight' => 20,
195 'type' => MENU_LOCAL_TASK,
196 'file' => 'locale.inc',
197 'file path' => 'includes',
198 );
199 $items['admin/config/regional/translate/export'] = array(
200 'title' => 'Export',
201 'page callback' => 'locale_translate_export_screen', // possibly multiple forms concatenated
202 'access arguments' => array('translate interface'),
203 'weight' => 30,
204 'type' => MENU_LOCAL_TASK,
205 'file' => 'locale.inc',
206 'file path' => 'includes',
207 );
208 $items['admin/config/regional/translate/edit/%'] = array(
209 'title' => 'Edit string',
210 'page callback' => 'drupal_get_form',
211 'page arguments' => array('locale_translate_edit_form', 5),
212 'access arguments' => array('translate interface'),
213 'type' => MENU_CALLBACK,
214 'file' => 'locale.inc',
215 'file path' => 'includes',
216 );
217 $items['admin/config/regional/translate/delete/%'] = array(
218 'title' => 'Delete string',
219 'page callback' => 'locale_translate_delete_page',
220 'page arguments' => array(5),
221 'access arguments' => array('translate interface'),
222 'type' => MENU_CALLBACK,
223 'file' => 'locale.inc',
224 'file path' => 'includes',
225 );
226
227 // Localize date formats.
228 $items['admin/config/regional/date-time/locale'] = array(
229 'title' => 'Localize',
230 'description' => 'Configure date formats for each locale',
231 'page callback' => 'locale_date_format_language_overview_page',
232 'access arguments' => array('administer site configuration'),
233 'type' => MENU_LOCAL_TASK,
234 'weight' => -8,
235 'file' => 'locale.inc',
236 'file path' => 'includes',
237 );
238 $items['admin/config/regional/date-time/locale/%/edit'] = array(
239 'title' => 'Localize date formats',
240 'description' => 'Configure date formats for each locale',
241 'page callback' => 'drupal_get_form',
242 'page arguments' => array('locale_date_format_form', 5),
243 'access arguments' => array('administer site configuration'),
244 'type' => MENU_CALLBACK,
245 'file' => 'locale.inc',
246 'file path' => 'includes',
247 );
248 $items['admin/config/regional/date-time/locale/%/reset'] = array(
249 'title' => 'Reset date formats',
250 'description' => 'Reset localized date formats to global defaults',
251 'page callback' => 'drupal_get_form',
252 'page arguments' => array('locale_date_format_reset_form', 5),
253 'access arguments' => array('administer site configuration'),
254 'type' => MENU_CALLBACK,
255 'file' => 'locale.inc',
256 'file path' => 'includes',
257 );
258
259 return $items;
260 }
261
262 /**
263 * Implements hook_init().
264 *
265 * Initialize date formats according to the user's current locale.
266 */
267 function locale_init() {
268 global $conf, $language;
269 include_once DRUPAL_ROOT . '/includes/locale.inc';
270
271 // For each date type (e.g. long, short), get the localized date format
272 // for the user's current language and override the default setting for it
273 // in $conf. This should happen on all pages except the date and time formats
274 // settings page, where we want to display the site default and not the
275 // localized version.
276 if (strpos($_GET['q'], 'admin/config/regional/date-time/formats') !== 0) {
277 $languages = array($language->language);
278
279 // Setup appropriate date formats for this locale.
280 $formats = locale_get_localized_date_format($languages);
281 foreach ($formats as $format_type => $format) {
282 $conf[$format_type] = $format;
283 }
284 }
285 }
286
287 /**
288 * Wrapper function to be able to set callbacks in locale.inc
289 */
290 function locale_inc_callback() {
291 $args = func_get_args();
292 $function = array_shift($args);
293 include_once DRUPAL_ROOT . '/includes/locale.inc';
294 return call_user_func_array($function, $args);
295 }
296
297 /**
298 * Implement hook_permission().
299 */
300 function locale_permission() {
301 return array(
302 'administer languages' => array(
303 'title' => t('Administer languages'),
304 'description' => t('Manage the languages in which the website content and interface text may be displayed.'),
305 ),
306 'translate interface' => array(
307 'title' => t('Translate the interface'),
308 'description' => t('Translate the text of the website interface.'),
309 ),
310 );
311 }
312
313 /**
314 * Implement hook_locale().
315 */
316 function locale_locale($op = 'groups') {
317 switch ($op) {
318 case 'groups':
319 return array('default' => t('Built-in interface'));
320 }
321 }
322
323 /**
324 * Form builder callback to display language selection widget.
325 *
326 * @ingroup forms
327 * @see locale_form_alter()
328 */
329 function locale_language_selector_form(&$form, &$form_state, $user) {
330 global $language;
331 $languages = language_list('enabled');
332 $languages = $languages[1];
333
334 // If the user is being created, we set the user language to the page language.
335 $user_preferred_language = $user->uid ? user_preferred_language($user) : $language;
336
337 $names = array();
338 foreach ($languages as $langcode => $item) {
339 $name = t($item->name);
340 $names[$langcode] = $name . ($item->native != $name ? ' (' . $item->native . ')' : '');
341 }
342 $form['locale'] = array(
343 '#type' => 'fieldset',
344 '#title' => t('Language settings'),
345 '#weight' => 1,
346 );
347
348 // Get language negotiation settings.
349 $mode = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_DEFAULT;
350 $form['locale']['language'] = array(
351 '#type' => (count($names) <= 5 ? 'radios' : 'select'),
352 '#title' => t('Language'),
353 '#default_value' => $user_preferred_language->language,
354 '#options' => $names,
355 '#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."),
356 );
357 }
358
359 /**
360 * Implement hook_form_FORM_ID_alter().
361 */
362 function locale_form_path_admin_form_alter(&$form, &$form_state) {
363 $form['language'] = array(
364 '#type' => 'select',
365 '#title' => t('Language'),
366 '#options' => array('' => t('All languages')) + locale_language_list('name'),
367 '#default_value' => $form['language']['#value'],
368 '#weight' => -10,
369 '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for <em>All languages</em>.'),
370 );
371 }
372
373 /**
374 * Implement hook_form_FORM_ID_alter().
375 */
376 function locale_form_node_type_form_alter(&$form, &$form_state) {
377 if (isset($form['identity']['type'])) {
378 $form['workflow']['language_content_type'] = array(
379 '#type' => 'radios',
380 '#title' => t('Multilingual support'),
381 '#default_value' => variable_get('language_content_type_' . $form['#node_type']->type, 0),
382 '#options' => array(t('Disabled'), t('Enabled')),
383 '#description' => t('Enable multilingual support for this content type. If enabled, a language selection field will be added to the editing form, allowing you to select from one of the <a href="!languages">enabled languages</a>. If disabled, new posts are saved with the default language. Existing content will not be affected by changing this option.', array('!languages' => url('admin/config/regional/language'))),
384 );
385 }
386 }
387
388 /**
389 * Return whether the given content type has multilingual support.
390 *
391 * @return
392 * True if multilingual support is enabled.
393 */
394 function locale_multilingual_node_type($type_name) {
395 return (bool) variable_get('language_content_type_' . $type_name, 0);
396 }
397
398 /**
399 * Implement hook_form_alter().
400 *
401 * Adds language fields to forms.
402 */
403 function locale_form_alter(&$form, &$form_state, $form_id) {
404 // Only alter user forms if there is more than one language.
405 if (drupal_multilingual()) {
406 // Display language selector when either creating a user on the admin
407 // interface or editing a user account.
408 if (($form_id == 'user_register_form' && user_access('administer users')) || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) {
409 locale_language_selector_form($form, $form_state, $form['#user']);
410 }
411 }
412 if (isset($form['#id']) && $form['#id'] == 'node-form') {
413 if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
414 $form['language'] = array(
415 '#type' => 'select',
416 '#title' => t('Language'),
417 '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
418 '#options' => array('' => t('Language neutral')) + locale_language_list('name'),
419 );
420 }
421 // Node type without language selector: assign the default for new nodes
422 elseif (!isset($form['#node']->nid)) {
423 $default = language_default();
424 $form['language'] = array(
425 '#type' => 'value',
426 '#value' => $default->language
427 );
428 }
429 $form['#submit'][] = 'locale_field_node_form_submit';
430 }
431 }
432
433 /**
434 * Form submit handler for node_form().
435 *
436 * Check if Locale is registered as a translation handler and handle possible
437 * node language changes.
438 */
439 function locale_field_node_form_submit($form, &$form_state) {
440 if (field_multilingual_check_translation_handlers('node', 'locale')) {
441 module_load_include('inc', 'locale', 'locale.field');
442 locale_field_node_form_update_field_language($form, $form_state);
443 }
444 }
445
446 /**
447 * Implement hook_theme().
448 */
449 function locale_theme() {
450 return array(
451 'locale_languages_overview_form' => array(
452 'render element' => 'form',
453 ),
454 'locale_languages_configure_form' => array(
455 'render element' => 'form',
456 ),
457 'locale_translation_filters' => array(
458 'render element' => 'form',
459 ),
460 'locale_date_format_form' => array(
461 'render element' => 'form',
462 ),
463 );
464 }
465
466 /**
467 * Implement hook_field_attach_view_alter().
468 */
469 function locale_field_attach_view_alter(&$output, $context) {
470 // In locale_field_fallback_view() we might call field_attach_view(). The
471 // static variable avoids unnecessary recursion.
472 static $recursion;
473
474 // Do not apply fallback rules if disabled or if Locale is not registered as a
475 // translation handler.
476 if (!$recursion && variable_get('locale_field_fallback_view', TRUE) && field_multilingual_check_translation_handlers($context['obj_type'], 'locale')) {
477 $recursion = TRUE;
478 module_load_include('inc', 'locale', 'locale.field');
479 locale_field_fallback_view($output, $context);
480 $recursion = FALSE;
481 }
482 }
483
484 /**
485 * Implement hook_entity_info_alter().
486 */
487 function locale_entity_info_alter(&$entity_info) {
488 $enabled = drupal_multilingual();
489 foreach ($entity_info as $type => $info) {
490 $entity_info[$type]['translation']['locale'] = $enabled;
491 }
492 }
493
494 /**
495 * Implement hook_language_types_info().
496 */
497 function locale_language_types_info() {
498 return array(
499 LANGUAGE_TYPE_CONTENT => array(
500 'name' => t('Content'),
501 'description' => t('If a piece of content is available in multiple languages, the one matching the <em>content</em> language will be used.'),
502 ),
503 LANGUAGE_TYPE_INTERFACE => array(
504 'name' => t('Interface'),
505 'description' => t('The interface labels will be displayed in the <em>interface</em> language.'),
506 ),
507 LANGUAGE_TYPE_URL => array(
508 'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL),
509 ),
510 );
511 }
512
513 /**
514 * Implement hook_language_negotiation_info().
515 */
516 function locale_language_negotiation_info() {
517 $file = 'includes/locale.inc';
518 $providers = array();
519
520 $providers[LOCALE_LANGUAGE_NEGOTIATION_URL] = array(
521 'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL),
522 'callbacks' => array(
523 'language' => 'locale_language_from_url',
524 'switcher' => 'locale_language_switcher_url',
525 'url_rewrite' => 'locale_language_url_rewrite_url',
526 ),
527 'file' => $file,
528 'weight' => -8,
529 'name' => t('URL'),
530 'description' => t('The language is determined from the URL (Path prefix or domain).'),
531 'config' => 'admin/config/regional/language/configure/url',
532 );
533
534 $providers[LOCALE_LANGUAGE_NEGOTIATION_SESSION] = array(
535 'callbacks' => array(
536 'language' => 'locale_language_from_session',
537 'switcher' => 'locale_language_switcher_session',
538 'url_rewrite' => 'locale_language_url_rewrite_session',
539 ),
540 'file' => $file,
541 'weight' => -6,
542 'name' => t('Session'),
543 'description' => t('The language is determined from a request/session parameter.'),
544 'config' => 'admin/config/regional/language/configure/session',
545 );
546
547 $providers[LOCALE_LANGUAGE_NEGOTIATION_USER] = array(
548 'callbacks' => array('language' => 'locale_language_from_user'),
549 'file' => $file,
550 'weight' => -4,
551 'name' => t('User'),
552 'description' => t('The language is determined from the language preference set in the user account.'),
553 );
554
555 $providers[LOCALE_LANGUAGE_NEGOTIATION_BROWSER] = array(
556 'callbacks' => array('language' => 'locale_language_from_browser'),
557 'name' => $file,
558 'weight' => -2,
559 'cache' => CACHE_DISABLED,
560 'name' => t('Browser'),
561 'description' => t('The language is determined from the browser\'s language settings.'),
562 );
563
564 $providers[LOCALE_LANGUAGE_NEGOTIATION_CONTENT] = array(
565 'types' => array(LANGUAGE_TYPE_INTERFACE),
566 'callbacks' => array('language' => 'locale_language_from_content'),
567 'file' => $file,
568 'weight' => 8,
569 'name' => t('Content'),
570 'description' => t('The interface language is the same as the negotiated content language.'),
571 );
572
573 return $providers;
574 }
575
576 // ---------------------------------------------------------------------------------
577 // Locale core functionality
578
579 /**
580 * Provides interface translation services.
581 *
582 * This function is called from t() to translate a string if needed.
583 *
584 * @param $string
585 * A string to look up translation for. If omitted, all the
586 * cached strings will be returned in all languages already
587 * used on the page.
588 * @param $context
589 * The context of this string.
590 * @param $langcode
591 * Language code to use for the lookup.
592 * @param $reset
593 * Set to TRUE to reset the in-memory cache.
594 */
595 function locale($string = NULL, $context = NULL, $langcode = NULL, $reset = FALSE) {
596 global $language;
597 static $locale_t;
598
599 if ($reset) {
600 // Reset in-memory cache.
601 $locale_t = NULL;
602 }
603
604 if (!isset($string)) {
605 // Return all cached strings if no string was specified
606 return $locale_t;
607 }
608
609 $langcode = isset($langcode) ? $langcode : $language->language;
610
611 // Store database cached translations in a static var.
612 if (!isset($locale_t[$langcode])) {
613 $locale_t[$langcode] = array();
614 // Disabling the usage of string caching allows a module to watch for
615 // the exact list of strings used on a page. From a performance
616 // perspective that is a really bad idea, so we have no user
617 // interface for this. Be careful when turning this option off!
618 if (variable_get('locale_cache_strings', 1) == 1) {
619 if ($cache = cache_get('locale:' . $langcode, 'cache')) {
620 $locale_t[$langcode] = $cache->data;
621 }
622 elseif (lock_acquire('locale_cache_' . $langcode)) {
623 // Refresh database stored cache of translations for given language.
624 // We only store short strings used in current version, to improve
625 // performance and consume less memory.
626 $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < 75", array(':language' => $langcode, ':version' => VERSION));
627 foreach ($result as $data) {
628 $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation);
629 }
630 cache_set('locale:' . $langcode, $locale_t[$langcode]);
631 lock_release('locale_cache_' . $langcode);
632 }
633 }
634 }
635
636 // If we have the translation cached, skip checking the database
637 if (!isset($locale_t[$langcode][$context][$string])) {
638
639 // We do not have this translation cached, so get it from the DB.
640 $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default'", array(
641 ':language' => $langcode,
642 ':source' => $string,
643 ':context' => (string) $context,
644 ))->fetchObject();
645 if ($translation) {
646 // We have the source string at least.
647 // Cache translation string or TRUE if no translation exists.
648 $locale_t[$langcode][$context][$string] = (empty($translation->translation) ? TRUE : $translation->translation);
649
650 if ($translation->version != VERSION) {
651 // This is the first use of this string under current Drupal version. Save version
652 // and clear cache, to include the string into caching next time. Saved version is
653 // also a string-history information for later pruning of the tables.
654 db_update('locales_source')
655 ->fields(array('version' => VERSION))
656 ->condition('lid', $translation->lid)
657 ->execute();
658 cache_clear_all('locale:', 'cache', TRUE);
659 }
660 }
661 else {
662 // We don't have the source string, cache this as untranslated.
663 db_insert('locales_source')
664 ->fields(array(
665 'location' => request_uri(),
666 'source' => $string,
667 'context' => (string) $context,
668 'textgroup' => 'default',
669 'version' => VERSION,
670 ))
671 ->execute();
672 $locale_t[$langcode][$context][$string] = TRUE;
673 // Clear locale cache so this string can be added in a later request.
674 cache_clear_all('locale:', 'cache', TRUE);
675 }
676 }
677
678 return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]);
679 }
680
681 /**
682 * Returns plural form index for a specific number.
683 *
684 * The index is computed from the formula of this language.
685 *
686 * @param $count
687 * Number to return plural for.
688 * @param $langcode
689 * Optional language code to translate to a language other than
690 * what is used to display the page.
691 */
692 function locale_get_plural($count, $langcode = NULL) {
693 global $language;
694 static $locale_formula, $plurals = array();
695
696 $langcode = $langcode ? $langcode : $language->language;
697
698 if (!isset($plurals[$langcode][$count])) {
699 if (!isset($locale_formula)) {
700 $language_list = language_list();
701 $locale_formula[$langcode] = $language_list[$langcode]->formula;
702 }
703 if ($locale_formula[$langcode]) {
704 $n = $count;
705 $plurals[$langcode][$count] = @eval('return intval(' . $locale_formula[$langcode] . ');');
706 return $plurals[$langcode][$count];
707 }
708 else {
709 $plurals[$langcode][$count] = -1;
710 return -1;
711 }
712 }
713 return $plurals[$langcode][$count];
714 }
715
716
717 /**
718 * Returns a language name
719 */
720 function locale_language_name($lang) {
721 static $list = NULL;
722 if (!isset($list)) {
723 $list = locale_language_list();
724 }
725 return ($lang && isset($list[$lang])) ? $list[$lang] : t('All');
726 }
727
728 /**
729 * Returns array of language names
730 *
731 * @param $field
732 * 'name' => names in current language, localized
733 * 'native' => native names
734 * @param $all
735 * Boolean to return all languages or only enabled ones
736 */
737 function locale_language_list($field = 'name', $all = FALSE) {
738 if ($all) {
739 $languages = language_list();
740 }
741 else {
742 $languages = language_list('enabled');
743 $languages = $languages[1];
744 }
745 $list = array();
746 foreach ($languages as $language) {
747 $list[$language->language] = ($field == 'name') ? t($language->name) : $language->$field;
748 }
749 return $list;
750 }
751
752 /**
753 * Imports translations when new modules or themes are installed or enabled.
754 *
755 * This function will either import translation for the component change
756 * right away, or start a batch if more files need to be imported.
757 *
758 * @param $components
759 * An array of component (theme and/or module) names to import
760 * translations for.
761 */
762 function locale_system_update($components) {
763 include_once DRUPAL_ROOT . '/includes/locale.inc';
764 if ($batch = locale_batch_by_component($components)) {
765 batch_set($batch);
766 }
767 }
768
769 /**
770 * Implement hook_js_alter().
771 *
772 * This function checks all JavaScript files currently added via drupal_add_js()
773 * and invokes parsing if they have not yet been parsed for Drupal.t()
774 * and Drupal.formatPlural() calls. Also refreshes the JavaScript translation
775 * file if necessary, and adds it to the page.
776 */
777 function locale_js_alter(&$javascript) {
778 global $language;
779
780 $dir = 'public://' . variable_get('locale_js_directory', 'languages');
781 $parsed = variable_get('javascript_parsed', array());
782 $files = $new_files = FALSE;
783
784 foreach ($javascript as $item) {
785 if ($item['type'] == 'file') {
786 $files = TRUE;
787 $filepath = $item['data'];
788 if (!in_array($filepath, $parsed)) {
789 // Don't parse our own translations files.
790 if (substr($filepath, 0, strlen($dir)) != $dir) {
791 locale_inc_callback('_locale_parse_js_file', $filepath);
792 watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath));
793 $parsed[] = $filepath;
794 $new_files = TRUE;
795 }
796 }
797 }
798 }
799
800 // If there are any new source files we parsed, invalidate existing
801 // JavaScript translation files for all languages, adding the refresh
802 // flags into the existing array.
803 if ($new_files) {
804 $parsed += locale_inc_callback('_locale_invalidate_js');
805 }
806
807 // If necessary, rebuild the translation file for the current language.
808 if (!empty($parsed['refresh:' . $language->language])) {
809 // Don't clear the refresh flag on failure, so that another try will
810 // be performed later.
811 if (locale_inc_callback('_locale_rebuild_js')) {
812 unset($parsed['refresh:' . $language->language]);
813 }
814 // Store any changes after refresh was attempted.
815 variable_set('javascript_parsed', $parsed);
816 }
817 // If no refresh was attempted, but we have new source files, we need
818 // to store them too. This occurs if current page is in English.
819 elseif ($new_files) {
820 variable_set('javascript_parsed', $parsed);
821 }
822
823 // Add the translation JavaScript file to the page.
824 if ($files && !empty($language->javascript)) {
825 // Add the translation JavaScript file to the page.
826 $file = $dir . '/' . $language->language . '_' . $language->javascript . '.js';
827 $javascript[$file] = drupal_js_defaults($file);
828 }
829 }
830
831 /*
832 * Implement hook_css_alter().
833 *
834 * This function checks all CSS files currently added via drupal_add_css() and
835 * and checks to see if a related right to left CSS file should be included.
836 */
837 function locale_css_alter(&$css) {
838 global $language;
839
840 // If the current language is RTL, add the CSS file with the RTL overrides.
841 if ($language->direction == LANGUAGE_RTL) {
842 foreach ($css as $data => $item) {
843 // Only provide RTL overrides for files.
844 if ($item['type'] == 'file') {
845 $rtl_path = str_replace('.css', '-rtl.css', $item['data']);
846 if (file_exists($rtl_path) && !isset($css[$rtl_path])) {
847 // Replicate the same item, but with the RTL path and a little larger
848 // weight so that it appears directly after the original CSS file.
849 $item['data'] = $rtl_path;
850 $item['weight'] += 0.01;
851 $css[$rtl_path] = $item;
852 }
853 }
854 }
855 }
856 }
857
858 // ---------------------------------------------------------------------------------
859 // Language switcher block
860
861 /**
862 * Implement hook_block_info().
863 */
864 function locale_block_info() {
865 include_once DRUPAL_ROOT . '/includes/language.inc';
866 $block = array();
867 $info = language_types_info();
868 foreach (language_types_configurable() as $type) {
869 $block[$type] = array(
870 'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])),
871 // Not worth caching.
872 'cache' => DRUPAL_NO_CACHE,
873 );
874 }
875 return $block;
876 }
877
878 /**
879 * Implement hook_block_view().
880 *
881 * Displays a language switcher. Only show if we have at least two languages.
882 */
883 function locale_block_view($type) {
884 if (drupal_multilingual()) {
885 $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
886 $links = language_negotiation_get_switch_links($type, $path);
887
888 if (isset($links->links) && count($links->links > 1)) {
889 $class = "language-switcher-{$links->provider}";
890 $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
891 $block['content'] = theme('links', $variables);
892 $block['subject'] = t('Languages');
893 return $block;
894 }
895 }
896 }
897
898 /**
899 * Theme locale translation filter selector.
900 *
901 * @ingroup themeable
902 */
903 function theme_locale_translation_filters($variables) {
904 $form = $variables['form'];
905 $output = '';
906
907 foreach (element_children($form['status']) as $key) {
908 $output .= drupal_render($form['status'][$key]);
909 }
910 $output .= '<div id="locale-translation-buttons">' . drupal_render($form['buttons']) . '</div>';
911 return $output;
912 }
913
914 /**
915 * Theme locale date format form.
916 *
917 * @ingroup themeable
918 */
919 function theme_locale_date_format_form($variables) {
920 $form = $variables['form'];
921 $header = array(
922 t('Date type'),
923 t('Format'),
924 );
925
926 foreach (element_children($form['date_formats']) as $key) {
927 $row = array();
928 $row[] = $form['date_formats'][$key]['#title'];
929 unset($form['date_formats'][$key]['#title']);
930 $row[] = array('data' => drupal_render($form['date_formats'][$key]));
931 $rows[] = $row;
932 }
933
934 $output = drupal_render($form['language']);
935 $output .= theme('table', array('header' => $header, 'rows' => $rows));
936 $output .= drupal_render_children($form);
937
938 return $output;
939 }
940
941 /**
942 * Display edit date format links for each language.
943 */
944 function locale_date_format_language_overview_page() {
945 $header = array(
946 t('Language'),
947 array('data' => t('Operations'), 'colspan' => '2'),
948 );
949
950 // Get list of languages.
951 $languages = locale_language_list('native');
952
953 foreach ($languages as $langcode => $info) {
954 $row = array();
955 $row[] = $languages[$langcode];
956 $row[] = l(t('edit'), 'admin/config/regional/date-time/locale/' . $langcode . '/edit');
957 $row[] = l(t('reset'), 'admin/config/regional/date-time/locale/' . $langcode . '/reset');
958 $rows[] = $row;
959 }
960
961 return theme('table', array('header' => $header, 'rows' => $rows));
962 }
963
964 /**
965 * Provide date localization configuration options to users.
966 */
967 function locale_date_format_form($form, &$form_state, $langcode) {
968 $languages = locale_language_list('native');
969 $language_name = $languages[$langcode];
970
971 // Display the current language name.
972 $form['language'] = array(
973 '#type' => 'item',
974 '#title' => t('Language'),
975 '#markup' => check_plain($language_name),
976 '#weight' => -10,
977 );
978 $form['langcode'] = array(
979 '#type' => 'value',
980 '#value' => $langcode,
981 );
982
983 // Get list of date format types.
984 $types = system_get_date_types();
985
986 // Get list of available formats.
987 $formats = system_get_date_formats();
988 $choices = array();
989 foreach ($formats as $type => $list) {
990 foreach ($list as $f => $format) {
991 $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
992 }
993 }
994
995 // Get configured formats for each language.
996 $locale_formats = system_date_format_locale($langcode);
997 // Display a form field for each format type.
998 foreach ($types as $type => $type_info) {
999 if (!empty($locale_formats) && in_array($type, array_keys($locale_formats))) {
1000 $default = $locale_formats[$type];
1001 }
1002 else {
1003 $default = variable_get('date_format_' . $type, array_shift(array_keys($formats)));
1004 }
1005
1006 // Show date format select list.
1007 $form['date_formats']['date_format_' . $type] = array(
1008 '#type' => 'select',
1009 '#title' => check_plain($type_info['title']),
1010 '#attributes' => array('class' => array('date-format')),
1011 '#default_value' => (isset($choices[$default]) ? $default : 'custom'),
1012 '#options' => $choices,
1013 );
1014 }
1015
1016 $form['buttons']['submit'] = array(
1017 '#type' => 'submit',
1018 '#value' => t('Save configuration'),
1019 );
1020
1021 return $form;
1022 }
1023
1024 /**
1025 * Submit handler for configuring localized date formats on the locale_date_format_form.
1026 */
1027 function locale_date_format_form_submit($form, &$form_state) {
1028 include_once DRUPAL_ROOT . '/includes/locale.inc';
1029 $langcode = $form_state['values']['langcode'];
1030
1031 // Get list of date format types.
1032 $types = system_get_date_types();
1033 foreach ($types as $type => $type_info) {
1034 $format = $form_state['values']['date_format_' . $type];
1035 if ($format == 'custom') {
1036 $format = $form_state['values']['date_format_' . $type . '_custom'];
1037 }
1038 locale_date_format_save($langcode, $type, $format);
1039 }
1040 drupal_set_message(t('Configuration saved.'));
1041 $form_state['redirect'] = 'admin/config/regional/date-time/locale';
1042 }
1043
1044 /**
1045 * Reset locale specific date formats to the global defaults.
1046 *
1047 * @param $langcode
1048 * Language code, e.g. 'en'.
1049 */
1050 function locale_date_format_reset_form($form, &$form_state, $langcode) {
1051 $form['langcode'] = array('#type' => 'value', '#value' => $langcode);
1052 $languages = language_list();
1053 return confirm_form($form,
1054 t('Are you sure you want to reset the date formats for %language to the global defaults?', array('%language' => $languages[$langcode]->name)),
1055 'admin/config/regional/date-time/locale',
1056 t('Resetting will remove all localized date formats for this language. This action cannot be undone.'),
1057 t('Reset'), t('Cancel'));
1058 }
1059
1060 /**
1061 * Reset date formats for a specific language to global defaults.
1062 */
1063 function locale_date_format_reset_form_submit($form, &$form_state) {
1064 db_delete('date_format_locale')
1065 ->condition('language', $form_state['values']['langcode'])
1066 ->execute();
1067 $form_state['redirect'] = 'admin/config/regional/date-time/locale';
1068 }
1069
1070 /**
1071 * Implement hook_url_outbound_alter().
1072 *
1073 * Rewrite outbound URLs with language based prefixes.
1074 */
1075 function locale_url_outbound_alter(&$path, &$options, $original_path) {
1076 // Only modify internal URLs.
1077 if (!$options['external']) {
1078 static $callbacks;
1079
1080 if (!isset($callbacks)) {
1081 $callbacks = array();
1082 include_once DRUPAL_ROOT . '/includes/language.inc';
1083
1084 foreach (language_types_configurable() as $type) {
1085 // Get url rewriter callbacks only from enabled language providers.
1086 $negotiation = variable_get("language_negotiation_$type", array());
1087
1088 foreach ($negotiation as $id => $provider) {
1089 if (isset($provider['file'])) {
1090 require_once DRUPAL_ROOT . '/' . $provider['file'];
1091 }
1092
1093 // Avoid duplicate callback entries.
1094 if (isset($provider['callbacks']['url_rewrite'])) {
1095 $callbacks[$provider['callbacks']['url_rewrite']] = NULL;
1096 }
1097 }
1098 }
1099
1100 $callbacks = array_keys($callbacks);
1101 }
1102
1103 foreach ($callbacks as $callback) {
1104 $callback($path, $options);
1105 }
1106 }
1107 }

  ViewVC Help
Powered by ViewVC 1.1.2