[ Index ] |
|
Code source de Drupal 5.3 |
1 <?php 2 // $Id: locale.inc,v 1.105.2.4 2007/05/21 00:20:02 drumm Exp $ 3 4 /** 5 * @file 6 * Admin-related functions for locale.module. 7 */ 8 9 // --------------------------------------------------------------------------------- 10 // Language addition functionality (administration only) 11 12 /** 13 * Helper function to add a language 14 */ 15 function _locale_add_language($code, $name, $onlylanguage = TRUE) { 16 db_query("INSERT INTO {locales_meta} (locale, name) VALUES ('%s','%s')", $code, $name); 17 $result = db_query("SELECT lid FROM {locales_source}"); 18 while ($string = db_fetch_object($result)) { 19 db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d,'%s', '')", $string->lid, $code); 20 } 21 22 // If only the language was added, and not a PO file import triggered 23 // the language addition, we need to inform the user on how to start 24 // a translation 25 if ($onlylanguage) { 26 drupal_set_message(t('The language %locale has been created and can now be used to import a translation. More information is available in the <a href="@locale-help">help screen</a>.', array('%locale' => t($name), '@locale-help' => url('admin/help/locale')))); 27 } 28 else { 29 drupal_set_message(t('The language %locale has been created.', array('%locale' => t($name)))); 30 } 31 32 watchdog('locale', t('The %language language (%locale) has been created.', array('%language' => $name, '%locale' => $code))); 33 } 34 35 /** 36 * User interface for the language management screen. 37 */ 38 function _locale_admin_manage_screen() { 39 $languages = locale_supported_languages(TRUE, TRUE); 40 41 $options = array(); 42 $form['name'] = array('#tree' => TRUE); 43 foreach ($languages['name'] as $key => $lang) { 44 $options[$key] = ''; 45 $status = db_fetch_object(db_query("SELECT isdefault, enabled FROM {locales_meta} WHERE locale = '%s'", $key)); 46 if ($status->enabled) { 47 $enabled[] = $key; 48 } 49 if ($status->isdefault) { 50 $isdefault = $key; 51 } 52 if ($key == 'en') { 53 $form['name']['en'] = array('#value' => check_plain($lang)); 54 } 55 else { 56 $original = db_fetch_object(db_query("SELECT COUNT(*) AS strings FROM {locales_source}")); 57 $translation = db_fetch_object(db_query("SELECT COUNT(*) AS translation FROM {locales_target} WHERE locale = '%s' AND translation != ''", $key)); 58 59 $ratio = ($original->strings > 0 && $translation->translation > 0) ? round(($translation->translation/$original->strings)*100., 2) : 0; 60 61 $form['name'][$key] = array('#type' => 'textfield', 62 '#default_value' => $lang, 63 '#size' => 15, 64 '#maxlength' => 64, 65 ); 66 $form['translation'][$key] = array('#value' => "$translation->translation/$original->strings ($ratio%)"); 67 } 68 } 69 $form['enabled'] = array('#type' => 'checkboxes', 70 '#options' => $options, 71 '#default_value' => $enabled, 72 ); 73 $form['site_default'] = array('#type' => 'radios', 74 '#options' => $options, 75 '#default_value' => $isdefault, 76 ); 77 $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); 78 $form['#base'] = 'locale_admin_manage_screen'; 79 80 return $form; 81 } 82 83 /** 84 * Theme the locale admin manager form. 85 */ 86 function theme_locale_admin_manage_screen($form) { 87 foreach ($form['name'] as $key => $element) { 88 // Do not take form control structures. 89 if (is_array($element) && element_child($key)) { 90 $rows[] = array(check_plain($key), drupal_render($form['name'][$key]), drupal_render($form['enabled'][$key]), drupal_render($form['site_default'][$key]), ($key != 'en' ? drupal_render($form['translation'][$key]) : t('n/a')), ($key != 'en' ? l(t('delete'), 'admin/settings/locale/language/delete/'. $key) : '')); 91 } 92 } 93 $header = array(array('data' => t('Code')), array('data' => t('English name')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Translated')), array('data' => t('Operations'))); 94 $output = theme('table', $header, $rows); 95 $output .= drupal_render($form); 96 97 return $output; 98 } 99 100 /** 101 * Process locale admin manager form submissions. 102 */ 103 function _locale_admin_manage_screen_submit($form_id, $form_values) { 104 // Save changes to existing languages. 105 $languages = locale_supported_languages(FALSE, TRUE); 106 foreach ($languages['name'] as $key => $value) { 107 if ($form_values['site_default'] == $key) { 108 $form_values['enabled'][$key] = 1; // autoenable the default language 109 } 110 $enabled = $form_values['enabled'][$key] ? 1 : 0; 111 if ($key == 'en') { 112 // Disallow name change for English locale. 113 db_query("UPDATE {locales_meta} SET isdefault = %d, enabled = %d WHERE locale = 'en'", ($form_values['site_default'] == $key), $enabled); 114 } 115 else { 116 db_query("UPDATE {locales_meta} SET name = '%s', isdefault = %d, enabled = %d WHERE locale = '%s'", $form_values['name'][$key], ($form_values['site_default'] == $key), $enabled, $key); 117 } 118 } 119 drupal_set_message(t('Configuration saved.')); 120 121 // Changing the locale settings impacts the interface: 122 cache_clear_all('*', 'cache_menu', TRUE); 123 cache_clear_all('*', 'cache_page', TRUE); 124 125 return 'admin/settings/locale/language/overview'; 126 } 127 128 function locale_add_language_form() { 129 $isocodes = _locale_prepare_iso_list(); 130 $form = array(); 131 $form['language list'] = array('#type' => 'fieldset', 132 '#title' => t('Language list'), 133 '#collapsible' => TRUE, 134 ); 135 $form['language list']['langcode'] = array('#type' => 'select', 136 '#title' => t('Language name'), 137 '#default_value' => key($isocodes), 138 '#options' => $isocodes, 139 '#description' => t('Select your language here, or add it below, if you are unable to find it.'), 140 ); 141 $form['language list']['submit'] = array('#type' => 'submit', '#value' => t('Add language')); 142 return $form; 143 } 144 145 function locale_custom_language_form() { 146 $form = array(); 147 $form['custom language'] = array('#type' => 'fieldset', 148 '#title' => t('Custom language'), 149 '#collapsible' => TRUE, 150 ); 151 $form['custom language']['langcode'] = array('#type' => 'textfield', 152 '#title' => t('Language code'), 153 '#size' => 12, 154 '#maxlength' => 60, 155 '#required' => TRUE, 156 '#description' => t('Commonly this is an <a href="@iso-codes">ISO 639 language code</a> with an optional country code for regional variants. Examples include "en", "en-US" and "zh-cn".', array('@iso-codes' => 'http://www.w3.org/WAI/ER/IG/ert/iso639.htm')), 157 ); 158 $form['custom language']['langname'] = array('#type' => 'textfield', 159 '#title' => t('Language name in English'), 160 '#maxlength' => 64, 161 '#required' => TRUE, 162 '#description' => t('Name of the language. Will be available for translation in all languages.'), 163 ); 164 $form['custom language']['submit'] = array('#type' => 'submit', '#value' => t('Add custom language')); 165 // Use the validation and submit functions of the add language form. 166 $form['#base'] = 'locale_add_language_form'; 167 return $form; 168 } 169 170 /** 171 * User interface for the language addition screen. 172 */ 173 function _locale_admin_manage_add_screen() { 174 $output = drupal_get_form('locale_add_language_form'); 175 $output .= drupal_get_form('locale_custom_language_form'); 176 return $output; 177 } 178 179 /** 180 * Validate the language addition form. 181 */ 182 function locale_add_language_form_validate($form_id, $form_values) { 183 if ($duplicate = db_num_rows(db_query("SELECT locale FROM {locales_meta} WHERE locale = '%s'", $form_values['langcode'])) != 0) { 184 form_set_error(t('The language %language (%code) already exists.', array('%language' => $form_values['langname'], '%code' => $form_values['langcode']))); 185 } 186 187 if (!isset($form_values['langname'])) { 188 $isocodes = _locale_get_iso639_list(); 189 if (!isset($isocodes[$form_values['langcode']])) { 190 form_set_error('langcode', t('Invalid language code.')); 191 } 192 } 193 } 194 195 /** 196 * Process the language addition form submission. 197 */ 198 function locale_add_language_form_submit($form_id, $form_values) { 199 if (isset($form_values['langname'])) { 200 // Custom language form. 201 _locale_add_language($form_values['langcode'], $form_values['langname']); 202 } 203 else { 204 $isocodes = _locale_get_iso639_list(); 205 _locale_add_language($form_values['langcode'], $isocodes[$form_values['langcode']][0]); 206 } 207 208 return 'admin/settings/locale'; 209 } 210 211 /** 212 * User interface for the translation import screen. 213 */ 214 function _locale_admin_import() { 215 $languages = locale_supported_languages(FALSE, TRUE); 216 $languages = array_map('t', $languages['name']); 217 unset($languages['en']); 218 219 if (!count($languages)) { 220 $languages = _locale_prepare_iso_list(); 221 } 222 else { 223 $languages = array( 224 t('Already added languages') => $languages, 225 t('Languages not yet added') => _locale_prepare_iso_list() 226 ); 227 } 228 229 $form = array(); 230 $form['import'] = array('#type' => 'fieldset', 231 '#title' => t('Import translation'), 232 ); 233 $form['import']['file'] = array('#type' => 'file', 234 '#title' => t('Language file'), 235 '#size' => 50, 236 '#description' => t('A gettext Portable Object (.po) file.'), 237 ); 238 $form['import']['langcode'] = array('#type' => 'select', 239 '#title' => t('Import into'), 240 '#options' => $languages, 241 '#description' => t('Choose the language you want to add strings into. If you choose a language which is not yet set up, then it will be added.'), 242 ); 243 $form['import']['mode'] = array('#type' => 'radios', 244 '#title' => t('Mode'), 245 '#default_value' => 'overwrite', 246 '#options' => array('overwrite' => t('Strings in the uploaded file replace existing ones, new ones are added'), 'keep' => t('Existing strings are kept, only new strings are added')), 247 ); 248 $form['import']['submit'] = array('#type' => 'submit', '#value' => t('Import')); 249 $form['#attributes']['enctype'] = 'multipart/form-data'; 250 251 return $form; 252 } 253 254 /** 255 * Process the locale import form submission. 256 */ 257 function _locale_admin_import_submit($form_id, $form_values) { 258 // Add language, if not yet supported 259 $languages = locale_supported_languages(TRUE, TRUE); 260 if (!isset($languages['name'][$form_values['langcode']])) { 261 $isocodes = _locale_get_iso639_list(); 262 _locale_add_language($form_values['langcode'], $isocodes[$form_values['langcode']][0], FALSE); 263 } 264 265 // Now import strings into the language 266 $file = file_check_upload('file'); 267 if ($ret = _locale_import_po($file, $form_values['langcode'], $form_values['mode']) == FALSE) { 268 $message = t('The translation import of %filename failed.', array('%filename' => $file->filename)); 269 drupal_set_message($message, 'error'); 270 watchdog('locale', $message, WATCHDOG_ERROR); 271 } 272 273 return 'admin/settings/locale'; 274 } 275 276 function _locale_export_po_form($languages) { 277 $form['export'] = array('#type' => 'fieldset', 278 '#title' => t('Export translation'), 279 '#collapsible' => TRUE, 280 ); 281 $form['export']['langcode'] = array('#type' => 'select', 282 '#title' => t('Language name'), 283 '#options' => $languages, 284 '#description' => t('Select the language you would like to export in gettext Portable Object (.po) format.'), 285 ); 286 $form['export']['submit'] = array('#type' => 'submit', '#value' => t('Export')); 287 return $form; 288 } 289 290 function _locale_export_pot_form() { 291 // Complete template export of the strings 292 $form['export'] = array('#type' => 'fieldset', 293 '#title' => t('Export template'), 294 '#collapsible' => TRUE, 295 '#description' => t('Generate a gettext Portable Object Template (.pot) file with all the interface strings from the Drupal locale database.'), 296 ); 297 $form['export']['submit'] = array('#type' => 'submit', '#value' => t('Export')); 298 $form['#base'] = '_locale_export_po_form'; 299 return $form; 300 } 301 302 /** 303 * User interface for the translation export screen 304 */ 305 function _locale_admin_export_screen() { 306 $languages = locale_supported_languages(FALSE, TRUE); 307 $languages = array_map('t', $languages['name']); 308 unset($languages['en']); 309 310 $output = ''; 311 // Offer language specific export if any language is set up 312 if (count($languages)) { 313 $output = drupal_get_form('_locale_export_po_form', $languages); 314 } 315 316 $output .= drupal_get_form('_locale_export_pot_form'); 317 318 return $output; 319 } 320 321 /** 322 * Process a locale export form submissions. 323 */ 324 function _locale_export_po_form_submit($form_id, $form_values) { 325 _locale_export_po($form_values['langcode']); 326 } 327 328 /** 329 * User interface for the string search screen 330 */ 331 function _locale_string_seek_form() { 332 // Get *all* languages set up 333 $languages = locale_supported_languages(FALSE, TRUE); 334 asort($languages['name']); unset($languages['name']['en']); 335 $languages['name'] = array_map('check_plain', $languages['name']); 336 337 // Present edit form preserving previous user settings 338 $query = _locale_string_seek_query(); 339 $form = array(); 340 $form['search'] = array('#type' => 'fieldset', 341 '#title' => t('Search'), 342 ); 343 $form['search']['string'] = array('#type' => 'textfield', 344 '#title' => t('Strings to search for'), 345 '#default_value' => $query->string, 346 '#size' => 30, 347 '#maxlength' => 30, 348 '#description' => t('Leave blank to show all strings. The search is case sensitive.'), 349 ); 350 $form['search']['language'] = array('#type' => 'radios', 351 '#title' => t('Language'), 352 '#default_value' => ($query->language ? $query->language : 'all'), 353 '#options' => array_merge(array('all' => t('All languages'), 'en' => t('English (provided by Drupal)')), $languages['name']), 354 ); 355 $form['search']['searchin'] = array('#type' => 'radios', 356 '#title' => t('Search in'), 357 '#default_value' => ($query->searchin ? $query->searchin : 'all'), 358 '#options' => array('all' => t('All strings in that language'), 'translated' => t('Only translated strings'), 'untranslated' => t('Only untranslated strings')), 359 ); 360 $form['search']['submit'] = array('#type' => 'submit', '#value' => t('Search')); 361 $form['#redirect'] = FALSE; 362 363 return $form; 364 } 365 366 /** 367 * User interface for string editing. 368 */ 369 function _locale_string_edit($lid) { 370 $languages = locale_supported_languages(FALSE, TRUE); 371 unset($languages['name']['en']); 372 373 $result = db_query('SELECT DISTINCT s.source, t.translation, t.locale FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.lid = %d', $lid); 374 $form = array(); 375 $form['translations'] = array('#tree' => TRUE); 376 while ($translation = db_fetch_object($result)) { 377 $orig = $translation->source; 378 379 // Approximate the number of rows in a textfield with a maximum of 10. 380 $rows = min(ceil(str_word_count($orig) / 12), 10); 381 382 $form['translations'][$translation->locale] = array( 383 '#type' => 'textarea', 384 '#title' => $languages['name'][$translation->locale], 385 '#default_value' => $translation->translation, 386 '#rows' => $rows, 387 ); 388 unset($languages['name'][$translation->locale]); 389 } 390 391 // Handle erroneous lid. 392 if (!isset($orig)){ 393 drupal_set_message(t('String not found.')); 394 drupal_goto('admin/settings/locale/string/search'); 395 } 396 397 // Add original text. Assign negative weight so that it floats to the top. 398 $form['item'] = array('#type' => 'item', 399 '#title' => t('Original text'), 400 '#value' => check_plain(wordwrap($orig, 0)), 401 '#weight' => -1, 402 ); 403 404 foreach ($languages['name'] as $key => $lang) { 405 $form['translations'][$key] = array( 406 '#type' => 'textarea', 407 '#title' => $lang, 408 '#rows' => $rows, 409 ); 410 } 411 412 $form['lid'] = array('#type' => 'value', '#value' => $lid); 413 $form['submit'] = array('#type' => 'submit', '#value' => t('Save translations')); 414 415 return $form; 416 } 417 418 /** 419 * Process string editing form submissions. 420 * Saves all translations of one string submitted from a form. 421 */ 422 function _locale_string_edit_submit($form_id, $form_values) { 423 $lid = $form_values['lid']; 424 foreach ($form_values['translations'] as $key => $value) { 425 $trans = db_fetch_object(db_query("SELECT translation FROM {locales_target} WHERE lid = %d AND locale = '%s'", $lid, $key)); 426 if (isset($trans->translation)) { 427 db_query("UPDATE {locales_target} SET translation = '%s' WHERE lid = %d AND locale = '%s'", $value, $lid, $key); 428 } 429 else { 430 db_query("INSERT INTO {locales_target} (lid, translation, locale) VALUES (%d, '%s', '%s')", $lid, $value, $key); 431 } 432 } 433 drupal_set_message(t('The string has been saved.')); 434 435 // Refresh the locale cache. 436 locale_refresh_cache(); 437 // Rebuild the menu, strings may have changed. 438 menu_rebuild(); 439 440 return 'admin/settings/locale/string/search'; 441 } 442 443 /** 444 * Delete a language string. 445 */ 446 function _locale_string_delete($lid) { 447 db_query('DELETE FROM {locales_source} WHERE lid = %d', $lid); 448 db_query('DELETE FROM {locales_target} WHERE lid = %d', $lid); 449 locale_refresh_cache(); 450 drupal_set_message(t('The string has been removed.')); 451 452 drupal_goto('admin/settings/locale/string/search'); 453 } 454 455 /** 456 * Parses Gettext Portable Object file information and inserts into database 457 * 458 * @param $file 459 * Drupal file object corresponding to the PO file to import 460 * @param $lang 461 * Language code 462 * @param $mode 463 * Should existing translations be replaced ('overwrite' or 'keep') 464 */ 465 function _locale_import_po($file, $lang, $mode) { 466 // If not in 'safe mode', increase the maximum execution time: 467 if (!ini_get('safe_mode')) { 468 set_time_limit(240); 469 } 470 471 // Check if we have the language already in the database 472 if (!db_fetch_object(db_query("SELECT locale FROM {locales_meta} WHERE locale = '%s'", $lang))) { 473 drupal_set_message(t('The language selected for import is not supported.'), 'error'); 474 return FALSE; 475 } 476 477 // Get strings from file (returns on failure after a partial import, or on success) 478 $status = _locale_import_read_po('db-store', $file, $mode, $lang); 479 if ($status === FALSE) { 480 // error messages are set in _locale_import_read_po 481 return FALSE; 482 } 483 484 // Get status information on import process 485 list($headerdone, $additions, $updates) = _locale_import_one_string('db-report'); 486 487 if (!$headerdone) { 488 drupal_set_message(t('The translation file %filename appears to have a missing or malformed header.', array('%filename' => $file->filename)), 'error'); 489 } 490 491 // rebuild locale cache 492 cache_clear_all("locale:$lang", 'cache'); 493 494 // rebuild the menu, strings may have changed 495 menu_rebuild(); 496 497 drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.', array('%number' => $additions, '%update' => $updates))); 498 watchdog('locale', t('Imported %file into %locale: %number new strings added and %update updated.', array('%file' => $file->filename, '%locale' => $lang, '%number' => $additions, '%update' => $updates))); 499 return TRUE; 500 } 501 502 /** 503 * Parses Gettext Portable Object file into an array 504 * 505 * @param $op 506 * Storage operation type: db-store or mem-store 507 * @param $file 508 * Drupal file object corresponding to the PO file to import 509 * @param $mode 510 * Should existing translations be replaced ('overwrite' or 'keep') 511 * @param $lang 512 * Language code 513 */ 514 function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL) { 515 516 $fd = fopen($file->filepath, "rb"); // File will get closed by PHP on return 517 if (!$fd) { 518 _locale_import_message('The translation import failed, because the file %filename could not be read.', $file); 519 return FALSE; 520 } 521 522 $context = "COMMENT"; // Parser context: COMMENT, MSGID, MSGID_PLURAL, MSGSTR and MSGSTR_ARR 523 $current = array(); // Current entry being read 524 $plural = 0; // Current plural form 525 $lineno = 0; // Current line 526 527 while (!feof($fd)) { 528 $line = fgets($fd, 10*1024); // A line should not be this long 529 $lineno++; 530 $line = trim(strtr($line, array("\\\n" => ""))); 531 532 if (!strncmp("#", $line, 1)) { // A comment 533 if ($context == "COMMENT") { // Already in comment context: add 534 $current["#"][] = substr($line, 1); 535 } 536 elseif (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { // End current entry, start a new one 537 _locale_import_one_string($op, $current, $mode, $lang, $file); 538 $current = array(); 539 $current["#"][] = substr($line, 1); 540 $context = "COMMENT"; 541 } 542 else { // Parse error 543 _locale_import_message('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $file, $lineno); 544 return FALSE; 545 } 546 } 547 elseif (!strncmp("msgid_plural", $line, 12)) { 548 if ($context != "MSGID") { // Must be plural form for current entry 549 _locale_import_message('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $file, $lineno); 550 return FALSE; 551 } 552 $line = trim(substr($line, 12)); 553 $quoted = _locale_import_parse_quoted($line); 554 if ($quoted === FALSE) { 555 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 556 return FALSE; 557 } 558 $current["msgid"] = $current["msgid"] ."\0". $quoted; 559 $context = "MSGID_PLURAL"; 560 } 561 elseif (!strncmp("msgid", $line, 5)) { 562 if ($context == "MSGSTR") { // End current entry, start a new one 563 _locale_import_one_string($op, $current, $mode, $lang, $file); 564 $current = array(); 565 } 566 elseif ($context == "MSGID") { // Already in this context? Parse error 567 _locale_import_message('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $file, $lineno); 568 return FALSE; 569 } 570 $line = trim(substr($line, 5)); 571 $quoted = _locale_import_parse_quoted($line); 572 if ($quoted === FALSE) { 573 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 574 return FALSE; 575 } 576 $current["msgid"] = $quoted; 577 $context = "MSGID"; 578 } 579 elseif (!strncmp("msgstr[", $line, 7)) { 580 if (($context != "MSGID") && ($context != "MSGID_PLURAL") && ($context != "MSGSTR_ARR")) { // Must come after msgid, msgid_plural, or msgstr[] 581 _locale_import_message('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $file, $lineno); 582 return FALSE; 583 } 584 if (strpos($line, "]") === FALSE) { 585 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 586 return FALSE; 587 } 588 $frombracket = strstr($line, "["); 589 $plural = substr($frombracket, 1, strpos($frombracket, "]") - 1); 590 $line = trim(strstr($line, " ")); 591 $quoted = _locale_import_parse_quoted($line); 592 if ($quoted === FALSE) { 593 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 594 return FALSE; 595 } 596 $current["msgstr"][$plural] = $quoted; 597 $context = "MSGSTR_ARR"; 598 } 599 elseif (!strncmp("msgstr", $line, 6)) { 600 if ($context != "MSGID") { // Should come just after a msgid block 601 _locale_import_message('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $file, $lineno); 602 return FALSE; 603 } 604 $line = trim(substr($line, 6)); 605 $quoted = _locale_import_parse_quoted($line); 606 if ($quoted === FALSE) { 607 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 608 return FALSE; 609 } 610 $current["msgstr"] = $quoted; 611 $context = "MSGSTR"; 612 } 613 elseif ($line != "") { 614 $quoted = _locale_import_parse_quoted($line); 615 if ($quoted === FALSE) { 616 _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno); 617 return FALSE; 618 } 619 if (($context == "MSGID") || ($context == "MSGID_PLURAL")) { 620 $current["msgid"] .= $quoted; 621 } 622 elseif ($context == "MSGSTR") { 623 $current["msgstr"] .= $quoted; 624 } 625 elseif ($context == "MSGSTR_ARR") { 626 $current["msgstr"][$plural] .= $quoted; 627 } 628 else { 629 _locale_import_message('The translation file %filename contains an error: there is an unexpected string on line %line.', $file, $lineno); 630 return FALSE; 631 } 632 } 633 } 634 635 // End of PO file, flush last entry 636 if (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { 637 _locale_import_one_string($op, $current, $mode, $lang, $file); 638 } 639 elseif ($context != "COMMENT") { 640 _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno); 641 return FALSE; 642 } 643 644 } 645 646 /** 647 * Sets an error message occurred during locale file parsing. 648 * 649 * @param $message 650 * The message to be translated 651 * @param $file 652 * Drupal file object corresponding to the PO file to import 653 * @param $lineno 654 * An optional line number argument 655 */ 656 function _locale_import_message($message, $file, $lineno = NULL) { 657 $vars = array('%filename' => $file->filename); 658 if (isset($lineno)) { 659 $vars['%line'] = $lineno; 660 } 661 $t = get_t(); 662 drupal_set_message($t($message, $vars), 'error'); 663 } 664 665 /** 666 * Imports a string into the database 667 * 668 * @param $op 669 * Operation to perform: 'db-store', 'db-report', 'mem-store' or 'mem-report' 670 * @param $value 671 * Details of the string stored 672 * @param $mode 673 * Should existing translations be replaced ('overwrite' or 'keep') 674 * @param $lang 675 * Language to store the string in 676 * @param $file 677 * Object representation of file being imported, only required when op is 'db-store' 678 */ 679 function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NULL, $file = NULL) { 680 static $additions = 0; 681 static $updates = 0; 682 static $headerdone = FALSE; 683 static $strings = array(); 684 685 switch ($op) { 686 // Return stored strings 687 case 'mem-report': 688 return $strings; 689 690 // Store string in memory (only supports single strings) 691 case 'mem-store': 692 $strings[$value['msgid']] = $value['msgstr']; 693 return; 694 695 // Called at end of import to inform the user 696 case 'db-report': 697 return array($headerdone, $additions, $updates); 698 699 // Store the string we got in the database 700 case 'db-store': 701 // We got header information 702 if ($value['msgid'] == '') { 703 $hdr = _locale_import_parse_header($value['msgstr']); 704 705 // Get the plural formula 706 if ($hdr["Plural-Forms"] && $p = _locale_import_parse_plural_forms($hdr["Plural-Forms"], $file->filename)) { 707 list($nplurals, $plural) = $p; 708 db_query("UPDATE {locales_meta} SET plurals = %d, formula = '%s' WHERE locale = '%s'", $nplurals, $plural, $lang); 709 } 710 else { 711 db_query("UPDATE {locales_meta} SET plurals = %d, formula = '%s' WHERE locale = '%s'", 0, '', $lang); 712 } 713 $headerdone = TRUE; 714 } 715 716 // Some real string to import 717 else { 718 $comments = _locale_import_shorten_comments($value['#']); 719 720 // Handle a translation for some plural string 721 if (strpos($value['msgid'], "\0")) { 722 $english = explode("\0", $value['msgid'], 2); 723 $entries = array_keys($value['msgstr']); 724 for ($i = 3; $i <= count($entries); $i++) { 725 $english[] = $english[1]; 726 } 727 $translation = array_map('_locale_import_append_plural', $value['msgstr'], $entries); 728 $english = array_map('_locale_import_append_plural', $english, $entries); 729 foreach ($translation as $key => $trans) { 730 if ($key == 0) { 731 $plid = 0; 732 } 733 $loc = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $english[$key])); 734 if (!empty($loc->lid)) { // a string exists 735 $lid = $loc->lid; 736 // update location field 737 db_query("UPDATE {locales_source} SET location = '%s' WHERE lid = %d", $comments, $lid); 738 $trans2 = db_fetch_object(db_query("SELECT lid, translation, plid, plural FROM {locales_target} WHERE lid = %d AND locale = '%s'", $lid, $lang)); 739 if (!$trans2->lid) { // no translation in current language 740 db_query("INSERT INTO {locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $lang, $trans, $plid, $key); 741 $additions++; 742 } // translation exists 743 else if ($mode == 'overwrite' || $trans2->translation == '') { 744 db_query("UPDATE {locales_target} SET translation = '%s', plid = %d, plural = %d WHERE locale = '%s' AND lid = %d", $trans, $plid, $key, $lang, $lid); 745 if ($trans2->translation == '') { 746 $additions++; 747 } 748 else { 749 $updates++; 750 } 751 } 752 } 753 else { // no string 754 db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", $comments, $english[$key]); 755 $loc = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $english[$key])); 756 $lid = $loc->lid; 757 db_query("INSERT INTO {locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $lang, $trans, $plid, $key); 758 if ($trans != '') { 759 $additions++; 760 } 761 } 762 $plid = $lid; 763 } 764 } 765 766 // A simple translation 767 else { 768 $english = $value['msgid']; 769 $translation = $value['msgstr']; 770 $loc = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $english)); 771 if (!empty($loc->lid)) { // a string exists 772 $lid = $loc->lid; 773 // update location field 774 db_query("UPDATE {locales_source} SET location = '%s' WHERE source = '%s'", $comments, $english); 775 $trans = db_fetch_object(db_query("SELECT lid, translation FROM {locales_target} WHERE lid = %d AND locale = '%s'", $lid, $lang)); 776 if (!$trans->lid) { // no translation in current language 777 db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '%s')", $lid, $lang, $translation); 778 $additions++; 779 } // translation exists 780 else if ($mode == 'overwrite') { //overwrite in any case 781 db_query("UPDATE {locales_target} SET translation = '%s' WHERE locale = '%s' AND lid = %d", $translation, $lang, $lid); 782 if ($trans->translation == '') { 783 $additions++; 784 } 785 else { 786 $updates++; 787 } 788 } // overwrite if empty string 789 else if ($trans->translation == '') { 790 db_query("UPDATE {locales_target} SET translation = '%s' WHERE locale = '%s' AND lid = %d", $translation, $lang, $lid); 791 $additions++; 792 } 793 } 794 else { // no string 795 db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", $comments, $english); 796 $loc = db_fetch_object(db_query("SELECT lid FROM {locales_source} WHERE source = '%s'", $english)); 797 $lid = $loc->lid; 798 db_query("INSERT INTO {locales_target} (lid, locale, translation) VALUES (%d, '%s', '%s')", $lid, $lang, $translation); 799 if ($translation != '') { 800 $additions++; 801 } 802 } 803 } 804 } 805 } // end of db-store operation 806 } 807 808 /** 809 * Parses a Gettext Portable Object file header 810 * 811 * @param $header 812 * A string containing the complete header 813 * @return 814 * An associative array of key-value pairs 815 */ 816 function _locale_import_parse_header($header) { 817 $hdr = array(); 818 819 $lines = explode("\n", $header); 820 foreach ($lines as $line) { 821 $line = trim($line); 822 if ($line) { 823 list($tag, $contents) = explode(":", $line, 2); 824 $hdr[trim($tag)] = trim($contents); 825 } 826 } 827 828 return $hdr; 829 } 830 831 /** 832 * Parses a Plural-Forms entry from a Gettext Portable Object file header 833 * 834 * @param $pluralforms 835 * A string containing the Plural-Forms entry 836 * @param $filename 837 * A string containing the filename 838 * @return 839 * An array containing the number of plurals and a 840 * formula in PHP for computing the plural form 841 */ 842 function _locale_import_parse_plural_forms($pluralforms, $filename) { 843 // First, delete all whitespace 844 $pluralforms = strtr($pluralforms, array(" " => "", "\t" => "")); 845 846 // Select the parts that define nplurals and plural 847 $nplurals = strstr($pluralforms, "nplurals="); 848 if (strpos($nplurals, ";")) { 849 $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9); 850 } 851 else { 852 return FALSE; 853 } 854 $plural = strstr($pluralforms, "plural="); 855 if (strpos($plural, ";")) { 856 $plural = substr($plural, 7, strpos($plural, ";") - 7); 857 } 858 else { 859 return FALSE; 860 } 861 862 // Get PHP version of the plural formula 863 $plural = _locale_import_parse_arithmetic($plural); 864 865 if ($plural !== FALSE) { 866 return array($nplurals, $plural); 867 } 868 else { 869 drupal_set_message(t('The translation file %filename contains an error: the plural formula could not be parsed.', array('%filename' => $filename)), 'error'); 870 return FALSE; 871 } 872 } 873 874 /** 875 * Parses and sanitizes an arithmetic formula into a PHP expression 876 * 877 * While parsing, we ensure, that the operators have the right 878 * precedence and associativity. 879 * 880 * @param $string 881 * A string containing the arithmetic formula 882 * @return 883 * The PHP version of the formula 884 */ 885 function _locale_import_parse_arithmetic($string) { 886 // Operator precedence table 887 $prec = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&" => 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+" => 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8); 888 // Right associativity 889 $rasc = array("?" => 1, ":" => 1); 890 891 $tokens = _locale_import_tokenize_formula($string); 892 893 // Parse by converting into infix notation then back into postfix 894 $opstk = array(); 895 $elstk = array(); 896 897 foreach ($tokens as $token) { 898 $ctok = $token; 899 900 // Numbers and the $n variable are simply pushed into $elarr 901 if (is_numeric($token)) { 902 $elstk[] = $ctok; 903 } 904 elseif ($ctok == "n") { 905 $elstk[] = '$n'; 906 } 907 elseif ($ctok == "(") { 908 $opstk[] = $ctok; 909 } 910 elseif ($ctok == ")") { 911 $topop = array_pop($opstk); 912 while (($topop != NULL) && ($topop != "(")) { 913 $elstk[] = $topop; 914 $topop = array_pop($opstk); 915 } 916 } 917 elseif (!empty($prec[$ctok])) { 918 // If it's an operator, then pop from $oparr into $elarr until the 919 // precedence in $oparr is less than current, then push into $oparr 920 $topop = array_pop($opstk); 921 while (($topop != NULL) && ($prec[$topop] >= $prec[$ctok]) && !(($prec[$topop] == $prec[$ctok]) && $rasc[$topop] && $rasc[$ctok])) { 922 $elstk[] = $topop; 923 $topop = array_pop($opstk); 924 } 925 if ($topop) { 926 $opstk[] = $topop; // Return element to top 927 } 928 $opstk[] = $ctok; // Parentheses are not needed 929 } 930 else { 931 return FALSE; 932 } 933 } 934 935 // Flush operator stack 936 $topop = array_pop($opstk); 937 while ($topop != NULL) { 938 $elstk[] = $topop; 939 $topop = array_pop($opstk); 940 } 941 942 // Now extract formula from stack 943 $prevsize = count($elstk) + 1; 944 while (count($elstk) < $prevsize) { 945 $prevsize = count($elstk); 946 for ($i = 2; $i < count($elstk); $i++) { 947 $op = $elstk[$i]; 948 if ($prec[$op]) { 949 $f = ""; 950 if ($op == ":") { 951 $f = $elstk[$i - 2] ."):". $elstk[$i - 1] .")"; 952 } 953 elseif ($op == "?") { 954 $f = "(". $elstk[$i - 2] ."?(". $elstk[$i - 1]; 955 } 956 else { 957 $f = "(". $elstk[$i - 2] . $op . $elstk[$i - 1] .")"; 958 } 959 array_splice($elstk, $i - 2, 3, $f); 960 break; 961 } 962 } 963 } 964 965 // If only one element is left, the number of operators is appropriate 966 if (count($elstk) == 1) { 967 return $elstk[0]; 968 } 969 else { 970 return FALSE; 971 } 972 } 973 974 /** 975 * Backward compatible implementation of token_get_all() for formula parsing 976 * 977 * @param $string 978 * A string containing the arithmetic formula 979 * @return 980 * The PHP version of the formula 981 */ 982 function _locale_import_tokenize_formula($formula) { 983 $formula = str_replace(" ", "", $formula); 984 $tokens = array(); 985 for ($i = 0; $i < strlen($formula); $i++) { 986 if (is_numeric($formula[$i])) { 987 $num = $formula[$i]; 988 $j = $i + 1; 989 while ($j < strlen($formula) && is_numeric($formula[$j])) { 990 $num .= $formula[$j]; 991 $j++; 992 } 993 $i = $j - 1; 994 $tokens[] = $num; 995 } 996 elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a space 997 $next = $formula[$i + 1]; 998 switch ($pos) { 999 case 1: 1000 case 2: 1001 case 3: 1002 case 4: 1003 if ($next == '=') { 1004 $tokens[] = $formula[$i] .'='; 1005 $i++; 1006 } 1007 else { 1008 $tokens[] = $formula[$i]; 1009 } 1010 break; 1011 case 5: 1012 if ($next == '&') { 1013 $tokens[] = '&&'; 1014 $i++; 1015 } 1016 else { 1017 $tokens[] = $formula[$i]; 1018 } 1019 break; 1020 case 6: 1021 if ($next == '|') { 1022 $tokens[] = '||'; 1023 $i++; 1024 } 1025 else { 1026 $tokens[] = $formula[$i]; 1027 } 1028 break; 1029 } 1030 } 1031 else { 1032 $tokens[] = $formula[$i]; 1033 } 1034 } 1035 return $tokens; 1036 } 1037 1038 /** 1039 * Modify a string to contain proper count indices 1040 * 1041 * This is a callback function used via array_map() 1042 * 1043 * @param $entry 1044 * An array element 1045 * @param $key 1046 * Index of the array element 1047 */ 1048 function _locale_import_append_plural($entry, $key) { 1049 // No modifications for 0, 1 1050 if ($key == 0 || $key == 1) { 1051 return $entry; 1052 } 1053 1054 // First remove any possibly false indices, then add new ones 1055 $entry = preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry); 1056 return preg_replace('/(@count)/', "\\1[$key]", $entry); 1057 } 1058 1059 /** 1060 * Generate a short, one string version of the passed comment array 1061 * 1062 * @param $comment 1063 * An array of strings containing a comment 1064 * @return 1065 * Short one string version of the comment 1066 */ 1067 function _locale_import_shorten_comments($comment) { 1068 $comm = ''; 1069 while (count($comment)) { 1070 $test = $comm . substr(array_shift($comment), 1) .', '; 1071 if (strlen($comm) < 130) { 1072 $comm = $test; 1073 } 1074 else { 1075 break; 1076 } 1077 } 1078 return substr($comm, 0, -2); 1079 } 1080 1081 /** 1082 * Parses a string in quotes 1083 * 1084 * @param $string 1085 * A string specified with enclosing quotes 1086 * @return 1087 * The string parsed from inside the quotes 1088 */ 1089 function _locale_import_parse_quoted($string) { 1090 if (substr($string, 0, 1) != substr($string, -1, 1)) { 1091 return FALSE; // Start and end quotes must be the same 1092 } 1093 $quote = substr($string, 0, 1); 1094 $string = substr($string, 1, -1); 1095 if ($quote == '"') { // Double quotes: strip slashes 1096 return stripcslashes($string); 1097 } 1098 elseif ($quote == "'") { // Simple quote: return as-is 1099 return $string; 1100 } 1101 else { 1102 return FALSE; // Unrecognized quote 1103 } 1104 } 1105 1106 /** 1107 * Exports a Portable Object (Template) file for a language 1108 * 1109 * @param $language Selects a language to generate the output for 1110 */ 1111 function _locale_export_po($language) { 1112 global $user; 1113 1114 // Get language specific strings, or all strings 1115 if ($language) { 1116 $meta = db_fetch_object(db_query("SELECT * FROM {locales_meta} WHERE locale = '%s'", $language)); 1117 $result = db_query("SELECT s.lid, s.source, s.location, t.translation, t.plid, t.plural FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.locale = '%s' ORDER BY t.plid, t.plural", $language); 1118 } 1119 else { 1120 $result = db_query("SELECT s.lid, s.source, s.location, t.plid, t.plural FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid ORDER BY t.plid, t.plural"); 1121 } 1122 1123 // Build array out of the database results 1124 $parent = array(); 1125 while ($child = db_fetch_object($result)) { 1126 if ($child->source != '') { 1127 $parent[$child->lid]['comment'] = $child->location; 1128 $parent[$child->lid]['msgid'] = $child->source; 1129 $parent[$child->lid]['translation'] = $child->translation; 1130 if ($child->plid) { 1131 $parent[$child->lid]['child'] = 1; 1132 $parent[$child->plid]['plural'] = $child->lid; 1133 } 1134 } 1135 } 1136 1137 // Generating Portable Object file for a language 1138 if ($language) { 1139 $filename = $language .'.po'; 1140 $header .= "# $meta->name translation of ". variable_get('site_name', 'Drupal') ."\n"; 1141 $header .= '# Copyright (c) '. date('Y') .' '. $user->name .' <'. $user->mail .">\n"; 1142 $header .= "#\n"; 1143 $header .= "msgid \"\"\n"; 1144 $header .= "msgstr \"\"\n"; 1145 $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; 1146 $header .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n"; 1147 $header .= "\"PO-Revision-Date: ". date("Y-m-d H:iO") ."\\n\"\n"; 1148 $header .= "\"Last-Translator: ". $user->name .' <'. $user->mail .">\\n\"\n"; 1149 $header .= "\"Language-Team: ". $meta->name .' <'. $user->mail .">\\n\"\n"; 1150 $header .= "\"MIME-Version: 1.0\\n\"\n"; 1151 $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; 1152 $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; 1153 if ($meta->formula && $meta->plurals) { 1154 $header .= "\"Plural-Forms: nplurals=". $meta->plurals ."; plural=". strtr($meta->formula, array('$' => '')) .";\\n\"\n"; 1155 } 1156 $header .= "\n"; 1157 watchdog('locale', t('Exported %locale translation file: %filename.', array('%locale' => $meta->name, '%filename' => $filename))); 1158 } 1159 1160 // Generating Portable Object Template 1161 else { 1162 $filename = 'drupal.pot'; 1163 $header .= "# LANGUAGE translation of PROJECT\n"; 1164 $header .= "# Copyright (c) YEAR NAME <EMAIL@ADDRESS>\n"; 1165 $header .= "#\n"; 1166 $header .= "msgid \"\"\n"; 1167 $header .= "msgstr \"\"\n"; 1168 $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; 1169 $header .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n"; 1170 $header .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n"; 1171 $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n"; 1172 $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n"; 1173 $header .= "\"MIME-Version: 1.0\\n\"\n"; 1174 $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; 1175 $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; 1176 $header .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n"; 1177 $header .= "\n"; 1178 watchdog('locale', t('Exported translation file: %filename.', array('%filename' => $filename))); 1179 } 1180 1181 // Start download process 1182 header("Content-Disposition: attachment; filename=$filename"); 1183 header("Content-Type: text/plain; charset=utf-8"); 1184 1185 print $header; 1186 1187 foreach ($parent as $lid => $message) { 1188 if (!isset($message['child'])) { 1189 if ($message['comment']) { 1190 print '#: '. $message['comment'] ."\n"; 1191 } 1192 print 'msgid '. _locale_export_print($message['msgid']); 1193 if ($plural = $message['plural']) { 1194 print 'msgid_plural '. _locale_export_print($parent[$plural]['msgid']); 1195 if ($language) { 1196 $translation = $message['translation']; 1197 for ($i = 0; $i < $meta->plurals; $i++) { 1198 print 'msgstr['. $i .'] '. _locale_export_print($translation); 1199 if ($plural) { 1200 $translation = $parent[$plural]['translation']; 1201 if ($i > 1) { 1202 $translation = _locale_export_remove_plural($translation); 1203 } 1204 $plural = $parent[$plural]['plural']; 1205 } 1206 else { 1207 $translation = ''; 1208 } 1209 } 1210 } 1211 else { 1212 print 'msgstr[0] ""'. "\n"; 1213 print 'msgstr[1] ""'. "\n"; 1214 } 1215 } 1216 else { 1217 if ($language) { 1218 print 'msgstr '. _locale_export_print($message['translation']); 1219 } 1220 else { 1221 print 'msgstr ""'. "\n"; 1222 } 1223 } 1224 print "\n"; 1225 } 1226 } 1227 die(); 1228 } 1229 1230 /** 1231 * Print out a string on multiple lines 1232 */ 1233 function _locale_export_print($str) { 1234 $stri = addcslashes($str, "\0..\37\\\""); 1235 $parts = array(); 1236 1237 // Cut text into several lines 1238 while ($stri != "") { 1239 $i = strpos($stri, "\\n"); 1240 if ($i === FALSE) { 1241 $curstr = $stri; 1242 $stri = ""; 1243 } 1244 else { 1245 $curstr = substr($stri, 0, $i + 2); 1246 $stri = substr($stri, $i + 2); 1247 } 1248 $curparts = explode("\n", _locale_export_wrap($curstr, 70)); 1249 $parts = array_merge($parts, $curparts); 1250 } 1251 1252 if (count($parts) > 1) { 1253 return "\"\"\n\"". implode("\"\n\"", $parts) ."\"\n"; 1254 } 1255 else { 1256 return "\"$parts[0]\"\n"; 1257 } 1258 } 1259 1260 /** 1261 * Custom word wrapping for Portable Object (Template) files. 1262 */ 1263 function _locale_export_wrap($str, $len) { 1264 $words = explode(' ', $str); 1265 $ret = array(); 1266 1267 $cur = ""; 1268 $nstr = 1; 1269 while (count($words)) { 1270 $word = array_shift($words); 1271 if ($nstr) { 1272 $cur = $word; 1273 $nstr = 0; 1274 } 1275 elseif (strlen("$cur $word") > $len) { 1276 $ret[] = $cur . " "; 1277 $cur = $word; 1278 } 1279 else { 1280 $cur = "$cur $word"; 1281 } 1282 } 1283 $ret[] = $cur; 1284 1285 return implode("\n", $ret); 1286 } 1287 1288 /** 1289 * Removes plural index information from a string 1290 */ 1291 function _locale_export_remove_plural($entry) { 1292 return preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry); 1293 } 1294 1295 /** 1296 * List languages in search result table 1297 */ 1298 function _locale_string_language_list($translation) { 1299 // Add CSS 1300 drupal_add_css(drupal_get_path('module', 'locale') .'/locale.css', 'module', 'all', FALSE); 1301 1302 $languages = locale_supported_languages(FALSE, TRUE); 1303 unset($languages['name']['en']); 1304 $output = ''; 1305 foreach ($languages['name'] as $key => $value) { 1306 if (isset($translation[$key])) { 1307 $output .= ($translation[$key] != '') ? $key .' ' : "<em class=\"locale-untranslated\">$key</em> "; 1308 } 1309 } 1310 1311 return $output; 1312 } 1313 1314 /** 1315 * Build object out of search criteria specified in request variables 1316 */ 1317 function _locale_string_seek_query() { 1318 static $query; 1319 1320 if (!isset($query)) { 1321 $fields = array('string', 'language', 'searchin'); 1322 $query = new stdClass(); 1323 if (is_array($_REQUEST['edit'])) { 1324 foreach ($_REQUEST['edit'] as $key => $value) { 1325 if (!empty($value) && in_array($key, $fields)) { 1326 $query->$key = $value; 1327 } 1328 } 1329 } 1330 else { 1331 foreach ($_REQUEST as $key => $value) { 1332 if (!empty($value) && in_array($key, $fields)) { 1333 $query->$key = strpos(',', $value) ? explode(',', $value) : $value; 1334 } 1335 } 1336 } 1337 } 1338 return $query; 1339 } 1340 1341 /** 1342 * Perform a string search and display results in a table 1343 */ 1344 function _locale_string_seek() { 1345 // We have at least one criterion to match 1346 if ($query = _locale_string_seek_query()) { 1347 $join = "SELECT s.source, s.location, s.lid, t.translation, t.locale FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid "; 1348 1349 $arguments = array(); 1350 // Compute LIKE section 1351 switch ($query->searchin) { 1352 case 'translated': 1353 $where = "WHERE (t.translation LIKE '%%%s%%' AND t.translation != '')"; 1354 $orderby = "ORDER BY t.translation"; 1355 $arguments[] = $query->string; 1356 break; 1357 case 'untranslated': 1358 $where = "WHERE (s.source LIKE '%%%s%%' AND t.translation = '')"; 1359 $orderby = "ORDER BY s.source"; 1360 $arguments[] = $query->string; 1361 break; 1362 case 'all' : 1363 default: 1364 $where = "WHERE (s.source LIKE '%%%s%%' OR t.translation LIKE '%%%s%%')"; 1365 $orderby = ''; 1366 $arguments[] = $query->string; 1367 $arguments[] = $query->string; 1368 break; 1369 } 1370 1371 switch ($query->language) { 1372 // Force search in source strings 1373 case "en": 1374 $sql = $join ." WHERE s.source LIKE '%%%s%%' ORDER BY s.source"; 1375 $arguments = array($query->string); // $where is not used, discard its arguments 1376 break; 1377 // Search in all languages 1378 case "all": 1379 $sql = "$join $where $orderby"; 1380 break; 1381 // Some different language 1382 default: 1383 $sql = "$join $where AND t.locale = '%s' $orderby"; 1384 $arguments[] = $query->language; 1385 } 1386 1387 $result = pager_query($sql, 50, 0, NULL, $arguments); 1388 1389 $header = array(t('String'), t('Locales'), array('data' => t('Operations'), 'colspan' => '2')); 1390 $arr = array(); 1391 while ($locale = db_fetch_object($result)) { 1392 $arr[$locale->lid]['locales'][$locale->locale] = $locale->translation; 1393 $arr[$locale->lid]['location'] = $locale->location; 1394 $arr[$locale->lid]['source'] = $locale->source; 1395 } 1396 foreach ($arr as $lid => $value) { 1397 $rows[] = array(array('data' => check_plain(truncate_utf8($value['source'], 150, FALSE, TRUE)) .'<br /><small>'. $value['location'] .'</small>'), array('data' => _locale_string_language_list($value['locales']), 'align' => 'center'), array('data' => l(t('edit'), "admin/settings/locale/string/edit/$lid"), 'class' => 'nowrap'), array('data' => l(t('delete'), "admin/settings/locale/string/delete/$lid"), 'class' => 'nowrap')); 1398 } 1399 1400 $request = array(); 1401 if (count($query)) { 1402 foreach ($query as $key => $value) { 1403 $request[$key] = (is_array($value)) ? implode(',', $value) : $value; 1404 } 1405 } 1406 1407 if (count($rows)) { 1408 $output .= theme('table', $header, $rows); 1409 } 1410 if ($pager = theme('pager', NULL, 50, 0, $request)) { 1411 $output .= $pager; 1412 } 1413 } 1414 1415 return $output; 1416 } 1417 1418 // --------------------------------------------------------------------------------- 1419 // List of some of the most common languages (administration only) 1420 1421 /** 1422 * Prepares the language code list for a select form item with only the unsupported ones 1423 */ 1424 function _locale_prepare_iso_list() { 1425 $languages = locale_supported_languages(FALSE, TRUE); 1426 $isocodes = _locale_get_iso639_list(); 1427 foreach ($isocodes as $key => $value) { 1428 if (isset($languages['name'][$key])) { 1429 unset($isocodes[$key]); 1430 continue; 1431 } 1432 if (count($value) == 2) { 1433 $tname = t($value[0]); 1434 $isocodes[$key] = ($tname == $value[1]) ? $tname : "$tname ($value[1])"; 1435 } 1436 else { 1437 $isocodes[$key] = t($value[0]); 1438 } 1439 } 1440 asort($isocodes); 1441 return $isocodes; 1442 } 1443 1444 /** 1445 * Some of the common languages with their English and native names 1446 * 1447 * Based on ISO 639 and http://people.w3.org/rishida/names/languages.html 1448 */ 1449 function _locale_get_iso639_list() { 1450 return array( 1451 "aa" => array("Afar"), 1452 "ab" => array("Abkhazian", "аҧсуа бызшәа"), 1453 "ae" => array("Avestan"), 1454 "af" => array("Afrikaans"), 1455 "ak" => array("Akan"), 1456 "am" => array("Amharic", "አማርኛ"), 1457 "ar" => array("Arabic", "العربية"), 1458 "as" => array("Assamese"), 1459 "av" => array("Avar"), 1460 "ay" => array("Aymara"), 1461 "az" => array("Azerbaijani", "azərbaycan"), 1462 "ba" => array("Bashkir"), 1463 "be" => array("Belarusian", "Беларуская"), 1464 "bg" => array("Bulgarian", "Български"), 1465 "bh" => array("Bihari"), 1466 "bi" => array("Bislama"), 1467 "bm" => array("Bambara", "Bamanankan"), 1468 "bn" => array("Bengali"), 1469 "bo" => array("Tibetan"), 1470 "br" => array("Breton"), 1471 "bs" => array("Bosnian", "Bosanski"), 1472 "ca" => array("Catalan", "Català"), 1473 "ce" => array("Chechen"), 1474 "ch" => array("Chamorro"), 1475 "co" => array("Corsican"), 1476 "cr" => array("Cree"), 1477 "cs" => array("Czech", "Čeština"), 1478 "cu" => array("Old Slavonic"), 1479 "cv" => array("Chuvash"), 1480 "cy" => array("Welsh", "Cymraeg"), 1481 "da" => array("Danish", "Dansk"), 1482 "de" => array("German", "Deutsch"), 1483 "dv" => array("Maldivian"), 1484 "dz" => array("Bhutani"), 1485 "ee" => array("Ewe", "Ɛʋɛ"), 1486 "el" => array("Greek", "Ελληνικά"), 1487 "en" => array("English"), 1488 "eo" => array("Esperanto"), 1489 "es" => array("Spanish", "Español"), 1490 "et" => array("Estonian", "Eesti"), 1491 "eu" => array("Basque", "Euskera"), 1492 "fa" => array("Persian", "فارسی"), 1493 "ff" => array("Fulah", "Fulfulde"), 1494 "fi" => array("Finnish", "Suomi"), 1495 "fj" => array("Fiji"), 1496 "fo" => array("Faeroese"), 1497 "fr" => array("French", "Français"), 1498 "fy" => array("Frisian", "Frysk"), 1499 "ga" => array("Irish", "Gaeilge"), 1500 "gd" => array("Scots Gaelic"), 1501 "gl" => array("Galician", "Galego"), 1502 "gn" => array("Guarani"), 1503 "gu" => array("Gujarati"), 1504 "gv" => array("Manx"), 1505 "ha" => array("Hausa"), 1506 "he" => array("Hebrew", "עברית"), 1507 "hi" => array("Hindi", "हिन्दी"), 1508 "ho" => array("Hiri Motu"), 1509 "hr" => array("Croatian", "Hrvatski"), 1510 "hu" => array("Hungarian", "Magyar"), 1511 "hy" => array("Armenian", "Հայերեն"), 1512 "hz" => array("Herero"), 1513 "ia" => array("Interlingua"), 1514 "id" => array("Indonesian", "Bahasa Indonesia"), 1515 "ie" => array("Interlingue"), 1516 "ig" => array("Igbo"), 1517 "ik" => array("Inupiak"), 1518 "is" => array("Icelandic", "Íslenska"), 1519 "it" => array("Italian", "Italiano"), 1520 "iu" => array("Inuktitut"), 1521 "ja" => array("Japanese", "日本語"), 1522 "jv" => array("Javanese"), 1523 "ka" => array("Georgian"), 1524 "kg" => array("Kongo"), 1525 "ki" => array("Kikuyu"), 1526 "kj" => array("Kwanyama"), 1527 "kk" => array("Kazakh", "Қазақ"), 1528 "kl" => array("Greenlandic"), 1529 "km" => array("Cambodian"), 1530 "kn" => array("Kannada", "ಕನ್ನಡ"), 1531 "ko" => array("Korean", "한국어"), 1532 "kr" => array("Kanuri"), 1533 "ks" => array("Kashmiri"), 1534 "ku" => array("Kurdish", "Kurdî"), 1535 "kv" => array("Komi"), 1536 "kw" => array("Cornish"), 1537 "ky" => array("Kirghiz", "Кыргыз"), 1538 "la" => array("Latin", "Latina"), 1539 "lb" => array("Luxembourgish"), 1540 "lg" => array("Luganda"), 1541 "ln" => array("Lingala"), 1542 "lo" => array("Laothian"), 1543 "lt" => array("Lithuanian", "Lietuviškai"), 1544 "lv" => array("Latvian", "Latviešu"), 1545 "mg" => array("Malagasy"), 1546 "mh" => array("Marshallese"), 1547 "mi" => array("Maori"), 1548 "mk" => array("Macedonian", "Македонски"), 1549 "ml" => array("Malayalam", "മലയാളം"), 1550 "mn" => array("Mongolian"), 1551 "mo" => array("Moldavian"), 1552 "mr" => array("Marathi"), 1553 "ms" => array("Malay", "Bahasa Melayu"), 1554 "mt" => array("Maltese", "Malti"), 1555 "my" => array("Burmese"), 1556 "na" => array("Nauru"), 1557 "nd" => array("North Ndebele"), 1558 "ne" => array("Nepali"), 1559 "ng" => array("Ndonga"), 1560 "nl" => array("Dutch", "Nederlands"), 1561 "nb" => array("Norwegian Bokmål", "Bokmål"), 1562 "nn" => array("Norwegian Nynorsk", "Nynorsk"), 1563 "nr" => array("South Ndebele"), 1564 "nv" => array("Navajo"), 1565 "ny" => array("Chichewa"), 1566 "oc" => array("Occitan"), 1567 "om" => array("Oromo"), 1568 "or" => array("Oriya"), 1569 "os" => array("Ossetian"), 1570 "pa" => array("Punjabi"), 1571 "pi" => array("Pali"), 1572 "pl" => array("Polish", "Polski"), 1573 "ps" => array("Pashto", "پښتو"), 1574 "pt" => array("Portuguese, Portugal", "Português"), 1575 "pt-br" => array("Portuguese, Brazil", "Português"), 1576 "qu" => array("Quechua"), 1577 "rm" => array("Rhaeto-Romance"), 1578 "rn" => array("Kirundi"), 1579 "ro" => array("Romanian", "Română"), 1580 "ru" => array("Russian", "Русский"), 1581 "rw" => array("Kinyarwanda"), 1582 "sa" => array("Sanskrit"), 1583 "sc" => array("Sardinian"), 1584 "sd" => array("Sindhi"), 1585 "se" => array("Northern Sami"), 1586 "sg" => array("Sango"), 1587 "sh" => array("Serbo-Croatian"), 1588 "si" => array("Singhalese"), 1589 "sk" => array("Slovak", "Slovenčina"), 1590 "sl" => array("Slovenian", "Slovenščina"), 1591 "sm" => array("Samoan"), 1592 "sn" => array("Shona"), 1593 "so" => array("Somali"), 1594 "sq" => array("Albanian", "Shqip"), 1595 "sr" => array("Serbian", "Српски"), 1596 "ss" => array("Siswati"), 1597 "st" => array("Sesotho"), 1598 "su" => array("Sudanese"), 1599 "sv" => array("Swedish", "Svenska"), 1600 "sw" => array("Swahili", "Kiswahili"), 1601 "ta" => array("Tamil", "தமிழ்"), 1602 "te" => array("Telugu", "తెలుగు"), 1603 "tg" => array("Tajik"), 1604 "th" => array("Thai", "ภาษาไทย"), 1605 "ti" => array("Tigrinya"), 1606 "tk" => array("Turkmen"), 1607 "tl" => array("Tagalog"), 1608 "tn" => array("Setswana"), 1609 "to" => array("Tonga"), 1610 "tr" => array("Turkish", "Türkçe"), 1611 "ts" => array("Tsonga"), 1612 "tt" => array("Tatar", "Tatarça"), 1613 "tw" => array("Twi"), 1614 "ty" => array("Tahitian"), 1615 "ug" => array("Uighur"), 1616 "uk" => array("Ukrainian", "Українська"), 1617 "ur" => array("Urdu", "اردو"), 1618 "uz" => array("Uzbek", "o'zbek"), 1619 "ve" => array("Venda"), 1620 "vi" => array("Vietnamese", "Tiếng Việt"), 1621 "wo" => array("Wolof"), 1622 "xh" => array("Xhosa", "isiXhosa"), 1623 "yi" => array("Yiddish"), 1624 "yo" => array("Yoruba", "Yorùbá"), 1625 "za" => array("Zhuang"), 1626 "zh-hans" => array("Chinese, Simplified", "简体中文"), 1627 "zh-hant" => array("Chinese, Traditional", "繁體中文"), 1628 "zu" => array("Zulu", "isiZulu"), 1629 ); 1630 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Gnr le : Fri Nov 30 16:20:15 2007 | par Balluche grce PHPXref 0.7 |
![]() |