| 1 | | <?php |
| 2 | | // $Id: locale.inc,v 1.190 2008/10/26 18:06:38 dries Exp $ |
| 3 | | |
| 4 | | /** |
| 5 | | * @file |
| 6 | | * Administration functions for locale.module. |
| 7 | | */ |
| 8 | | |
| 9 | | /** |
| 10 | | * Regular expression pattern used to localize JavaScript strings. |
| 11 | | */ |
| 12 | 35 | define('LOCALE_JS_STRING',
'(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+'); |
| 13 | | |
| 14 | | /** |
| 15 | | * Translation import mode overwriting all existing translations |
| 16 | | * if new translated version available. |
| 17 | | */ |
| 18 | 35 | define('LOCALE_IMPORT_OVERWRITE', 0); |
| 19 | | |
| 20 | | /** |
| 21 | | * Translation import mode keeping existing translations and only |
| 22 | | * inserting new strings. |
| 23 | | */ |
| 24 | 35 | define('LOCALE_IMPORT_KEEP', 1); |
| 25 | | |
| 26 | | /** |
| 27 | | * @defgroup locale-language-overview Language overview functionality |
| 28 | | * @{ |
| 29 | | */ |
| 30 | | |
| 31 | | /** |
| 32 | | * User interface for the language overview screen. |
| 33 | | */ |
| 34 | 35 | function locale_languages_overview_form() { |
| 35 | 8 | $languages = language_list('language', TRUE); |
| 36 | | |
| 37 | 8 | $options = array(); |
| 38 | 8 | $form['weight'] = array('#tree' => TRUE); |
| 39 | 8 | foreach ($languages as $langcode => $language) { |
| 40 | | |
| 41 | 8 | $options[$langcode] = ''; |
| 42 | 8 | if ($language->enabled) { |
| 43 | 8 | $enabled[] = $langcode; |
| 44 | 8 | } |
| 45 | 8 | $form['weight'][$langcode] = array( |
| 46 | 8 | '#type' => 'weight', |
| 47 | 8 | '#default_value' => $language->weight, |
| 48 | 8 | '#attributes' => array('class' => 'language-order-weight'), |
| 49 | | ); |
| 50 | 8 | $form['name'][$langcode] = array('#markup' =>
check_plain($language->name)); |
| 51 | 8 | $form['native'][$langcode] = array('#markup' =>
check_plain($language->native)); |
| 52 | 8 | $form['direction'][$langcode] = array('#markup' =>
($language->direction == LANGUAGE_RTL ? t('Right to left') : t('Left to
right'))); |
| 53 | 8 | } |
| 54 | 8 | $form['enabled'] = array('#type' => 'checkboxes', |
| 55 | 8 | '#options' => $options, |
| 56 | 8 | '#default_value' => $enabled, |
| 57 | | ); |
| 58 | 8 | $form['site_default'] = array('#type' => 'radios', |
| 59 | 8 | '#options' => $options, |
| 60 | 8 | '#default_value' => language_default('language'), |
| 61 | | ); |
| 62 | 8 | $form['submit'] = array('#type' => 'submit', '#value' => t('Save
configuration')); |
| 63 | 8 | $form['#theme'] = 'locale_languages_overview_form'; |
| 64 | | |
| 65 | 8 | return $form; |
| 66 | 0 | } |
| 67 | | |
| 68 | | /** |
| 69 | | * Theme the language overview form. |
| 70 | | * |
| 71 | | * @ingroup themeable |
| 72 | | */ |
| 73 | 35 | function theme_locale_languages_overview_form($form) { |
| 74 | 7 | $default = language_default(); |
| 75 | 7 | foreach ($form['name'] as $key => $element) { |
| 76 | | // Do not take form control structures. |
| 77 | 7 | if (is_array($element) && element_child($key)) { |
| 78 | | // Disable checkbox for the default language, because it cannot be
disabled. |
| 79 | 7 | if ($key == $default->language) { |
| 80 | 7 | $form['enabled'][$key]['#attributes']['disabled'] = 'disabled'; |
| 81 | 7 | } |
| 82 | 7 | $rows[] = array( |
| 83 | | 'data' => array( |
| 84 | 7 | '<strong>' . drupal_render($form['name'][$key]) . '</strong>', |
| 85 | 7 | drupal_render($form['native'][$key]), |
| 86 | 7 | check_plain($key), |
| 87 | 7 | drupal_render($form['direction'][$key]), |
| 88 | 7 | array('data' => drupal_render($form['enabled'][$key]), 'align' =>
'center'), |
| 89 | 7 | drupal_render($form['site_default'][$key]), |
| 90 | 7 | drupal_render($form['weight'][$key]), |
| 91 | 7 | l(t('edit'), 'admin/settings/language/edit/' . $key) . (($key !=
'en' && $key != $default->language) ? ' ' . l(t('delete'),
'admin/settings/language/delete/' . $key) : '') |
| 92 | 7 | ), |
| 93 | | 'class' => 'draggable' |
| 94 | 7 | ); |
| 95 | 7 | } |
| 96 | 7 | } |
| 97 | 7 | $header = array(array('data' => t('English name')), array('data' =>
t('Native name')), array('data' => t('Code')), array('data' =>
t('Direction')), array('data' => t('Enabled')), array('data' =>
t('Default')), array('data' => t('Weight')), array('data' =>
t('Operations'))); |
| 98 | 7 | $output = theme('table', $header, $rows, array('id' =>
'language-order')); |
| 99 | 7 | $output .= drupal_render($form); |
| 100 | | |
| 101 | 7 | drupal_add_tabledrag('language-order', 'order', 'sibling',
'language-order-weight'); |
| 102 | | |
| 103 | 7 | return $output; |
| 104 | 0 | } |
| 105 | | |
| 106 | | /** |
| 107 | | * Process language overview form submissions, updating existing languages. |
| 108 | | */ |
| 109 | 35 | function locale_languages_overview_form_submit($form, &$form_state) { |
| 110 | 1 | $languages = language_list(); |
| 111 | 1 | $default = language_default(); |
| 112 | 1 | $enabled_count = 0; |
| 113 | 1 | foreach ($languages as $langcode => $language) { |
| 114 | 1 | if ($form_state['values']['site_default'] == $langcode ||
$default->language == $langcode) { |
| 115 | | // Automatically enable the default language and the language |
| 116 | | // which was default previously (because we will not get the |
| 117 | | // value from that disabled checkox). |
| 118 | 1 | $form_state['values']['enabled'][$langcode] = 1; |
| 119 | 1 | } |
| 120 | 1 | if ($form_state['values']['enabled'][$langcode]) { |
| 121 | 1 | $enabled_count++; |
| 122 | 1 | $language->enabled = 1; |
| 123 | 1 | } |
| 124 | | else { |
| 125 | 0 | $language->enabled = 0; |
| 126 | | } |
| 127 | 1 | $language->weight = $form_state['values']['weight'][$langcode]; |
| 128 | 1 | db_query("UPDATE {languages} SET enabled = %d, weight = %d WHERE
language = '%s'", $language->enabled, $language->weight, $langcode); |
| 129 | 1 | $languages[$langcode] = $language; |
| 130 | 1 | } |
| 131 | 1 | drupal_set_message(t('Configuration saved.')); |
| 132 | 1 | variable_set('language_default',
$languages[$form_state['values']['site_default']]); |
| 133 | 1 | variable_set('language_count', $enabled_count); |
| 134 | | |
| 135 | | // Changing the language settings impacts the interface. |
| 136 | 1 | cache_clear_all('*', 'cache_page', TRUE); |
| 137 | | |
| 138 | 1 | $form_state['redirect'] = 'admin/settings/language'; |
| 139 | 1 | return; |
| 140 | 0 | } |
| 141 | | /** |
| 142 | | * @} End of "locale-language-overview" |
| 143 | | */ |
| 144 | | |
| 145 | | /** |
| 146 | | * @defgroup locale-language-add-edit Language addition and editing
functionality |
| 147 | | * @{ |
| 148 | | */ |
| 149 | | |
| 150 | | /** |
| 151 | | * User interface for the language addition screen. |
| 152 | | */ |
| 153 | 35 | function locale_languages_add_screen() { |
| 154 | 6 | $output = drupal_get_form('locale_languages_predefined_form'); |
| 155 | 4 | $output .= drupal_get_form('locale_languages_custom_form'); |
| 156 | 3 | return $output; |
| 157 | 0 | } |
| 158 | | |
| 159 | | /** |
| 160 | | * Predefined language setup form. |
| 161 | | */ |
| 162 | 35 | function locale_languages_predefined_form() { |
| 163 | 6 | $predefined = _locale_prepare_predefined_list(); |
| 164 | 6 | $form = array(); |
| 165 | 6 | $form['language list'] = array('#type' => 'fieldset', |
| 166 | 6 | '#title' => t('Predefined language'), |
| 167 | 6 | '#collapsible' => TRUE, |
| 168 | | ); |
| 169 | 6 | $form['language list']['langcode'] = array('#type' => 'select', |
| 170 | 6 | '#title' => t('Language name'), |
| 171 | 6 | '#default_value' => key($predefined), |
| 172 | 6 | '#options' => $predefined, |
| 173 | 6 | '#description' => t('Select the desired language and click the <em>Add
language</em> button. (Use the <em>Custom language</em> options if your
desired language does not appear in this list.)'), |
| 174 | | ); |
| 175 | 6 | $form['language list']['submit'] = array('#type' => 'submit', '#value' =>
t('Add language')); |
| 176 | 6 | return $form; |
| 177 | 0 | } |
| 178 | | |
| 179 | | /** |
| 180 | | * Custom language addition form. |
| 181 | | */ |
| 182 | 35 | function locale_languages_custom_form() { |
| 183 | 4 | $form = array(); |
| 184 | 4 | $form['custom language'] = array('#type' => 'fieldset', |
| 185 | 4 | '#title' => t('Custom language'), |
| 186 | 4 | '#collapsible' => TRUE, |
| 187 | 4 | '#collapsed' => TRUE, |
| 188 | | ); |
| 189 | 4 | _locale_languages_common_controls($form['custom language']); |
| 190 | 4 | $form['custom language']['submit'] = array( |
| 191 | 4 | '#type' => 'submit', |
| 192 | 4 | '#value' => t('Add custom language') |
| 193 | 4 | ); |
| 194 | | // Reuse the validation and submit functions of the predefined language
setup form. |
| 195 | 4 | $form['#submit'][] = 'locale_languages_predefined_form_submit'; |
| 196 | 4 | $form['#validate'][] = 'locale_languages_predefined_form_validate'; |
| 197 | 4 | return $form; |
| 198 | 0 | } |
| 199 | | |
| 200 | | /** |
| 201 | | * Editing screen for a particular language. |
| 202 | | * |
| 203 | | * @param $langcode |
| 204 | | * Language code of the language to edit. |
| 205 | | */ |
| 206 | 35 | function locale_languages_edit_form(&$form_state, $langcode) { |
| 207 | 0 | if ($language = db_fetch_object(db_query("SELECT * FROM {languages} WHERE
language = '%s'", $langcode))) { |
| 208 | 0 | $form = array(); |
| 209 | 0 | _locale_languages_common_controls($form, $language); |
| 210 | 0 | $form['submit'] = array( |
| 211 | 0 | '#type' => 'submit', |
| 212 | 0 | '#value' => t('Save language') |
| 213 | 0 | ); |
| 214 | 0 | $form['#submit'][] = 'locale_languages_edit_form_submit'; |
| 215 | 0 | $form['#validate'][] = 'locale_languages_edit_form_validate'; |
| 216 | 0 | return $form; |
| 217 | 0 | } |
| 218 | | else { |
| 219 | 0 | drupal_not_found(); |
| 220 | | } |
| 221 | 0 | } |
| 222 | | |
| 223 | | /** |
| 224 | | * Common elements of the language addition and editing form. |
| 225 | | * |
| 226 | | * @param $form |
| 227 | | * A parent form item (or empty array) to add items below. |
| 228 | | * @param $language |
| 229 | | * Language object to edit. |
| 230 | | */ |
| 231 | 35 | function _locale_languages_common_controls(&$form, $language = NULL) { |
| 232 | 4 | if (!is_object($language)) { |
| 233 | 4 | $language = new stdClass(); |
| 234 | 4 | } |
| 235 | 4 | if (isset($language->language)) { |
| 236 | 0 | $form['langcode_view'] = array( |
| 237 | 0 | '#type' => 'item', |
| 238 | 0 | '#title' => t('Language code'), |
| 239 | 0 | '#markup' => $language->language |
| 240 | 0 | ); |
| 241 | 0 | $form['langcode'] = array( |
| 242 | 0 | '#type' => 'value', |
| 243 | 0 | '#value' => $language->language |
| 244 | 0 | ); |
| 245 | 0 | } |
| 246 | | else { |
| 247 | 4 | $form['langcode'] = array('#type' => 'textfield', |
| 248 | 4 | '#title' => t('Language code'), |
| 249 | 4 | '#size' => 12, |
| 250 | 4 | '#maxlength' => 60, |
| 251 | 4 | '#required' => TRUE, |
| 252 | 4 | '#default_value' => @$language->language, |
| 253 | 4 | '#disabled' => (isset($language->language)), |
| 254 | 4 | '#description' => t('<a href="@rfc4646">RFC 4646</a> compliant
language identifier. Language codes typically use a country code, and
optionally, a script or regional variant name. <em>Examples: "en", "en-US"
and "zh-Hant".</em>', array('@rfc4646' =>
'http://www.ietf.org/rfc/rfc4646.txt')), |
| 255 | | ); |
| 256 | | } |
| 257 | 4 | $form['name'] = array('#type' => 'textfield', |
| 258 | 4 | '#title' => t('Language name in English'), |
| 259 | 4 | '#maxlength' => 64, |
| 260 | 4 | '#default_value' => @$language->name, |
| 261 | 4 | '#required' => TRUE, |
| 262 | 4 | '#description' => t('Name of the language in English. Will be available
for translation in all languages.'), |
| 263 | | ); |
| 264 | 4 | $form['native'] = array('#type' => 'textfield', |
| 265 | 4 | '#title' => t('Native language name'), |
| 266 | 4 | '#maxlength' => 64, |
| 267 | 4 | '#default_value' => @$language->native, |
| 268 | 4 | '#required' => TRUE, |
| 269 | 4 | '#description' => t('Name of the language in the language being
added.'), |
| 270 | | ); |
| 271 | 4 | $form['prefix'] = array('#type' => 'textfield', |
| 272 | 4 | '#title' => t('Path prefix'), |
| 273 | 4 | '#maxlength' => 64, |
| 274 | 4 | '#default_value' => @$language->prefix, |
| 275 | 4 | '#description' => t('Language code or other custom string for pattern
matching within the path. With language negotiation set to <em>Path prefix
only</em> or <em>Path prefix with language fallback</em>, this site is
presented in this language when the Path prefix value matches an element in
the path. For the default language, this value may be left blank.
<strong>Modifying this value will break existing URLs and should be used
with caution in a production environment.</strong> <em>Example: Specifying
"deutsch" as the path prefix for German results in URLs in the form
"www.example.com/deutsch/node".</em>') |
| 276 | 4 | ); |
| 277 | 4 | $form['domain'] = array('#type' => 'textfield', |
| 278 | 4 | '#title' => t('Language domain'), |
| 279 | 4 | '#maxlength' => 128, |
| 280 | 4 | '#default_value' => @$language->domain, |
| 281 | 4 | '#description' => t('Language-specific URL, with protocol. With
language negotiation set to <em>Domain name only</em>, the site is
presented in this language when the URL accessing the site references this
domain. For the default language, this value may be left blank.
<strong>This value must include a protocol as part of the string.</strong>
<em>Example: Specifying "http://example.de" or "http://de.example.com" as
language domains for German results in URLs in the forms
"http://example.de/node" and "http://de.example.com/node",
respectively.</em>'), |
| 282 | | ); |
| 283 | 4 | $form['direction'] = array('#type' => 'radios', |
| 284 | 4 | '#title' => t('Direction'), |
| 285 | 4 | '#required' => TRUE, |
| 286 | 4 | '#description' => t('Direction that text in this language is
presented.'), |
| 287 | 4 | '#default_value' => @$language->direction, |
| 288 | 4 | '#options' => array(LANGUAGE_LTR => t('Left to right'), LANGUAGE_RTL =>
t('Right to left')) |
| 289 | 4 | ); |
| 290 | 4 | return $form; |
| 291 | 0 | } |
| 292 | | |
| 293 | | /** |
| 294 | | * Validate the language addition form. |
| 295 | | */ |
| 296 | 35 | function locale_languages_predefined_form_validate($form, &$form_state) { |
| 297 | 3 | $langcode = $form_state['values']['langcode']; |
| 298 | | |
| 299 | 3 | if ($duplicate = db_result(db_query("SELECT COUNT(*) FROM {languages}
WHERE language = '%s'", $langcode)) != 0) { |
| 300 | 0 | form_set_error('langcode', t('The language %language (%code) already
exists.', array('%language' => $form_state['values']['name'], '%code' =>
$langcode))); |
| 301 | 0 | } |
| 302 | | |
| 303 | 3 | if (!isset($form_state['values']['name'])) { |
| 304 | | // Predefined language selection. |
| 305 | 2 | $predefined = _locale_get_predefined_list(); |
| 306 | 2 | if (!isset($predefined[$langcode])) { |
| 307 | 0 | form_set_error('langcode', t('Invalid language code.')); |
| 308 | 0 | } |
| 309 | 2 | } |
| 310 | | else { |
| 311 | | // Reuse the editing form validation routine if we add a custom
language. |
| 312 | 1 | locale_languages_edit_form_validate($form, $form_state); |
| 313 | | } |
| 314 | 3 | } |
| 315 | | |
| 316 | | /** |
| 317 | | * Process the language addition form submission. |
| 318 | | */ |
| 319 | 35 | function locale_languages_predefined_form_submit($form, &$form_state) { |
| 320 | 3 | $langcode = $form_state['values']['langcode']; |
| 321 | 3 | if (isset($form_state['values']['name'])) { |
| 322 | | // Custom language form. |
| 323 | 1 | locale_add_language($langcode, $form_state['values']['name'],
$form_state['values']['native'], $form_state['values']['direction'],
$form_state['values']['domain'], $form_state['values']['prefix']); |
| 324 | 1 | drupal_set_message(t('The language %language has been created and can
now be used. More information is available on the <a
href="@locale-help">help screen</a>.', array('%language' =>
t($form_state['values']['name']), '@locale-help' =>
url('admin/help/locale')))); |
| 325 | 1 | } |
| 326 | | else { |
| 327 | | // Predefined language selection. |
| 328 | 2 | $predefined = _locale_get_predefined_list(); |
| 329 | 2 | locale_add_language($langcode); |
| 330 | 2 | drupal_set_message(t('The language %language has been created and can
now be used. More information is available on the <a
href="@locale-help">help screen</a>.', array('%language' =>
t($predefined[$langcode][0]), '@locale-help' =>
url('admin/help/locale')))); |
| 331 | | } |
| 332 | | |
| 333 | | // See if we have language files to import for the newly added |
| 334 | | // language, collect and import them. |
| 335 | 3 | if ($batch = locale_batch_by_language($langcode,
'_locale_batch_language_finished')) { |
| 336 | 0 | batch_set($batch); |
| 337 | 0 | } |
| 338 | | |
| 339 | 3 | $form_state['redirect'] = 'admin/settings/language'; |
| 340 | 3 | return; |
| 341 | 0 | } |
| 342 | | |
| 343 | | /** |
| 344 | | * Validate the language editing form. Reused for custom language addition
too. |
| 345 | | */ |
| 346 | 35 | function locale_languages_edit_form_validate($form, &$form_state) { |
| 347 | 1 | if (!empty($form_state['values']['domain']) &&
!empty($form_state['values']['prefix'])) { |
| 348 | 0 | form_set_error('prefix', t('Domain and path prefix values should not be
set at the same time.')); |
| 349 | 0 | } |
| 350 | 1 | if (!empty($form_state['values']['domain']) && $duplicate =
db_fetch_object(db_query("SELECT language FROM {languages} WHERE domain =
'%s' AND language <> '%s'", $form_state['values']['domain'],
$form_state['values']['langcode']))) { |
| 351 | 0 | form_set_error('domain', t('The domain (%domain) is already tied to a
language (%language).', array('%domain' => $form_state['values']['domain'],
'%language' => $duplicate->language))); |
| 352 | 0 | } |
| 353 | 1 | if (empty($form_state['values']['prefix']) &&
language_default('language') != $form_state['values']['langcode'] &&
empty($form_state['values']['domain'])) { |
| 354 | 0 | form_set_error('prefix', t('Only the default language can have both the
domain and prefix empty.')); |
| 355 | 0 | } |
| 356 | 1 | if (!empty($form_state['values']['prefix']) && $duplicate =
db_fetch_object(db_query("SELECT language FROM {languages} WHERE prefix =
'%s' AND language <> '%s'", $form_state['values']['prefix'],
$form_state['values']['langcode']))) { |
| 357 | 0 | form_set_error('prefix', t('The prefix (%prefix) is already tied to a
language (%language).', array('%prefix' => $form_state['values']['prefix'],
'%language' => $duplicate->language))); |
| 358 | 0 | } |
| 359 | 1 | } |
| 360 | | |
| 361 | | /** |
| 362 | | * Process the language editing form submission. |
| 363 | | */ |
| 364 | 35 | function locale_languages_edit_form_submit($form, &$form_state) { |
| 365 | 0 | db_query("UPDATE {languages} SET name = '%s', native = '%s', domain =
'%s', prefix = '%s', direction = %d WHERE language = '%s'",
$form_state['values']['name'], $form_state['values']['native'],
$form_state['values']['domain'], $form_state['values']['prefix'],
$form_state['values']['direction'], $form_state['values']['langcode']); |
| 366 | 0 | $default = language_default(); |
| 367 | 0 | if ($default->language == $form_state['values']['langcode']) { |
| 368 | 0 | $properties = array('name', 'native', 'direction', 'enabled',
'plurals', 'formula', 'domain', 'prefix', 'weight'); |
| 369 | 0 | foreach ($properties as $keyname) { |
| 370 | 0 | if (isset($form_state['values'][$keyname])) { |
| 371 | 0 | $default->$keyname = $form_state['values'][$keyname]; |
| 372 | 0 | } |
| 373 | 0 | } |
| 374 | 0 | variable_set('language_default', $default); |
| 375 | 0 | } |
| 376 | 0 | $form_state['redirect'] = 'admin/settings/language'; |
| 377 | 0 | return; |
| 378 | 0 | } |
| 379 | | /** |
| 380 | | * @} End of "locale-language-add-edit" |
| 381 | | */ |
| 382 | | |
| 383 | | /** |
| 384 | | * @defgroup locale-language-delete Language deletion functionality |
| 385 | | * @{ |
| 386 | | */ |
| 387 | | |
| 388 | | /** |
| 389 | | * User interface for the language deletion confirmation screen. |
| 390 | | */ |
| 391 | 35 | function locale_languages_delete_form(&$form_state, $langcode) { |
| 392 | | |
| 393 | | // Do not allow deletion of English locale. |
| 394 | 3 | if ($langcode == 'en') { |
| 395 | 0 | drupal_set_message(t('The English language cannot be deleted.')); |
| 396 | 0 | drupal_goto('admin/settings/language'); |
| 397 | 0 | } |
| 398 | | |
| 399 | 3 | if (language_default('language') == $langcode) { |
| 400 | 0 | drupal_set_message(t('The default language cannot be deleted.')); |
| 401 | 0 | drupal_goto('admin/settings/language'); |
| 402 | 0 | } |
| 403 | | |
| 404 | | // For other languages, warn user that data loss is ahead. |
| 405 | 3 | $languages = language_list(); |
| 406 | | |
| 407 | 3 | if (!isset($languages[$langcode])) { |
| 408 | 1 | drupal_not_found(); |
| 409 | 1 | } |
| 410 | | else { |
| 411 | 2 | $form['langcode'] = array('#type' => 'value', '#value' => $langcode); |
| 412 | 2 | return confirm_form($form, t('Are you sure you want to delete the
language %name?', array('%name' => t($languages[$langcode]->name))),
'admin/settings/language', t('Deleting a language will remove all interface
translations associated with it, and posts in this language will be set to
be language neutral. This action cannot be undone.'), t('Delete'),
t('Cancel')); |
| 413 | | } |
| 414 | 1 | } |
| 415 | | |
| 416 | | /** |
| 417 | | * Process language deletion submissions. |
| 418 | | */ |
| 419 | 35 | function locale_languages_delete_form_submit($form, &$form_state) { |
| 420 | 1 | $languages = language_list(); |
| 421 | 1 | if (isset($languages[$form_state['values']['langcode']])) { |
| 422 | | // Remove translations first. |
| 423 | 1 | db_query("DELETE FROM {locales_target} WHERE language = '%s'",
$form_state['values']['langcode']); |
| 424 | 1 | cache_clear_all('locale:' . $form_state['values']['langcode'],
'cache'); |
| 425 | | // With no translations, this removes existing JavaScript translations
file. |
| 426 | 1 | _locale_rebuild_js($form_state['values']['langcode']); |
| 427 | | // Remove the language. |
| 428 | 1 | db_query("DELETE FROM {languages} WHERE language = '%s'",
$form_state['values']['langcode']); |
| 429 | 1 | db_query("UPDATE {node} SET language = '' WHERE language = '%s'",
$form_state['values']['langcode']); |
| 430 | 1 | $variables = array('%locale' =>
$languages[$form_state['values']['langcode']]->name); |
| 431 | 1 | drupal_set_message(t('The language %locale has been removed.',
$variables)); |
| 432 | 1 | watchdog('locale', 'The language %locale has been removed.',
$variables); |
| 433 | 1 | } |
| 434 | | |
| 435 | | // Changing the language settings impacts the interface: |
| 436 | 1 | cache_clear_all('*', 'cache_page', TRUE); |
| 437 | | |
| 438 | 1 | $form_state['redirect'] = 'admin/settings/language'; |
| 439 | 1 | return; |
| 440 | 0 | } |
| 441 | | /** |
| 442 | | * @} End of "locale-language-add-edit" |
| 443 | | */ |
| 444 | | |
| 445 | | /** |
| 446 | | * @defgroup locale-languages-negotiation Language negotiation options
screen |
| 447 | | * @{ |
| 448 | | */ |
| 449 | | |
| 450 | | /** |
| 451 | | * Setting for language negotiation options |
| 452 | | */ |
| 453 | 35 | function locale_languages_configure_form() { |
| 454 | 0 | $form['language_negotiation'] = array( |
| 455 | 0 | '#title' => t('Language negotiation'), |
| 456 | 0 | '#type' => 'radios', |
| 457 | | '#options' => array( |
| 458 | 0 | LANGUAGE_NEGOTIATION_NONE => t('None.'), |
| 459 | 0 | LANGUAGE_NEGOTIATION_PATH_DEFAULT => t('Path prefix only.'), |
| 460 | 0 | LANGUAGE_NEGOTIATION_PATH => t('Path prefix with language
fallback.'), |
| 461 | 0 | LANGUAGE_NEGOTIATION_DOMAIN => t('Domain name only.')), |
| 462 | 0 | '#default_value' => variable_get('language_negotiation',
LANGUAGE_NEGOTIATION_NONE), |
| 463 | 0 | '#description' => t("Select the mechanism used to determine your site's
presentation language. <strong>Modifying this setting may break all
incoming URLs and should be used with caution in a production
environment.</strong>") |
| 464 | 0 | ); |
| 465 | 0 | $form['submit'] = array( |
| 466 | 0 | '#type' => 'submit', |
| 467 | 0 | '#value' => t('Save settings') |
| 468 | 0 | ); |
| 469 | 0 | return $form; |
| 470 | 0 | } |
| 471 | | |
| 472 | | /** |
| 473 | | * Submit function for language negotiation settings. |
| 474 | | */ |
| 475 | 35 | function locale_languages_configure_form_submit($form, &$form_state) { |
| 476 | 0 | variable_set('language_negotiation',
$form_state['values']['language_negotiation']); |
| 477 | 0 | drupal_set_message(t('Language negotiation configuration saved.')); |
| 478 | 0 | $form_state['redirect'] = 'admin/settings/language'; |
| 479 | 0 | return; |
| 480 | 0 | } |
| 481 | | /** |
| 482 | | * @} End of "locale-languages-negotiation" |
| 483 | | */ |
| 484 | | |
| 485 | | /** |
| 486 | | * @defgroup locale-translate-overview Translation overview screen. |
| 487 | | * @{ |
| 488 | | */ |
| 489 | | |
| 490 | | /** |
| 491 | | * Overview screen for translations. |
| 492 | | */ |
| 493 | 35 | function locale_translate_overview_screen() { |
| 494 | 0 | $languages = language_list('language', TRUE); |
| 495 | 0 | $groups = module_invoke_all('locale', 'groups'); |
| 496 | | |
| 497 | | // Build headers with all groups in order. |
| 498 | 0 | $headers = array_merge(array(t('Language')), array_values($groups)); |
| 499 | | |
| 500 | | // Collect summaries of all source strings in all groups. |
| 501 | 0 | $sums = db_query("SELECT COUNT(*) AS strings, textgroup FROM
{locales_source} GROUP BY textgroup"); |
| 502 | 0 | $groupsums = array(); |
| 503 | 0 | while ($group = db_fetch_object($sums)) { |
| 504 | 0 | $groupsums[$group->textgroup] = $group->strings; |
| 505 | 0 | } |
| 506 | | |
| 507 | | // Set up overview table with default values, ensuring common order for
values. |
| 508 | 0 | $rows = array(); |
| 509 | 0 | foreach ($languages as $langcode => $language) { |
| 510 | 0 | $rows[$langcode] = array('name' => ($langcode == 'en' ? t('English
(built-in)') : t($language->name))); |
| 511 | 0 | foreach ($groups as $group => $name) { |
| 512 | 0 | $rows[$langcode][$group] = ($langcode == 'en' ? t('n/a') : '0/' .
(isset($groupsums[$group]) ? $groupsums[$group] : 0) . ' (0%)'); |
| 513 | 0 | } |
| 514 | 0 | } |
| 515 | | |
| 516 | | // Languages with at least one record in the locale table. |
| 517 | 0 | $translations = db_query("SELECT COUNT(*) AS translation, t.language,
s.textgroup FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid
= t.lid GROUP BY textgroup, language"); |
| 518 | 0 | while ($data = db_fetch_object($translations)) { |
| 519 | 0 | $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation >
0) ? round(($data->translation/$groupsums[$data->textgroup])*100., 2) : 0; |
| 520 | 0 | $rows[$data->language][$data->textgroup] = $data->translation . '/' .
$groupsums[$data->textgroup] . " ($ratio%)"; |
| 521 | 0 | } |
| 522 | | |
| 523 | 0 | return theme('table', $headers, $rows); |
| 524 | 0 | } |
| 525 | | /** |
| 526 | | * @} End of "locale-translate-overview" |
| 527 | | */ |
| 528 | | |
| 529 | | /** |
| 530 | | * @defgroup locale-translate-seek Translation search screen. |
| 531 | | * @{ |
| 532 | | */ |
| 533 | | |
| 534 | | /** |
| 535 | | * String search screen. |
| 536 | | */ |
| 537 | 35 | function locale_translate_seek_screen() { |
| 538 | 8 | $output = _locale_translate_seek(); |
| 539 | 8 | $output .= drupal_get_form('locale_translate_seek_form'); |
| 540 | 8 | return $output; |
| 541 | 0 | } |
| 542 | | |
| 543 | | /** |
| 544 | | * User interface for the string search screen. |
| 545 | | */ |
| 546 | 35 | function locale_translate_seek_form() { |
| 547 | | // Get all languages, except English |
| 548 | 8 | $languages = locale_language_list('name', TRUE); |
| 549 | 8 | unset($languages['en']); |
| 550 | | |
| 551 | | // Present edit form preserving previous user settings |
| 552 | 8 | $query = _locale_translate_seek_query(); |
| 553 | 8 | $form = array(); |
| 554 | 8 | $form['search'] = array('#type' => 'fieldset', |
| 555 | 8 | '#title' => t('Search'), |
| 556 | | ); |
| 557 | 8 | $form['search']['string'] = array('#type' => 'textfield', |
| 558 | 8 | '#title' => t('String contains'), |
| 559 | 8 | '#default_value' => @$query['string'], |
| 560 | 8 | '#description' => t('Leave blank to show all strings. The search is
case sensitive.'), |
| 561 | | ); |
| 562 | 8 | $form['search']['language'] = array( |
| 563 | | // Change type of form widget if more the 5 options will |
| 564 | | // be present (2 of the options are added below). |
| 565 | 8 | '#type' => (count($languages) <= 3 ? 'radios' : 'select'), |
| 566 | 8 | '#title' => t('Language'), |
| 567 | 8 | '#default_value' => (!empty($query['language']) ? $query['language'] :
'all'), |
| 568 | 8 | '#options' => array_merge(array('all' => t('All languages'), 'en' =>
t('English (provided by Drupal)')), $languages), |
| 569 | | ); |
| 570 | 8 | $form['search']['translation'] = array('#type' => 'radios', |
| 571 | 8 | '#title' => t('Search in'), |
| 572 | 8 | '#default_value' => (!empty($query['translation']) ?
$query['translation'] : 'all'), |
| 573 | 8 | '#options' => array('all' => t('Both translated and untranslated
strings'), 'translated' => t('Only translated strings'), 'untranslated' =>
t('Only untranslated strings')), |
| 574 | | ); |
| 575 | 8 | $groups = module_invoke_all('locale', 'groups'); |
| 576 | 8 | $form['search']['group'] = array('#type' => 'radios', |
| 577 | 8 | '#title' => t('Limit search to'), |
| 578 | 8 | '#default_value' => (!empty($query['group']) ? $query['group'] :
'all'), |
| 579 | 8 | '#options' => array_merge(array('all' => t('All text groups')),
$groups), |
| 580 | | ); |
| 581 | | |
| 582 | 8 | $form['search']['submit'] = array('#type' => 'submit', '#value' =>
t('Search')); |
| 583 | 8 | $form['#redirect'] = FALSE; |
| 584 | | |
| 585 | 8 | return $form; |
| 586 | 0 | } |
| 587 | | /** |
| 588 | | * @} End of "locale-translate-seek" |
| 589 | | */ |
| 590 | | |
| 591 | | /** |
| 592 | | * @defgroup locale-translate-import Translation import screen. |
| 593 | | * @{ |
| 594 | | */ |
| 595 | | |
| 596 | | /** |
| 597 | | * User interface for the translation import screen. |
| 598 | | */ |
| 599 | 35 | function locale_translate_import_form() { |
| 600 | | // Get all languages, except English |
| 601 | 0 | $names = locale_language_list('name', TRUE); |
| 602 | 0 | unset($names['en']); |
| 603 | | |
| 604 | 0 | if (!count($names)) { |
| 605 | 0 | $languages = _locale_prepare_predefined_list(); |
| 606 | 0 | $default = array_shift(array_keys($languages)); |
| 607 | 0 | } |
| 608 | | else { |
| 609 | | $languages = array( |
| 610 | 0 | t('Already added languages') => $names, |
| 611 | 0 | t('Languages not yet added') => _locale_prepare_predefined_list() |
| 612 | 0 | ); |
| 613 | 0 | $default = array_shift(array_keys($names)); |
| 614 | | } |
| 615 | | |
| 616 | 0 | $form = array(); |
| 617 | 0 | $form['import'] = array('#type' => 'fieldset', |
| 618 | 0 | '#title' => t('Import translation'), |
| 619 | | ); |
| 620 | 0 | $form['import']['file'] = array('#type' => 'file', |
| 621 | 0 | '#title' => t('Language file'), |
| 622 | 0 | '#size' => 50, |
| 623 | 0 | '#description' => t('A Gettext Portable Object (<em>.po</em>) file.'), |
| 624 | | ); |
| 625 | 0 | $form['import']['langcode'] = array('#type' => 'select', |
| 626 | 0 | '#title' => t('Import into'), |
| 627 | 0 | '#options' => $languages, |
| 628 | 0 | '#default_value' => $default, |
| 629 | 0 | '#description' => t('Choose the language you want to add strings into.
If you choose a language which is not yet set up, it will be added.'), |
| 630 | | ); |
| 631 | 0 | $form['import']['group'] = array('#type' => 'radios', |
| 632 | 0 | '#title' => t('Text group'), |
| 633 | 0 | '#default_value' => 'default', |
| 634 | 0 | '#options' => module_invoke_all('locale', 'groups'), |
| 635 | 0 | '#description' => t('Imported translations will be added to this text
group.'), |
| 636 | | ); |
| 637 | 0 | $form['import']['mode'] = array('#type' => 'radios', |
| 638 | 0 | '#title' => t('Mode'), |
| 639 | 0 | '#default_value' => LOCALE_IMPORT_KEEP, |
| 640 | | '#options' => array( |
| 641 | 0 | LOCALE_IMPORT_OVERWRITE => t('Strings in the uploaded file replace
existing ones, new ones are added'), |
| 642 | 0 | LOCALE_IMPORT_KEEP => t('Existing strings are kept, only new strings
are added') |
| 643 | 0 | ), |
| 644 | | ); |
| 645 | 0 | $form['import']['submit'] = array('#type' => 'submit', '#value' =>
t('Import')); |
| 646 | 0 | $form['#attributes']['enctype'] = 'multipart/form-data'; |
| 647 | | |
| 648 | 0 | return $form; |
| 649 | 0 | } |
| 650 | | |
| 651 | | /** |
| 652 | | * Process the locale import form submission. |
| 653 | | */ |
| 654 | 35 | function locale_translate_import_form_submit($form, &$form_state) { |
| 655 | | // Ensure we have the file uploaded |
| 656 | 0 | if ($file = file_save_upload('file')) { |
| 657 | | |
| 658 | | // Add language, if not yet supported |
| 659 | 0 | $languages = language_list('language', TRUE); |
| 660 | 0 | $langcode = $form_state['values']['langcode']; |
| 661 | 0 | if (!isset($languages[$langcode])) { |
| 662 | 0 | $predefined = _locale_get_predefined_list(); |
| 663 | 0 | locale_add_language($langcode); |
| 664 | 0 | drupal_set_message(t('The language %language has been created.',
array('%language' => t($predefined[$langcode][0])))); |
| 665 | 0 | } |
| 666 | | |
| 667 | | // Now import strings into the language |
| 668 | 0 | if ($ret = _locale_import_po($file, $langcode,
$form_state['values']['mode'], $form_state['values']['group']) == FALSE) { |
| 669 | 0 | $variables = array('%filename' => $file->filename); |
| 670 | 0 | drupal_set_message(t('The translation import of %filename failed.',
$variables), 'error'); |
| 671 | 0 | watchdog('locale', 'The translation import of %filename failed.',
$variables, WATCHDOG_ERROR); |
| 672 | 0 | } |
| 673 | 0 | } |
| 674 | | else { |
| 675 | 0 | drupal_set_message(t('File to import not found.'), 'error'); |
| 676 | 0 | return 'admin/build/translate/import'; |
| 677 | | } |
| 678 | | |
| 679 | 0 | $form_state['redirect'] = 'admin/build/translate'; |
| 680 | 0 | return; |
| 681 | 0 | } |
| 682 | | /** |
| 683 | | * @} End of "locale-translate-import" |
| 684 | | */ |
| 685 | | |
| 686 | | /** |
| 687 | | * @defgroup locale-translate-export Translation export screen. |
| 688 | | * @{ |
| 689 | | */ |
| 690 | | |
| 691 | | /** |
| 692 | | * User interface for the translation export screen. |
| 693 | | */ |
| 694 | 35 | function locale_translate_export_screen() { |
| 695 | | // Get all languages, except English |
| 696 | 0 | $names = locale_language_list('name', TRUE); |
| 697 | 0 | unset($names['en']); |
| 698 | 0 | $output = ''; |
| 699 | | // Offer translation export if any language is set up. |
| 700 | 0 | if (count($names)) { |
| 701 | 0 | $output = drupal_get_form('locale_translate_export_po_form', $names); |
| 702 | 0 | } |
| 703 | 0 | $output .= drupal_get_form('locale_translate_export_pot_form'); |
| 704 | 0 | return $output; |
| 705 | 0 | } |
| 706 | | |
| 707 | | /** |
| 708 | | * Form to export PO files for the languages provided. |
| 709 | | * |
| 710 | | * @param $names |
| 711 | | * An associate array with localized language names |
| 712 | | */ |
| 713 | 35 | function locale_translate_export_po_form(&$form_state, $names) { |
| 714 | 0 | $form['export'] = array('#type' => 'fieldset', |
| 715 | 0 | '#title' => t('Export translation'), |
| 716 | 0 | '#collapsible' => TRUE, |
| 717 | | ); |
| 718 | 0 | $form['export']['langcode'] = array('#type' => 'select', |
| 719 | 0 | '#title' => t('Language name'), |
| 720 | 0 | '#options' => $names, |
| 721 | 0 | '#description' => t('Select the language to export in Gettext Portable
Object (<em>.po</em>) format.'), |
| 722 | | ); |
| 723 | 0 | $form['export']['group'] = array('#type' => 'radios', |
| 724 | 0 | '#title' => t('Text group'), |
| 725 | 0 | '#default_value' => 'default', |
| 726 | 0 | '#options' => module_invoke_all('locale', 'groups'), |
| 727 | | ); |
| 728 | 0 | $form['export']['submit'] = array('#type' => 'submit', '#value' =>
t('Export')); |
| 729 | 0 | return $form; |
| 730 | 0 | } |
| 731 | | |
| 732 | | /** |
| 733 | | * Translation template export form. |
| 734 | | */ |
| 735 | 35 | function locale_translate_export_pot_form() { |
| 736 | | // Complete template export of the strings |
| 737 | 0 | $form['export'] = array('#type' => 'fieldset', |
| 738 | 0 | '#title' => t('Export template'), |
| 739 | 0 | '#collapsible' => TRUE, |
| 740 | 0 | '#description' => t('Generate a Gettext Portable Object Template
(<em>.pot</em>) file with all strings from the Drupal locale database.'), |
| 741 | | ); |
| 742 | 0 | $form['export']['group'] = array('#type' => 'radios', |
| 743 | 0 | '#title' => t('Text group'), |
| 744 | 0 | '#default_value' => 'default', |
| 745 | 0 | '#options' => module_invoke_all('locale', 'groups'), |
| 746 | | ); |
| 747 | 0 | $form['export']['submit'] = array('#type' => 'submit', '#value' =>
t('Export')); |
| 748 | | // Reuse PO export submission callback. |
| 749 | 0 | $form['#submit'][] = 'locale_translate_export_po_form_submit'; |
| 750 | 0 | $form['#validate'][] = 'locale_translate_export_po_form_validate'; |
| 751 | 0 | return $form; |
| 752 | 0 | } |
| 753 | | |
| 754 | | /** |
| 755 | | * Process a translation (or template) export form submission. |
| 756 | | */ |
| 757 | 35 | function locale_translate_export_po_form_submit($form, &$form_state) { |
| 758 | | // If template is required, language code is not given. |
| 759 | 0 | $language = NULL; |
| 760 | 0 | if (isset($form_state['values']['langcode'])) { |
| 761 | 0 | $languages = language_list(); |
| 762 | 0 | $language = $languages[$form_state['values']['langcode']]; |
| 763 | 0 | } |
| 764 | 0 | _locale_export_po($language, _locale_export_po_generate($language,
_locale_export_get_strings($language, $form_state['values']['group']))); |
| 765 | 0 | } |
| 766 | | /** |
| 767 | | * @} End of "locale-translate-export" |
| 768 | | */ |
| 769 | | |
| 770 | | /** |
| 771 | | * @defgroup locale-translate-edit Translation text editing |
| 772 | | * @{ |
| 773 | | */ |
| 774 | | |
| 775 | | /** |
| 776 | | * User interface for string editing. |
| 777 | | */ |
| 778 | 35 | function locale_translate_edit_form(&$form_state, $lid) { |
| 779 | | // Fetch source string, if possible. |
| 780 | 2 | $source = db_fetch_object(db_query('SELECT source, textgroup, location
FROM {locales_source} WHERE lid = %d', $lid)); |
| 781 | 2 | if (!$source) { |
| 782 | 0 | drupal_set_message(t('String not found.'), 'error'); |
| 783 | 0 | drupal_goto('admin/build/translate/search'); |
| 784 | 0 | } |
| 785 | | |
| 786 | | // Add original text to the top and some values for form altering. |
| 787 | | $form = array( |
| 788 | | 'original' => array( |
| 789 | 2 | '#type' => 'item', |
| 790 | 2 | '#title' => t('Original text'), |
| 791 | 2 | '#markup' => check_plain(wordwrap($source->source, 0)), |
| 792 | 2 | ), |
| 793 | | 'lid' => array( |
| 794 | 2 | '#type' => 'value', |
| 795 | | '#value' => $lid |
| 796 | 2 | ), |
| 797 | | 'textgroup' => array( |
| 798 | 2 | '#type' => 'value', |
| 799 | 2 | '#value' => $source->textgroup, |
| 800 | 2 | ), |
| 801 | | 'location' => array( |
| 802 | 2 | '#type' => 'value', |
| 803 | 2 | '#value' => $source->location |
| 804 | 2 | ), |
| 805 | 2 | ); |
| 806 | | |
| 807 | | // Include default form controls with empty values for all languages. |
| 808 | | // This ensures that the languages are always in the same order in forms. |
| 809 | 2 | $languages = language_list(); |
| 810 | 2 | $default = language_default(); |
| 811 | | // We don't need the default language value, that value is in $source. |
| 812 | 2 | $omit = $source->textgroup == 'default' ? 'en' : $default->language; |
| 813 | 2 | unset($languages[($omit)]); |
| 814 | 2 | $form['translations'] = array('#tree' => TRUE); |
| 815 | | // Approximate the number of rows to use in the default textarea. |
| 816 | 2 | $rows = min(ceil(str_word_count($source->source) / 12), 10); |
| 817 | 2 | foreach ($languages as $langcode => $language) { |
| 818 | 2 | $form['translations'][$langcode] = array( |
| 819 | 2 | '#type' => 'textarea', |
| 820 | 2 | '#title' => t($language->name), |
| 821 | 2 | '#rows' => $rows, |
| 822 | 2 | '#default_value' => '', |
| 823 | | ); |
| 824 | 2 | } |
| 825 | | |
| 826 | | // Fetch translations and fill in default values in the form. |
| 827 | 2 | $result = db_query("SELECT DISTINCT translation, language FROM
{locales_target} WHERE lid = %d AND language <> '%s'", $lid, $omit); |
| 828 | 2 | while ($translation = db_fetch_object($result)) { |
| 829 | 0 | $form['translations'][$translation->language]['#default_value'] =
$translation->translation; |
| 830 | 0 | } |
| 831 | | |
| 832 | 2 | $form['submit'] = array('#type' => 'submit', '#value' => t('Save
translations')); |
| 833 | 2 | return $form; |
| 834 | 0 | } |
| 835 | | |
| 836 | | /** |
| 837 | | * Process string editing form submissions. |
| 838 | | * Saves all translations of one string submitted from a form. |
| 839 | | */ |
| 840 | 35 | function locale_translate_edit_form_submit($form, &$form_state) { |
| 841 | 1 | $lid = $form_state['values']['lid']; |
| 842 | 1 | foreach ($form_state['values']['translations'] as $key => $value) { |
| 843 | 1 | $translation = db_result(db_query("SELECT translation FROM
{locales_target} WHERE lid = %d AND language = '%s'", $lid, $key)); |
| 844 | 1 | if (!empty($value)) { |
| 845 | | // Only update or insert if we have a value to use. |
| 846 | 1 | if (!empty($translation)) { |
| 847 | 0 | db_query("UPDATE {locales_target} SET translation = '%s' WHERE lid
= %d AND language = '%s'", $value, $lid, $key); |
| 848 | 0 | } |
| 849 | | else { |
| 850 | 1 | db_query("INSERT INTO {locales_target} (lid, translation, language)
VALUES (%d, '%s', '%s')", $lid, $value, $key); |
| 851 | | } |
| 852 | 1 | } |
| 853 | 0 | elseif (!empty($translation)) { |
| 854 | | // Empty translation entered: remove existing entry from database. |
| 855 | 0 | db_query("DELETE FROM {locales_target} WHERE lid = %d AND language =
'%s'", $lid, $key); |
| 856 | 0 | } |
| 857 | | |
| 858 | | // Force JavaScript translation file recreation for this language. |
| 859 | 1 | _locale_invalidate_js($key); |
| 860 | 1 | } |
| 861 | | |
| 862 | 1 | drupal_set_message(t('The string has been saved.')); |
| 863 | | |
| 864 | | // Clear locale cache. |
| 865 | 1 | _locale_invalidate_js(); |
| 866 | 1 | cache_clear_all('locale:', 'cache', TRUE); |
| 867 | | |
| 868 | 1 | $form_state['redirect'] = 'admin/build/translate/search'; |
| 869 | 1 | return; |
| 870 | 0 | } |
| 871 | | /** |
| 872 | | * @} End of "locale-translate-edit" |
| 873 | | */ |
| 874 | | |
| 875 | | /** |
| 876 | | * @defgroup locale-translate-delete Translation delete interface. |
| 877 | | * @{ |
| 878 | | */ |
| 879 | | |
| 880 | | /** |
| 881 | | * String deletion confirmation page. |
| 882 | | */ |
| 883 | 35 | function locale_translate_delete_page($lid) { |
| 884 | 2 | if ($source = db_fetch_object(db_query('SELECT * FROM {locales_source}
WHERE lid = %d', $lid))) { |
| 885 | 2 | return drupal_get_form('locale_translate_delete_form', $source); |
| 886 | 0 | } |
| 887 | | else { |
| 888 | 0 | return drupal_not_found(); |
| 889 | | } |
| 890 | 0 | } |
| 891 | | |
| 892 | | /** |
| 893 | | * User interface for the string deletion confirmation screen. |
| 894 | | */ |
| 895 | 35 | function locale_translate_delete_form(&$form_state, $source) { |
| 896 | 2 | $form['lid'] = array('#type' => 'value', '#value' => $source->lid); |
| 897 | 2 | return confirm_form($form, t('Are you sure you want to delete the string
"%source"?', array('%source' => $source->source)),
'admin/build/translate/search', t('Deleting the string will remove all
translations of this string in all languages. This action cannot be
undone.'), t('Delete'), t('Cancel')); |
| 898 | 0 | } |
| 899 | | |
| 900 | | /** |
| 901 | | * Process string deletion submissions. |
| 902 | | */ |
| 903 | 35 | function locale_translate_delete_form_submit($form, &$form_state) { |
| 904 | 1 | db_query('DELETE FROM {locales_source} WHERE lid = %d',
$form_state['values']['lid']); |
| 905 | 1 | db_query('DELETE FROM {locales_target} WHERE lid = %d',
$form_state['values']['lid']); |
| 906 | | // Force JavaScript translation file recreation for all languages. |
| 907 | 1 | _locale_invalidate_js(); |
| 908 | 1 | cache_clear_all('locale:', 'cache', TRUE); |
| 909 | 1 | drupal_set_message(t('The string has been removed.')); |
| 910 | 1 | $form_state['redirect'] = 'admin/build/translate/search'; |
| 911 | 1 | } |
| 912 | | /** |
| 913 | | * @} End of "locale-translate-delete" |
| 914 | | */ |
| 915 | | |
| 916 | | /** |
| 917 | | * @defgroup locale-api-add Language addition API. |
| 918 | | * @{ |
| 919 | | */ |
| 920 | | |
| 921 | | /** |
| 922 | | * API function to add a language. |
| 923 | | * |
| 924 | | * @param $langcode |
| 925 | | * Language code. |
| 926 | | * @param $name |
| 927 | | * English name of the language |
| 928 | | * @param $native |
| 929 | | * Native name of the language |
| 930 | | * @param $direction |
| 931 | | * LANGUAGE_LTR or LANGUAGE_RTL |
| 932 | | * @param $domain |
| 933 | | * Optional custom domain name with protocol, without |
| 934 | | * trailing slash (eg. http://de.example.com). |
| 935 | | * @param $prefix |
| 936 | | * Optional path prefix for the language. Defaults to the |
| 937 | | * language code if omitted. |
| 938 | | * @param $enabled |
| 939 | | * Optionally TRUE to enable the language when created or FALSE to
disable. |
| 940 | | * @param $default |
| 941 | | * Optionally set this language to be the default. |
| 942 | | */ |
| 943 | 35 | function locale_add_language($langcode, $name = NULL, $native = NULL,
$direction = LANGUAGE_LTR, $domain = '', $prefix = '', $enabled = TRUE,
$default = FALSE) { |
| 944 | | // Default prefix on language code. |
| 945 | 3 | if (empty($prefix)) { |
| 946 | 2 | $prefix = $langcode; |
| 947 | 2 | } |
| 948 | | |
| 949 | | // If name was not set, we add a predefined language. |
| 950 | 3 | if (!isset($name)) { |
| 951 | 2 | $predefined = _locale_get_predefined_list(); |
| 952 | 2 | $name = $predefined[$langcode][0]; |
| 953 | 2 | $native = isset($predefined[$langcode][1]) ? $predefined[$langcode][1]
: $predefined[$langcode][0]; |
| 954 | 2 | $direction = isset($predefined[$langcode][2]) ?
$predefined[$langcode][2] : LANGUAGE_LTR; |
| 955 | 2 | } |
| 956 | | |
| 957 | 3 | db_query("INSERT INTO {languages} (language, name, native, direction,
domain, prefix, enabled) VALUES ('%s', '%s', '%s', %d, '%s', '%s', %d)",
$langcode, $name, $native, $direction, $domain, $prefix, $enabled); |
| 958 | | |
| 959 | | // Only set it as default if enabled. |
| 960 | 3 | if ($enabled && $default) { |
| 961 | 0 | variable_set('language_default', (object) array('language' =>
$langcode, 'name' => $name, 'native' => $native, 'direction' => $direction,
'enabled' => (int) $enabled, 'plurals' => 0, 'formula' => '', 'domain' =>
'', 'prefix' => $prefix, 'weight' => 0, 'javascript' => '')); |
| 962 | 0 | } |
| 963 | | |
| 964 | 3 | if ($enabled) { |
| 965 | | // Increment enabled language count if we are adding an enabled
language. |
| 966 | 3 | variable_set('language_count', variable_get('language_count', 1) + 1); |
| 967 | 3 | } |
| 968 | | |
| 969 | | // Force JavaScript translation file creation for the newly added
language. |
| 970 | 3 | _locale_invalidate_js($langcode); |
| 971 | | |
| 972 | 3 | watchdog('locale', 'The %language language (%code) has been created.',
array('%language' => $name, '%code' => $langcode)); |
| 973 | 3 | } |
| 974 | | /** |
| 975 | | * @} End of "locale-api-add" |
| 976 | | */ |
| 977 | | |
| 978 | | /** |
| 979 | | * @defgroup locale-api-import Translation import API. |
| 980 | | * @{ |
| 981 | | */ |
| 982 | | |
| 983 | | /** |
| 984 | | * Parses Gettext Portable Object file information and inserts into
database |
| 985 | | * |
| 986 | | * @param $file |
| 987 | | * Drupal file object corresponding to the PO file to import |
| 988 | | * @param $langcode |
| 989 | | * Language code |
| 990 | | * @param $mode |
| 991 | | * Should existing translations be replaced LOCALE_IMPORT_KEEP or
LOCALE_IMPORT_OVERWRITE |
| 992 | | * @param $group |
| 993 | | * Text group to import PO file into (eg. 'default' for interface
translations) |
| 994 | | */ |
| 995 | 35 | function _locale_import_po($file, $langcode, $mode, $group = NULL) { |
| 996 | | // If not in 'safe mode', increase the maximum execution time. |
| 997 | 0 | if (!ini_get('safe_mode')) { |
| 998 | 0 | set_time_limit(240); |
| 999 | 0 | } |
| 1000 | | |
| 1001 | | // Check if we have the language already in the database. |
| 1002 | 0 | if (!db_fetch_object(db_query("SELECT language FROM {languages} WHERE
language = '%s'", $langcode))) { |
| 1003 | 0 | drupal_set_message(t('The language selected for import is not
supported.'), 'error'); |
| 1004 | 0 | return FALSE; |
| 1005 | 0 | } |
| 1006 | | |
| 1007 | | // Get strings from file (returns on failure after a partial import, or
on success) |
| 1008 | 0 | $status = _locale_import_read_po('db-store', $file, $mode, $langcode,
$group); |
| 1009 | 0 | if ($status === FALSE) { |
| 1010 | | // Error messages are set in _locale_import_read_po(). |
| 1011 | 0 | return FALSE; |
| 1012 | 0 | } |
| 1013 | | |
| 1014 | | // Get status information on import process. |
| 1015 | 0 | list($headerdone, $additions, $updates, $deletes) =
_locale_import_one_string('db-report'); |
| 1016 | | |
| 1017 | 0 | if (!$headerdone) { |
| 1018 | 0 | drupal_set_message(t('The translation file %filename appears to have a
missing or malformed header.', array('%filename' => $file->filename)),
'error'); |
| 1019 | 0 | } |
| 1020 | | |
| 1021 | | // Clear cache and force refresh of JavaScript translations. |
| 1022 | 0 | _locale_invalidate_js($langcode); |
| 1023 | 0 | cache_clear_all('locale:', 'cache', TRUE); |
| 1024 | | |
| 1025 | | // Rebuild the menu, strings may have changed. |
| 1026 | 0 | menu_rebuild(); |
| 1027 | | |
| 1028 | 0 | drupal_set_message(t('The translation was successfully imported. There
are %number newly created translated strings, %update strings were updated
and %delete strings were removed.', array('%number' => $additions,
'%update' => $updates, '%delete' => $deletes))); |
| 1029 | 0 | watchdog('locale', 'Imported %file into %locale: %number new strings
added, %update updated and %delete removed.', array('%file' =>
$file->filename, '%locale' => $langcode, '%number' => $additions, '%update'
=> $updates, '%delete' => $deletes)); |
| 1030 | 0 | return TRUE; |
| 1031 | 0 | } |
| 1032 | | |
| 1033 | | /** |
| 1034 | | * Parses Gettext Portable Object file into an array |
| 1035 | | * |
| 1036 | | * @param $op |
| 1037 | | * Storage operation type: db-store or mem-store |
| 1038 | | * @param $file |
| 1039 | | * Drupal file object corresponding to the PO file to import |
| 1040 | | * @param $mode |
| 1041 | | * Should existing translations be replaced LOCALE_IMPORT_KEEP or
LOCALE_IMPORT_OVERWRITE |
| 1042 | | * @param $lang |
| 1043 | | * Language code |
| 1044 | | * @param $group |
| 1045 | | * Text group to import PO file into (eg. 'default' for interface
translations) |
| 1046 | | */ |
| 1047 | 35 | function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL,
$group = 'default') { |
| 1048 | | |
| 1049 | 0 | $fd = fopen(DRUPAL_ROOT . '/' . $file->filepath, "rb"); // File will get
closed by PHP on return |
| 1050 | 0 | if (!$fd) { |
| 1051 | 0 | _locale_import_message('The translation import failed, because the file
%filename could not be read.', $file); |
| 1052 | 0 | return FALSE; |
| 1053 | 0 | } |
| 1054 | | |
| 1055 | 0 | $context = "COMMENT"; // Parser context: COMMENT, MSGID, MSGID_PLURAL,
MSGSTR and MSGSTR_ARR |
| 1056 | 0 | $current = array(); // Current entry being read |
| 1057 | 0 | $plural = 0; // Current plural form |
| 1058 | 0 | $lineno = 0; // Current line |
| 1059 | | |
| 1060 | 0 | while (!feof($fd)) { |
| 1061 | 0 | $line = fgets($fd, 10*1024); // A line should not be this long |
| 1062 | 0 | if ($lineno == 0) { |
| 1063 | | // The first line might come with a UTF-8 BOM, which should be
removed. |
| 1064 | 0 | $line = str_replace("\xEF\xBB\xBF", '', $line); |
| 1065 | 0 | } |
| 1066 | 0 | $lineno++; |
| 1067 | 0 | $line = trim(strtr($line, array("\\\n" => ""))); |
| 1068 | | |
| 1069 | 0 | if (!strncmp("#", $line, 1)) { // A comment |
| 1070 | 0 | if ($context == "COMMENT") { // Already in comment context: add |
| 1071 | 0 | $current["#"][] = substr($line, 1); |
| 1072 | 0 | } |
| 1073 | 0 | elseif (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { //
End current entry, start a new one |
| 1074 | 0 | _locale_import_one_string($op, $current, $mode, $lang, $file,
$group); |
| 1075 | 0 | $current = array(); |
| 1076 | 0 | $current["#"][] = substr($line, 1); |
| 1077 | 0 | $context = "COMMENT"; |
| 1078 | 0 | } |
| 1079 | | else { // Parse error |
| 1080 | 0 | _locale_import_message('The translation file %filename contains an
error: "msgstr" was expected but not found on line %line.', $file,
$lineno); |
| 1081 | 0 | return FALSE; |
| 1082 | | } |
| 1083 | 0 | } |
| 1084 | 0 | elseif (!strncmp("msgid_plural", $line, 12)) { |
| 1085 | 0 | if ($context != "MSGID") { // Must be plural form for current entry |
| 1086 | 0 | _locale_import_message('The translation file %filename contains an
error: "msgid_plural" was expected but not found on line %line.', $file,
$lineno); |
| 1087 | 0 | return FALSE; |
| 1088 | 0 | } |
| 1089 | 0 | $line = trim(substr($line, 12)); |
| 1090 | 0 | $quoted = _locale_import_parse_quoted($line); |
| 1091 | 0 | if ($quoted === FALSE) { |
| 1092 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1093 | 0 | return FALSE; |
| 1094 | 0 | } |
| 1095 | 0 | $current["msgid"] = $current["msgid"] . "\0" . $quoted; |
| 1096 | 0 | $context = "MSGID_PLURAL"; |
| 1097 | 0 | } |
| 1098 | 0 | elseif (!strncmp("msgid", $line, 5)) { |
| 1099 | 0 | if ($context == "MSGSTR") { // End current entry, start a new one |
| 1100 | 0 | _locale_import_one_string($op, $current, $mode, $lang, $file,
$group); |
| 1101 | 0 | $current = array(); |
| 1102 | 0 | } |
| 1103 | 0 | elseif ($context == "MSGID") { // Already in this context? Parse
error |
| 1104 | 0 | _locale_import_message('The translation file %filename contains an
error: "msgid" is unexpected on line %line.', $file, $lineno); |
| 1105 | 0 | return FALSE; |
| 1106 | 0 | } |
| 1107 | 0 | $line = trim(substr($line, 5)); |
| 1108 | 0 | $quoted = _locale_import_parse_quoted($line); |
| 1109 | 0 | if ($quoted === FALSE) { |
| 1110 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1111 | 0 | return FALSE; |
| 1112 | 0 | } |
| 1113 | 0 | $current["msgid"] = $quoted; |
| 1114 | 0 | $context = "MSGID"; |
| 1115 | 0 | } |
| 1116 | 0 | elseif (!strncmp("msgstr[", $line, 7)) { |
| 1117 | 0 | if (($context != "MSGID") && ($context != "MSGID_PLURAL") &&
($context != "MSGSTR_ARR")) { // Must come after msgid, msgid_plural, or
msgstr[] |
| 1118 | 0 | _locale_import_message('The translation file %filename contains an
error: "msgstr[]" is unexpected on line %line.', $file, $lineno); |
| 1119 | 0 | return FALSE; |
| 1120 | 0 | } |
| 1121 | 0 | if (strpos($line, "]") === FALSE) { |
| 1122 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1123 | 0 | return FALSE; |
| 1124 | 0 | } |
| 1125 | 0 | $frombracket = strstr($line, "["); |
| 1126 | 0 | $plural = substr($frombracket, 1, strpos($frombracket, "]") - 1); |
| 1127 | 0 | $line = trim(strstr($line, " ")); |
| 1128 | 0 | $quoted = _locale_import_parse_quoted($line); |
| 1129 | 0 | if ($quoted === FALSE) { |
| 1130 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1131 | 0 | return FALSE; |
| 1132 | 0 | } |
| 1133 | 0 | $current["msgstr"][$plural] = $quoted; |
| 1134 | 0 | $context = "MSGSTR_ARR"; |
| 1135 | 0 | } |
| 1136 | 0 | elseif (!strncmp("msgstr", $line, 6)) { |
| 1137 | 0 | if ($context != "MSGID") { // Should come just after a msgid block |
| 1138 | 0 | _locale_import_message('The translation file %filename contains an
error: "msgstr" is unexpected on line %line.', $file, $lineno); |
| 1139 | 0 | return FALSE; |
| 1140 | 0 | } |
| 1141 | 0 | $line = trim(substr($line, 6)); |
| 1142 | 0 | $quoted = _locale_import_parse_quoted($line); |
| 1143 | 0 | if ($quoted === FALSE) { |
| 1144 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1145 | 0 | return FALSE; |
| 1146 | 0 | } |
| 1147 | 0 | $current["msgstr"] = $quoted; |
| 1148 | 0 | $context = "MSGSTR"; |
| 1149 | 0 | } |
| 1150 | 0 | elseif ($line != "") { |
| 1151 | 0 | $quoted = _locale_import_parse_quoted($line); |
| 1152 | 0 | if ($quoted === FALSE) { |
| 1153 | 0 | _locale_import_message('The translation file %filename contains a
syntax error on line %line.', $file, $lineno); |
| 1154 | 0 | return FALSE; |
| 1155 | 0 | } |
| 1156 | 0 | if (($context == "MSGID") || ($context == "MSGID_PLURAL")) { |
| 1157 | 0 | $current["msgid"] .= $quoted; |
| 1158 | 0 | } |
| 1159 | 0 | elseif ($context == "MSGSTR") { |
| 1160 | 0 | $current["msgstr"] .= $quoted; |
| 1161 | 0 | } |
| 1162 | 0 | elseif ($context == "MSGSTR_ARR") { |
| 1163 | 0 | $current["msgstr"][$plural] .= $quoted; |
| 1164 | 0 | } |
| 1165 | | else { |
| 1166 | 0 | _locale_import_message('The translation file %filename contains an
error: there is an unexpected string on line %line.', $file, $lineno); |
| 1167 | 0 | return FALSE; |
| 1168 | | } |
| 1169 | 0 | } |
| 1170 | 0 | } |
| 1171 | | |
| 1172 | | // End of PO file, flush last entry |
| 1173 | 0 | if (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { |
| 1174 | 0 | _locale_import_one_string($op, $current, $mode, $lang, $file, $group); |
| 1175 | 0 | } |
| 1176 | 0 | elseif ($context != "COMMENT") { |
| 1177 | 0 | _locale_import_message('The translation file %filename ended
unexpectedly at line %line.', $file, $lineno); |
| 1178 | 0 | return FALSE; |
| 1179 | 0 | } |
| 1180 | | |
| 1181 | 0 | } |
| 1182 | | |
| 1183 | | /** |
| 1184 | | * Sets an error message occurred during locale file parsing. |
| 1185 | | * |
| 1186 | | * @param $message |
| 1187 | | * The message to be translated |
| 1188 | | * @param $file |
| 1189 | | * Drupal file object corresponding to the PO file to import |
| 1190 | | * @param $lineno |
| 1191 | | * An optional line number argument |
| 1192 | | */ |
| 1193 | 35 | function _locale_import_message($message, $file, $lineno = NULL) { |
| 1194 | 0 | $vars = array('%filename' => $file->filename); |
| 1195 | 0 | if (isset($lineno)) { |
| 1196 | 0 | $vars['%line'] = $lineno; |
| 1197 | 0 | } |
| 1198 | 0 | $t = get_t(); |
| 1199 | 0 | drupal_set_message($t($message, $vars), 'error'); |
| 1200 | 0 | } |
| 1201 | | |
| 1202 | | /** |
| 1203 | | * Imports a string into the database |
| 1204 | | * |
| 1205 | | * @param $op |
| 1206 | | * Operation to perform: 'db-store', 'db-report', 'mem-store' or
'mem-report' |
| 1207 | | * @param $value |
| 1208 | | * Details of the string stored |
| 1209 | | * @param $mode |
| 1210 | | * Should existing translations be replaced LOCALE_IMPORT_KEEP or
LOCALE_IMPORT_OVERWRITE |
| 1211 | | * @param $lang |
| 1212 | | * Language to store the string in |
| 1213 | | * @param $file |
| 1214 | | * Object representation of file being imported, only required when op is
'db-store' |
| 1215 | | * @param $group |
| 1216 | | * Text group to import PO file into (eg. 'default' for interface
translations) |
| 1217 | | */ |
| 1218 | 35 | function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang
= NULL, $file = NULL, $group = 'default') { |
| 1219 | 0 | static $report = array(0, 0, 0); |
| 1220 | 0 | static $headerdone = FALSE; |
| 1221 | 0 | static $strings = array(); |
| 1222 | | |
| 1223 | | switch ($op) { |
| 1224 | | // Return stored strings |
| 1225 | 0 | case 'mem-report': |
| 1226 | 0 | return $strings; |
| 1227 | | |
| 1228 | | // Store string in memory (only supports single strings) |
| 1229 | 0 | case 'mem-store': |
| 1230 | 0 | $strings[$value['msgid']] = $value['msgstr']; |
| 1231 | 0 | return; |
| 1232 | | |
| 1233 | | // Called at end of import to inform the user |
| 1234 | 0 | case 'db-report': |
| 1235 | 0 | return array($headerdone, $report[0], $report[1], $report[2]); |
| 1236 | | |
| 1237 | | // Store the string we got in the database. |
| 1238 | 0 | case 'db-store': |
| 1239 | | // We got header information. |
| 1240 | 0 | if ($value['msgid'] == '') { |
| 1241 | 0 | $header = _locale_import_parse_header($value['msgstr']); |
| 1242 | | |
| 1243 | | // Get the plural formula and update in database. |
| 1244 | 0 | if (isset($header["Plural-Forms"]) && $p =
_locale_import_parse_plural_forms($header["Plural-Forms"],
$file->filename)) { |
| 1245 | 0 | list($nplurals, $plural) = $p; |
| 1246 | 0 | db_query("UPDATE {languages} SET plurals = %d, formula = '%s'
WHERE language = '%s'", $nplurals, $plural, $lang); |
| 1247 | 0 | } |
| 1248 | | else { |
| 1249 | 0 | db_query("UPDATE {languages} SET plurals = %d, formula = '%s'
WHERE language = '%s'", 0, '', $lang); |
| 1250 | | } |
| 1251 | 0 | $headerdone = TRUE; |
| 1252 | 0 | } |
| 1253 | | |
| 1254 | | else { |
| 1255 | | // Some real string to import. |
| 1256 | 0 | $comments = _locale_import_shorten_comments(empty($value['#']) ?
array() : $value['#']); |
| 1257 | | |
| 1258 | 0 | if (strpos($value['msgid'], "\0")) { |
| 1259 | | // This string has plural versions. |
| 1260 | 0 | $english = explode("\0", $value['msgid'], 2); |
| 1261 | 0 | $entries = array_keys($value['msgstr']); |
| 1262 | 0 | for ($i = 3; $i <= count($entries); $i++) { |
| 1263 | 0 | $english[] = $english[1]; |
| 1264 | 0 | } |
| 1265 | 0 | $translation = array_map('_locale_import_append_plural',
$value['msgstr'], $entries); |
| 1266 | 0 | $english = array_map('_locale_import_append_plural', $english,
$entries); |
| 1267 | 0 | foreach ($translation as $key => $trans) { |
| 1268 | 0 | if ($key == 0) { |
| 1269 | 0 | $plid = 0; |
| 1270 | 0 | } |
| 1271 | 0 | $plid = _locale_import_one_string_db($report, $lang,
$english[$key], $trans, $group, $comments, $mode, $plid, $key); |
| 1272 | 0 | } |
| 1273 | 0 | } |
| 1274 | | |
| 1275 | | else { |
| 1276 | | // A simple string to import. |
| 1277 | 0 | $english = $value['msgid']; |
| 1278 | 0 | $translation = $value['msgstr']; |
| 1279 | 0 | _locale_import_one_string_db($report, $lang, $english,
$translation, $group, $comments, $mode); |
| 1280 | | } |
| 1281 | | } |
| 1282 | 0 | } // end of db-store operation |
| 1283 | 0 | } |
| 1284 | | |
| 1285 | | /** |
| 1286 | | * Import one string into the database. |
| 1287 | | * |
| 1288 | | * @param $report |
| 1289 | | * Report array summarizing the number of changes done in the form: |
| 1290 | | * array(inserts, updates, deletes). |
| 1291 | | * @param $langcode |
| 1292 | | * Language code to import string into. |
| 1293 | | * @param $source |
| 1294 | | * Source string. |
| 1295 | | * @param $translation |
| 1296 | | * Translation to language specified in $langcode. |
| 1297 | | * @param $textgroup |
| 1298 | | * Name of textgroup to store translation in. |
| 1299 | | * @param $location |
| 1300 | | * Location value to save with source string. |
| 1301 | | * @param $mode |
| 1302 | | * Import mode to use, LOCALE_IMPORT_KEEP or LOCALE_IMPORT_OVERWRITE. |
| 1303 | | * @param $plid |
| 1304 | | * Optional plural ID to use. |
| 1305 | | * @param $plural |
| 1306 | | * Optional plural value to use. |
| 1307 | | * @return |
| 1308 | | * The string ID of the existing string modified or the new string added. |
| 1309 | | */ |
| 1310 | 35 | function _locale_import_one_string_db(&$report, $langcode, $source,
$translation, $textgroup, $location, $mode, $plid = NULL, $plural = NULL) { |
| 1311 | 0 | $lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE source
= '%s' AND textgroup = '%s'", $source, $textgroup)); |
| 1312 | | |
| 1313 | 0 | if (!empty($translation)) { |
| 1314 | 0 | if ($lid) { |
| 1315 | | // We have this source string saved already. |
| 1316 | 0 | db_query("UPDATE {locales_source} SET location = '%s' WHERE lid =
%d", $location, $lid); |
| 1317 | 0 | $exists = (bool) db_result(db_query("SELECT lid FROM {locales_target}
WHERE lid = %d AND language = '%s'", $lid, $langcode)); |
| 1318 | 0 | if (!$exists) { |
| 1319 | | // No translation in this language. |
| 1320 | 0 | db_query("INSERT INTO {locales_target} (lid, language, translation,
plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $langcode,
$translation, $plid, $plural); |
| 1321 | 0 | $report[0]++; |
| 1322 | 0 | } |
| 1323 | 0 | elseif ($mode == LOCALE_IMPORT_OVERWRITE) { |
| 1324 | | // Translation exists, only overwrite if instructed. |
| 1325 | 0 | db_query("UPDATE {locales_target} SET translation = '%s', plid =
%d, plural = %d WHERE language = '%s' AND lid = %d", $translation, $plid,
$plural, $langcode, $lid); |
| 1326 | 0 | $report[1]++; |
| 1327 | 0 | } |
| 1328 | 0 | } |
| 1329 | | else { |
| 1330 | | // No such source string in the database yet. |
| 1331 | 0 | db_query("INSERT INTO {locales_source} (location, source, textgroup)
VALUES ('%s', '%s', '%s')", $location, $source, $textgroup); |
| 1332 | 0 | $lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE
source = '%s' AND textgroup = '%s'", $source, $textgroup)); |
| 1333 | 0 | db_query("INSERT INTO {locales_target} (lid, language, translation,
plid, plural) VALUES (%d, '%s', '%s', %d, %d)", $lid, $langcode,
$translation, $plid, $plural); |
| 1334 | 0 | $report[0]++; |
| 1335 | | } |
| 1336 | 0 | } |
| 1337 | 0 | elseif ($mode == LOCALE_IMPORT_OVERWRITE) { |
| 1338 | | // Empty translation, remove existing if instructed. |
| 1339 | 0 | db_query("DELETE FROM {locales_target} WHERE language = '%s' AND lid =
%d AND plid = %d AND plural = %d", $translation, $langcode, $lid, $plid,
$plural); |
| 1340 | 0 | $report[2]++; |
| 1341 | 0 | } |
| 1342 | | |
| 1343 | 0 | return $lid; |
| 1344 | 0 | } |
| 1345 | | |
| 1346 | | /** |
| 1347 | | * Parses a Gettext Portable Object file header |
| 1348 | | * |
| 1349 | | * @param $header |
| 1350 | | * A string containing the complete header |
| 1351 | | * @return |
| 1352 | | * An associative array of key-value pairs |
| 1353 | | */ |
| 1354 | 35 | function _locale_import_parse_header($header) { |
| 1355 | 0 | $header_parsed = array(); |
| 1356 | 0 | $lines = array_map('trim', explode("\n", $header)); |
| 1357 | 0 | foreach ($lines as $line) { |
| 1358 | 0 | if ($line) { |
| 1359 | 0 | list($tag, $contents) = explode(":", $line, 2); |
| 1360 | 0 | $header_parsed[trim($tag)] = trim($contents); |
| 1361 | 0 | } |
| 1362 | 0 | } |
| 1363 | 0 | return $header_parsed; |
| 1364 | 0 | } |
| 1365 | | |
| 1366 | | /** |
| 1367 | | * Parses a Plural-Forms entry from a Gettext Portable Object file header |
| 1368 | | * |
| 1369 | | * @param $pluralforms |
| 1370 | | * A string containing the Plural-Forms entry |
| 1371 | | * @param $filename |
| 1372 | | * A string containing the filename |
| 1373 | | * @return |
| 1374 | | * An array containing the number of plurals and a |
| 1375 | | * formula in PHP for computing the plural form |
| 1376 | | */ |
| 1377 | 35 | function _locale_import_parse_plural_forms($pluralforms, $filename) { |
| 1378 | | // First, delete all whitespace |
| 1379 | 0 | $pluralforms = strtr($pluralforms, array(" " => "", "\t" => "")); |
| 1380 | | |
| 1381 | | // Select the parts that define nplurals and plural |
| 1382 | 0 | $nplurals = strstr($pluralforms, "nplurals="); |
| 1383 | 0 | if (strpos($nplurals, ";")) { |
| 1384 | 0 | $nplurals = substr($nplurals, 9, strpos($nplurals, ";") - 9); |
| 1385 | 0 | } |
| 1386 | | else { |
| 1387 | 0 | return FALSE; |
| 1388 | | } |
| 1389 | 0 | $plural = strstr($pluralforms, "plural="); |
| 1390 | 0 | if (strpos($plural, ";")) { |
| 1391 | 0 | $plural = substr($plural, 7, strpos($plural, ";") - 7); |
| 1392 | 0 | } |
| 1393 | | else { |
| 1394 | 0 | return FALSE; |
| 1395 | | } |
| 1396 | | |
| 1397 | | // Get PHP version of the plural formula |
| 1398 | 0 | $plural = _locale_import_parse_arithmetic($plural); |
| 1399 | | |
| 1400 | 0 | if ($plural !== FALSE) { |
| 1401 | 0 | return array($nplurals, $plural); |
| 1402 | 0 | } |
| 1403 | | else { |
| 1404 | 0 | drupal_set_message(t('The translation file %filename contains an error:
the plural formula could not be parsed.', array('%filename' => $filename)),
'error'); |
| 1405 | 0 | return FALSE; |
| 1406 | | } |
| 1407 | 0 | } |
| 1408 | | |
| 1409 | | /** |
| 1410 | | * Parses and sanitizes an arithmetic formula into a PHP expression |
| 1411 | | * |
| 1412 | | * While parsing, we ensure, that the operators have the right |
| 1413 | | * precedence and associativity. |
| 1414 | | * |
| 1415 | | * @param $string |
| 1416 | | * A string containing the arithmetic formula |
| 1417 | | * @return |
| 1418 | | * The PHP version of the formula |
| 1419 | | */ |
| 1420 | 35 | function _locale_import_parse_arithmetic($string) { |
| 1421 | | // Operator precedence table |
| 1422 | 0 | $prec = array("(" => -1, ")" => -1, "?" => 1, ":" => 1, "||" => 3, "&&"
=> 4, "==" => 5, "!=" => 5, "<" => 6, ">" => 6, "<=" => 6, ">=" => 6, "+"
=> 7, "-" => 7, "*" => 8, "/" => 8, "%" => 8); |
| 1423 | | // Right associativity |
| 1424 | 0 | $rasc = array("?" => 1, ":" => 1); |
| 1425 | | |
| 1426 | 0 | $tokens = _locale_import_tokenize_formula($string); |
| 1427 | | |
| 1428 | | // Parse by converting into infix notation then back into postfix |
| 1429 | 0 | $opstk = array(); |
| 1430 | 0 | $elstk = array(); |
| 1431 | | |
| 1432 | 0 | foreach ($tokens as $token) { |
| 1433 | 0 | $ctok = $token; |
| 1434 | | |
| 1435 | | // Numbers and the $n variable are simply pushed into $elarr |
| 1436 | 0 | if (is_numeric($token)) { |
| 1437 | 0 | $elstk[] = $ctok; |
| 1438 | 0 | } |
| 1439 | 0 | elseif ($ctok == "n") { |
| 1440 | 0 | $elstk[] = '$n'; |
| 1441 | 0 | } |
| 1442 | 0 | elseif ($ctok == "(") { |
| 1443 | 0 | $opstk[] = $ctok; |
| 1444 | 0 | } |
| 1445 | 0 | elseif ($ctok == ")") { |
| 1446 | 0 | $topop = array_pop($opstk); |
| 1447 | 0 | while (isset($topop) && ($topop != "(")) { |
| 1448 | 0 | $elstk[] = $topop; |
| 1449 | 0 | $topop = array_pop($opstk); |
| 1450 | 0 | } |
| 1451 | 0 | } |
| 1452 | 0 | elseif (!empty($prec[$ctok])) { |
| 1453 | | // If it's an operator, then pop from $oparr into $elarr until the |
| 1454 | | // precedence in $oparr is less than current, then push into $oparr |
| 1455 | 0 | $topop = array_pop($opstk); |
| 1456 | 0 | while (isset($topop) && ($prec[$topop] >= $prec[$ctok]) &&
!(($prec[$topop] == $prec[$ctok]) && !empty($rasc[$topop]) &&
!empty($rasc[$ctok]))) { |
| 1457 | 0 | $elstk[] = $topop; |
| 1458 | 0 | $topop = array_pop($opstk); |
| 1459 | 0 | } |
| 1460 | 0 | if ($topop) { |
| 1461 | 0 | $opstk[] = $topop; // Return element to top |
| 1462 | 0 | } |
| 1463 | 0 | $opstk[] = $ctok; // Parentheses are not needed |
| 1464 | 0 | } |
| 1465 | | else { |
| 1466 | 0 | return FALSE; |
| 1467 | | } |
| 1468 | 0 | } |
| 1469 | | |
| 1470 | | // Flush operator stack |
| 1471 | 0 | $topop = array_pop($opstk); |
| 1472 | 0 | while ($topop != NULL) { |
| 1473 | 0 | $elstk[] = $topop; |
| 1474 | 0 | $topop = array_pop($opstk); |
| 1475 | 0 | } |
| 1476 | | |
| 1477 | | // Now extract formula from stack |
| 1478 | 0 | $prevsize = count($elstk) + 1; |
| 1479 | 0 | while (count($elstk) < $prevsize) { |
| 1480 | 0 | $prevsize = count($elstk); |
| 1481 | 0 | for ($i = 2; $i < count($elstk); $i++) { |
| 1482 | 0 | $op = $elstk[$i]; |
| 1483 | 0 | if (!empty($prec[$op])) { |
| 1484 | 0 | $f = ""; |
| 1485 | 0 | if ($op == ":") { |
| 1486 | 0 | $f = $elstk[$i - 2] . "):" . $elstk[$i - 1] . ")"; |
| 1487 | 0 | } |
| 1488 | 0 | elseif ($op == "?") { |
| 1489 | 0 | $f = "(" . $elstk[$i - 2] . "?(" . $elstk[$i - 1]; |
| 1490 | 0 | } |
| 1491 | | else { |
| 1492 | 0 | $f = "(" . $elstk[$i - 2] . $op . $elstk[$i - 1] . ")"; |
| 1493 | | } |
| 1494 | 0 | array_splice($elstk, $i - 2, 3, $f); |
| 1495 | 0 | break; |
| 1496 | 0 | } |
| 1497 | 0 | } |
| 1498 | 0 | } |
| 1499 | | |
| 1500 | | // If only one element is left, the number of operators is appropriate |
| 1501 | 0 | if (count($elstk) == 1) { |
| 1502 | 0 | return $elstk[0]; |
| 1503 | 0 | } |
| 1504 | | else { |
| 1505 | 0 | return FALSE; |
| 1506 | | } |
| 1507 | 0 | } |
| 1508 | | |
| 1509 | | /** |
| 1510 | | * Backward compatible implementation of token_get_all() for formula
parsing |
| 1511 | | * |
| 1512 | | * @param $string |
| 1513 | | * A string containing the arithmetic formula |
| 1514 | | * @return |
| 1515 | | * The PHP version of the formula |
| 1516 | | */ |
| 1517 | 35 | function _locale_import_tokenize_formula($formula) { |
| 1518 | 0 | $formula = str_replace(" ", "", $formula); |
| 1519 | 0 | $tokens = array(); |
| 1520 | 0 | for ($i = 0; $i < strlen($formula); $i++) { |
| 1521 | 0 | if (is_numeric($formula[$i])) { |
| 1522 | 0 | $num = $formula[$i]; |
| 1523 | 0 | $j = $i + 1; |
| 1524 | 0 | while ($j < strlen($formula) && is_numeric($formula[$j])) { |
| 1525 | 0 | $num .= $formula[$j]; |
| 1526 | 0 | $j++; |
| 1527 | 0 | } |
| 1528 | 0 | $i = $j - 1; |
| 1529 | 0 | $tokens[] = $num; |
| 1530 | 0 | } |
| 1531 | 0 | elseif ($pos = strpos(" =<>!&|", $formula[$i])) { // We won't have a
space |
| 1532 | 0 | $next = $formula[$i + 1]; |
| 1533 | | switch ($pos) { |
| 1534 | 0 | case 1: |
| 1535 | 0 | case 2: |
| 1536 | 0 | case 3: |
| 1537 | 0 | case 4: |
| 1538 | 0 | if ($next == '=') { |
| 1539 | 0 | $tokens[] = $formula[$i] . '='; |
| 1540 | 0 | $i++; |
| 1541 | 0 | } |
| 1542 | | else { |
| 1543 | 0 | $tokens[] = $formula[$i]; |
| 1544 | | } |
| 1545 | 0 | break; |
| 1546 | 0 | case 5: |
| 1547 | 0 | if ($next == '&') { |
| 1548 | 0 | $tokens[] = '&&'; |
| 1549 | 0 | $i++; |
| 1550 | 0 | } |
| 1551 | | else { |
| 1552 | 0 | $tokens[] = $formula[$i]; |
| 1553 | | } |
| 1554 | 0 | break; |
| 1555 | 0 | case 6: |
| 1556 | 0 | if ($next == '|') { |
| 1557 | 0 | $tokens[] = '||'; |
| 1558 | 0 | $i++; |
| 1559 | 0 | } |
| 1560 | | else { |
| 1561 | 0 | $tokens[] = $formula[$i]; |
| 1562 | | } |
| 1563 | 0 | break; |
| 1564 | 0 | } |
| 1565 | 0 | } |
| 1566 | | else { |
| 1567 | 0 | $tokens[] = $formula[$i]; |
| 1568 | | } |
| 1569 | 0 | } |
| 1570 | 0 | return $tokens; |
| 1571 | 0 | } |
| 1572 | | |
| 1573 | | /** |
| 1574 | | * Modify a string to contain proper count indices |
| 1575 | | * |
| 1576 | | * This is a callback function used via array_map() |
| 1577 | | * |
| 1578 | | * @param $entry |
| 1579 | | * An array element |
| 1580 | | * @param $key |
| 1581 | | * Index of the array element |
| 1582 | | */ |
| 1583 | 35 | function _locale_import_append_plural($entry, $key) { |
| 1584 | | // No modifications for 0, 1 |
| 1585 | 0 | if ($key == 0 || $key == 1) { |
| 1586 | 0 | return $entry; |
| 1587 | 0 | } |
| 1588 | | |
| 1589 | | // First remove any possibly false indices, then add new ones |
| 1590 | 0 | $entry = preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry); |
| 1591 | 0 | return preg_replace('/(@count)/', "\\1[$key]", $entry); |
| 1592 | 0 | } |
| 1593 | | |
| 1594 | | /** |
| 1595 | | * Generate a short, one string version of the passed comment array |
| 1596 | | * |
| 1597 | | * @param $comment |
| 1598 | | * An array of strings containing a comment |
| 1599 | | * @return |
| 1600 | | * Short one string version of the comment |
| 1601 | | */ |
| 1602 | 35 | function _locale_import_shorten_comments($comment) { |
| 1603 | 0 | $comm = ''; |
| 1604 | 0 | while (count($comment)) { |
| 1605 | 0 | $test = $comm . substr(array_shift($comment), 1) . ', '; |
| 1606 | 0 | if (strlen($comm) < 130) { |
| 1607 | 0 | $comm = $test; |
| 1608 | 0 | } |
| 1609 | | else { |
| 1610 | 0 | break; |
| 1611 | | } |
| 1612 | 0 | } |
| 1613 | 0 | return substr($comm, 0, -2); |
| 1614 | 0 | } |
| 1615 | | |
| 1616 | | /** |
| 1617 | | * Parses a string in quotes |
| 1618 | | * |
| 1619 | | * @param $string |
| 1620 | | * A string specified with enclosing quotes |
| 1621 | | * @return |
| 1622 | | * The string parsed from inside the quotes |
| 1623 | | */ |
| 1624 | 35 | function _locale_import_parse_quoted($string) { |
| 1625 | 0 | if (substr($string, 0, 1) != substr($string, -1, 1)) { |
| 1626 | 0 | return FALSE; // Start and end quotes must be the same |
| 1627 | 0 | } |
| 1628 | 0 | $quote = substr($string, 0, 1); |
| 1629 | 0 | $string = substr($string, 1, -1); |
| 1630 | 0 | if ($quote == '"') { // Double quotes: strip slashes |
| 1631 | 0 | return stripcslashes($string); |
| 1632 | 0 | } |
| 1633 | 0 | elseif ($quote == "'") { // Simple quote: return as-is |
| 1634 | 0 | return $string; |
| 1635 | 0 | } |
| 1636 | | else { |
| 1637 | 0 | return FALSE; // Unrecognized quote |
| 1638 | | } |
| 1639 | 0 | } |
| 1640 | | /** |
| 1641 | | * @} End of "locale-api-import" |
| 1642 | | */ |
| 1643 | | |
| 1644 | | /** |
| 1645 | | * Parses a JavaScript file, extracts strings wrapped in Drupal.t() and |
| 1646 | | * Drupal.formatPlural() and inserts them into the database. |
| 1647 | | */ |
| 1648 | 35 | function _locale_parse_js_file($filepath) { |
| 1649 | 11 | global $language; |
| 1650 | | |
| 1651 | | // Load the JavaScript file. |
| 1652 | 11 | $file = file_get_contents($filepath); |
| 1653 | | |
| 1654 | | // Match all calls to Drupal.t() in an array. |
| 1655 | | // Note: \s also matches newlines with the 's' modifier. |
| 1656 | 11 | preg_match_all('~[^\w]Drupal\s*\.\s*t\s*\(\s*(' . LOCALE_JS_STRING .
')\s*[,\)]~s', $file, $t_matches); |
| 1657 | | |
| 1658 | | // Match all Drupal.formatPlural() calls in another array. |
| 1659 | 11 | preg_match_all('~[^\w]Drupal\s*\.\s*formatPlural\s*\(\s*.+?\s*,\s*(' .
LOCALE_JS_STRING .
')\s*,\s*((?:(?:\'(?:\\\\\'|[^\'])*@count(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*@count(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+)\s*[,\)]~s',
$file, $plural_matches); |
| 1660 | | |
| 1661 | | // Loop through all matches and process them. |
| 1662 | 11 | $all_matches = array_merge($plural_matches[1], $t_matches[1]); |
| 1663 | 11 | foreach ($all_matches as $key => $string) { |
| 1664 | 8 | $strings = array($string); |
| 1665 | | |
| 1666 | | // If there is also a plural version of this string, add it to the
strings array. |
| 1667 | 8 | if (isset($plural_matches[2][$key])) { |
| 1668 | 0 | $strings[] = $plural_matches[2][$key]; |
| 1669 | 0 | } |
| 1670 | | |
| 1671 | 8 | foreach ($strings as $key => $string) { |
| 1672 | | // Remove the quotes and string concatenations from the string. |
| 1673 | 8 | $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s',
substr($string, 1, -1))); |
| 1674 | | |
| 1675 | 8 | $result = db_query("SELECT lid, location FROM {locales_source} WHERE
source = '%s' AND textgroup = 'default'", $string); |
| 1676 | 8 | if ($source = db_fetch_object($result)) { |
| 1677 | | // We already have this source string and now have to add the
location |
| 1678 | | // to the location column, if this file is not yet present in
there. |
| 1679 | 2 | $locations = preg_split('~\s*;\s*~', $source->location); |
| 1680 | | |
| 1681 | 2 | if (!in_array($filepath, $locations)) { |
| 1682 | 0 | $locations[] = $filepath; |
| 1683 | 0 | $locations = implode('; ', $locations); |
| 1684 | | |
| 1685 | | // Save the new locations string to the database. |
| 1686 | 0 | db_query("UPDATE {locales_source} SET location = '%s' WHERE lid =
%d", $locations, $source->lid); |
| 1687 | 0 | } |
| 1688 | 2 | } |
| 1689 | | else { |
| 1690 | | // We don't have the source string yet, thus we insert it into the
database. |
| 1691 | 8 | db_query("INSERT INTO {locales_source} (location, source,
textgroup) VALUES ('%s', '%s', 'default')", $filepath, $string); |
| 1692 | | } |
| 1693 | 8 | } |
| 1694 | 8 | } |
| 1695 | 11 | } |
| 1696 | | |
| 1697 | | /** |
| 1698 | | * @defgroup locale-api-export Translation (template) export API. |
| 1699 | | * @{ |
| 1700 | | */ |
| 1701 | | |
| 1702 | | /** |
| 1703 | | * Generates a structured array of all strings with translations in |
| 1704 | | * $language, if given. This array can be used to generate an export |
| 1705 | | * of the string in the database. |
| 1706 | | * |
| 1707 | | * @param $language |
| 1708 | | * Language object to generate the output for, or NULL if generating |
| 1709 | | * translation template. |
| 1710 | | * @param $group |
| 1711 | | * Text group to export PO file from (eg. 'default' for interface
translations) |
| 1712 | | */ |
| 1713 | 35 | function _locale_export_get_strings($language = NULL, $group = 'default') { |
| 1714 | 0 | if (isset($language)) { |
| 1715 | 0 | $result = db_query("SELECT s.lid, s.source, s.location, t.translation,
t.plid, t.plural FROM {locales_source} s LEFT JOIN {locales_target} t ON
s.lid = t.lid AND t.language = '%s' WHERE s.textgroup = '%s' ORDER BY
t.plid, t.plural", $language->language, $group); |
| 1716 | 0 | } |
| 1717 | | else { |
| 1718 | 0 | $result = db_query("SELECT s.lid, s.source, s.location, t.plid,
t.plural FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid =
t.lid WHERE s.textgroup = '%s' ORDER BY t.plid, t.plural", $group); |
| 1719 | | } |
| 1720 | 0 | $strings = array(); |
| 1721 | 0 | while ($child = db_fetch_object($result)) { |
| 1722 | | $string = array( |
| 1723 | 0 | 'comment' => $child->location, |
| 1724 | 0 | 'source' => $child->source, |
| 1725 | 0 | 'translation' => isset($child->translation) ? $child->translation :
'' |
| 1726 | 0 | ); |
| 1727 | 0 | if ($child->plid) { |
| 1728 | | // Has a parent lid. Since we process in the order of plids, |
| 1729 | | // we already have the parent in the array, so we can add the |
| 1730 | | // lid to the next plural version to it. This builds a linked |
| 1731 | | // list of plurals. |
| 1732 | 0 | $string['child'] = TRUE; |
| 1733 | 0 | $strings[$child->plid]['plural'] = $child->lid; |
| 1734 | 0 | } |
| 1735 | 0 | $strings[$child->lid] = $string; |
| 1736 | 0 | } |
| 1737 | 0 | return $strings; |
| 1738 | 0 | } |
| 1739 | | |
| 1740 | | /** |
| 1741 | | * Generates the PO(T) file contents for given strings. |
| 1742 | | * |
| 1743 | | * @param $language |
| 1744 | | * Language object to generate the output for, or NULL if generating |
| 1745 | | * translation template. |
| 1746 | | * @param $strings |
| 1747 | | * Array of strings to export. See _locale_export_get_strings() |
| 1748 | | * on how it should be formatted. |
| 1749 | | * @param $header |
| 1750 | | * The header portion to use for the output file. Defaults |
| 1751 | | * are provided for PO and POT files. |
| 1752 | | */ |
| 1753 | 35 | function _locale_export_po_generate($language = NULL, $strings = array(),
$header = NULL) { |
| 1754 | 0 | global $user; |
| 1755 | | |
| 1756 | 0 | if (!isset($header)) { |
| 1757 | 0 | if (isset($language)) { |
| 1758 | 0 | $header = '# ' . $language->name . ' translation of ' .
variable_get('site_name', 'Drupal') . "\n"; |
| 1759 | 0 | $header .= '# Generated by ' . $user->name . ' <' . $user->mail .
">\n"; |
| 1760 | 0 | $header .= "#\n"; |
| 1761 | 0 | $header .= "msgid \"\"\n"; |
| 1762 | 0 | $header .= "msgstr \"\"\n"; |
| 1763 | 0 | $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; |
| 1764 | 0 | $header .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n"; |
| 1765 | 0 | $header .= "\"PO-Revision-Date: " . date("Y-m-d H:iO") . "\\n\"\n"; |
| 1766 | 0 | $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n"; |
| 1767 | 0 | $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n"; |
| 1768 | 0 | $header .= "\"MIME-Version: 1.0\\n\"\n"; |
| 1769 | 0 | $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; |
| 1770 | 0 | $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; |
| 1771 | 0 | if ($language->formula && $language->plurals) { |
| 1772 | 0 | $header .= "\"Plural-Forms: nplurals=" . $language->plurals . ";
plural=" . strtr($language->formula, array('$' => '')) . ";\\n\"\n"; |
| 1773 | 0 | } |
| 1774 | 0 | } |
| 1775 | | else { |
| 1776 | 0 | $header = "# LANGUAGE translation of PROJECT\n"; |
| 1777 | 0 | $header .= "# Copyright (c) YEAR NAME <EMAIL@ADDRESS>\n"; |
| 1778 | 0 | $header .= "#\n"; |
| 1779 | 0 | $header .= "msgid \"\"\n"; |
| 1780 | 0 | $header .= "msgstr \"\"\n"; |
| 1781 | 0 | $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; |
| 1782 | 0 | $header .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n"; |
| 1783 | 0 | $header .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n"; |
| 1784 | 0 | $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n"; |
| 1785 | 0 | $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n"; |
| 1786 | 0 | $header .= "\"MIME-Version: 1.0\\n\"\n"; |
| 1787 | 0 | $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; |
| 1788 | 0 | $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; |
| 1789 | 0 | $header .= "\"Plural-Forms: nplurals=INTEGER;
plural=EXPRESSION;\\n\"\n"; |
| 1790 | | } |
| 1791 | 0 | } |
| 1792 | | |
| 1793 | 0 | $output = $header . "\n"; |
| 1794 | | |
| 1795 | 0 | foreach ($strings as $lid => $string) { |
| 1796 | | // Only process non-children, children are output below their parent. |
| 1797 | 0 | if (!isset($string['child'])) { |
| 1798 | 0 | if ($string['comment']) { |
| 1799 | 0 | $output .= '#: ' . $string['comment'] . "\n"; |
| 1800 | 0 | } |
| 1801 | 0 | $output .= 'msgid ' . _locale_export_string($string['source']); |
| 1802 | 0 | if (!empty($string['plural'])) { |
| 1803 | 0 | $plural = $string['plural']; |
| 1804 | 0 | $output .= 'msgid_plural ' .
_locale_export_string($strings[$plural]['source']); |
| 1805 | 0 | if (isset($language)) { |
| 1806 | 0 | $translation = $string['translation']; |
| 1807 | 0 | for ($i = 0; $i < $language->plurals; $i++) { |
| 1808 | 0 | $output .= 'msgstr[' . $i . '] ' .
_locale_export_string($translation); |
| 1809 | 0 | if ($plural) { |
| 1810 | 0 | $translation =
_locale_export_remove_plural($strings[$plural]['translation']); |
| 1811 | 0 | $plural = isset($strings[$plural]['plural']) ?
$strings[$plural]['plural'] : 0; |
| 1812 | 0 | } |
| 1813 | | else { |
| 1814 | 0 | $translation = ''; |
| 1815 | | } |
| 1816 | 0 | } |
| 1817 | 0 | } |
| 1818 | | else { |
| 1819 | 0 | $output .= 'msgstr[0] ""' . "\n"; |
| 1820 | 0 | $output .= 'msgstr[1] ""' . "\n"; |
| 1821 | | } |
| 1822 | 0 | } |
| 1823 | | else { |
| 1824 | 0 | $output .= 'msgstr ' .
_locale_export_string($string['translation']); |
| 1825 | | } |
| 1826 | 0 | $output .= "\n"; |
| 1827 | 0 | } |
| 1828 | 0 | } |
| 1829 | 0 | return $output; |
| 1830 | 0 | } |
| 1831 | | |
| 1832 | | /** |
| 1833 | | * Write a generated PO or POT file to the output. |
| 1834 | | * |
| 1835 | | * @param $language |
| 1836 | | * Language object to generate the output for, or NULL if generating |
| 1837 | | * translation template. |
| 1838 | | * @param $output |
| 1839 | | * The PO(T) file to output as a string. See _locale_export_generate_po() |
| 1840 | | * on how it can be generated. |
| 1841 | | */ |
| 1842 | 35 | function _locale_export_po($language = NULL, $output = NULL) { |
| 1843 | | // Log the export event. |
| 1844 | 0 | if (isset($language)) { |
| 1845 | 0 | $filename = $language->language . '.po'; |
| 1846 | 0 | watchdog('locale', 'Exported %locale translation file: %filename.',
array('%locale' => $language->name, '%filename' => $filename)); |
| 1847 | 0 | } |
| 1848 | | else { |
| 1849 | 0 | $filename = 'drupal.pot'; |
| 1850 | 0 | watchdog('locale', 'Exported translation file: %filename.',
array('%filename' => $filename)); |
| 1851 | | } |
| 1852 | | // Download the file fo the client. |
| 1853 | 0 | header("Content-Disposition: attachment; filename=$filename"); |
| 1854 | 0 | header("Content-Type: text/plain; charset=utf-8"); |
| 1855 | 0 | print $output; |
| 1856 | 0 | die(); |
| 1857 | 0 | } |
| 1858 | | |
| 1859 | | /** |
| 1860 | | * Print out a string on multiple lines |
| 1861 | | */ |
| 1862 | 35 | function _locale_export_string($str) { |
| 1863 | 0 | $stri = addcslashes($str, "\0..\37\\\""); |
| 1864 | 0 | $parts = array(); |
| 1865 | | |
| 1866 | | // Cut text into several lines |
| 1867 | 0 | while ($stri != "") { |
| 1868 | 0 | $i = strpos($stri, "\\n"); |
| 1869 | 0 | if ($i === FALSE) { |
| 1870 | 0 | $curstr = $stri; |
| 1871 | 0 | $stri = ""; |
| 1872 | 0 | } |
| 1873 | | else { |
| 1874 | 0 | $curstr = substr($stri, 0, $i + 2); |
| 1875 | 0 | $stri = substr($stri, $i + 2); |
| 1876 | | } |
| 1877 | 0 | $curparts = explode("\n", _locale_export_wrap($curstr, 70)); |
| 1878 | 0 | $parts = array_merge($parts, $curparts); |
| 1879 | 0 | } |
| 1880 | | |
| 1881 | | // Multiline string |
| 1882 | 0 | if (count($parts) > 1) { |
| 1883 | 0 | return "\"\"\n\"" . implode("\"\n\"", $parts) . "\"\n"; |
| 1884 | 0 | } |
| 1885 | | // Single line string |
| 1886 | 0 | elseif (count($parts) == 1) { |
| 1887 | 0 | return "\"$parts[0]\"\n"; |
| 1888 | 0 | } |
| 1889 | | // No translation |
| 1890 | | else { |
| 1891 | 0 | return "\"\"\n"; |
| 1892 | | } |
| 1893 | 0 | } |
| 1894 | | |
| 1895 | | /** |
| 1896 | | * Custom word wrapping for Portable Object (Template) files. |
| 1897 | | */ |
| 1898 | 35 | function _locale_export_wrap($str, $len) { |
| 1899 | 0 | $words = explode(' ', $str); |
| 1900 | 0 | $ret = array(); |
| 1901 | | |
| 1902 | 0 | $cur = ""; |
| 1903 | 0 | $nstr = 1; |
| 1904 | 0 | while (count($words)) { |
| 1905 | 0 | $word = array_shift($words); |
| 1906 | 0 | if ($nstr) { |
| 1907 | 0 | $cur = $word; |
| 1908 | 0 | $nstr = 0; |
| 1909 | 0 | } |
| 1910 | 0 | elseif (strlen("$cur $word") > $len) { |
| 1911 | 0 | $ret[] = $cur . " "; |
| 1912 | 0 | $cur = $word; |
| 1913 | 0 | } |
| 1914 | | else { |
| 1915 | 0 | $cur = "$cur $word"; |
| 1916 | | } |
| 1917 | 0 | } |
| 1918 | 0 | $ret[] = $cur; |
| 1919 | | |
| 1920 | 0 | return implode("\n", $ret); |
| 1921 | 0 | } |
| 1922 | | |
| 1923 | | /** |
| 1924 | | * Removes plural index information from a string |
| 1925 | | */ |
| 1926 | 35 | function _locale_export_remove_plural($entry) { |
| 1927 | 0 | return preg_replace('/(@count)\[[0-9]\]/', '\\1', $entry); |
| 1928 | 0 | } |
| 1929 | | /** |
| 1930 | | * @} End of "locale-api-export" |
| 1931 | | */ |
| 1932 | | |
| 1933 | | /** |
| 1934 | | * @defgroup locale-api-seek String search functions. |
| 1935 | | * @{ |
| 1936 | | */ |
| 1937 | | |
| 1938 | | /** |
| 1939 | | * Perform a string search and display results in a table |
| 1940 | | */ |
| 1941 | 35 | function _locale_translate_seek() { |
| 1942 | 8 | $output = ''; |
| 1943 | | |
| 1944 | | // We have at least one criterion to match |
| 1945 | 8 | if ($query = _locale_translate_seek_query()) { |
| 1946 | 3 | $join = "SELECT s.source, s.location, s.lid, s.textgroup,
t.translation, t.language FROM {locales_source} s LEFT JOIN
{locales_target} t ON s.lid = t.lid "; |
| 1947 | | |
| 1948 | 3 | $arguments = array(); |
| 1949 | 3 | $limit_language = FALSE; |
| 1950 | | // Compute LIKE section |
| 1951 | 3 | switch ($query['translation']) { |
| 1952 | 3 | case 'translated': |
| 1953 | 0 | $where = "WHERE (t.translation LIKE ?)"; |
| 1954 | 0 | $orderby = "ORDER BY t.translation"; |
| 1955 | 0 | $arguments[] = '%'. $query['string'] .'%'; |
| 1956 | 0 | break; |
| 1957 | 3 | case 'untranslated': |
| 1958 | 0 | $where = "WHERE (s.source LIKE ? AND t.translation IS NULL)"; |
| 1959 | 0 | $orderby = "ORDER BY s.source"; |
| 1960 | 0 | $arguments[] = '%'. $query['string'] .'%'; |
| 1961 | 0 | break; |
| 1962 | 3 | case 'all' : |
| 1963 | 3 | default: |
| 1964 | 3 | $where = "WHERE (s.source LIKE ? OR t.translation LIKE ?)"; |
| 1965 | 3 | $orderby = ''; |
| 1966 | 3 | $arguments[] = '%'. $query['string'] .'%'; |
| 1967 | 3 | $arguments[] = '%'. $query['string'] .'%'; |
| 1968 | 3 | break; |
| 1969 | 0 | } |
| 1970 | 3 | $grouplimit = ''; |
| 1971 | 3 | if (!empty($query['group']) && $query['group'] != 'all') { |
| 1972 | 0 | $grouplimit = " AND s.textgroup = ?"; |
| 1973 | 0 | $arguments[] = $query['group']; |
| 1974 | 0 | } |
| 1975 | | |
| 1976 | 3 | switch ($query['language']) { |
| 1977 | | // Force search in source strings |
| 1978 | 3 | case "en": |
| 1979 | 0 | $sql = $join . " WHERE s.source LIKE ? $grouplimit ORDER BY
s.source"; |
| 1980 | 0 | $arguments = array('%' . $query['string'] . '%'); // $where is not
used, discard its arguments |
| 1981 | 0 | if (!empty($grouplimit)) { |
| 1982 | 0 | $arguments[] = $query['group']; |
| 1983 | 0 | } |
| 1984 | 0 | break; |
| 1985 | | // Search in all languages |
| 1986 | 3 | case "all": |
| 1987 | 3 | $sql = "$join $where $grouplimit $orderby"; |
| 1988 | 3 | break; |
| 1989 | | // Some different language |
| 1990 | 0 | default: |
| 1991 | 0 | $sql = "$join AND t.language = ? $where $grouplimit $orderby"; |
| 1992 | 0 | array_unshift($arguments, $query['language']); |
| 1993 | | // Don't show translation flags for other languages, we can't see
them with this search. |
| 1994 | 0 | $limit_language = $query['language']; |
| 1995 | 0 | } |
| 1996 | | |
| 1997 | 3 | $result = pager_query($sql, 50, 0, NULL, $arguments); |
| 1998 | | |
| 1999 | 3 | $groups = module_invoke_all('locale', 'groups'); |
| 2000 | 3 | $header = array(t('Text group'), t('String'), ($limit_language) ?
t('Language') : t('Languages'), array('data' => t('Operations'), 'colspan'
=> '2')); |
| 2001 | 3 | $arr = array(); |
| 2002 | 3 | while ($locale = db_fetch_object($result)) { |
| 2003 | 2 | $arr[$locale->lid]['group'] = $groups[$locale->textgroup]; |
| 2004 | 2 | $arr[$locale->lid]['languages'][$locale->language] =
$locale->translation; |
| 2005 | 2 | $arr[$locale->lid]['location'] = $locale->location; |
| 2006 | 2 | $arr[$locale->lid]['source'] = $locale->source; |
| 2007 | 2 | } |
| 2008 | 3 | $rows = array(); |
| 2009 | 3 | foreach ($arr as $lid => $value) { |
| 2010 | 2 | $rows[] = array( |
| 2011 | 2 | $value['group'], |
| 2012 | 2 | array('data' => check_plain(truncate_utf8($value['source'], 150,
FALSE, TRUE)) . '<br /><small>' . $value['location'] . '</small>'), |
| 2013 | 2 | array('data' =>
_locale_translate_language_list($value['languages'], $limit_language),
'align' => 'center'), |
| 2014 | 2 | array('data' => l(t('edit'), "admin/build/translate/edit/$lid"),
'class' => 'nowrap'), |
| 2015 | 2 | array('data' => l(t('delete'),
"admin/build/translate/delete/$lid"), 'class' => 'nowrap'), |
| 2016 | | ); |
| 2017 | 2 | } |
| 2018 | | |
| 2019 | 3 | if (count($rows)) { |
| 2020 | 2 | $output .= theme('table', $header, $rows); |
| 2021 | 2 | if ($pager = theme('pager', NULL, 50)) { |
| 2022 | 0 | $output .= $pager; |
| 2023 | 0 | } |
| 2024 | 2 | } |
| 2025 | | else { |
| 2026 | 1 | $output .= t('No strings found for your search.'); |
| 2027 | | } |
| 2028 | 3 | } |
| 2029 | | |
| 2030 | 8 | return $output; |
| 2031 | 0 | } |
| 2032 | | |
| 2033 | | /** |
| 2034 | | * Build array out of search criteria specified in request variables |
| 2035 | | */ |
| 2036 | 35 | function _locale_translate_seek_query() { |
| 2037 | 8 | static $query = NULL; |
| 2038 | 8 | if (!isset($query)) { |
| 2039 | 8 | $query = array(); |
| 2040 | 8 | $fields = array('string', 'language', 'translation', 'group'); |
| 2041 | 8 | foreach ($fields as $field) { |
| 2042 | 8 | if (isset($_REQUEST[$field])) { |
| 2043 | 3 | $query[$field] = $_REQUEST[$field]; |
| 2044 | 3 | } |
| 2045 | 8 | } |
| 2046 | 8 | } |
| 2047 | 8 | return $query; |
| 2048 | 0 | } |
| 2049 | | |
| 2050 | | /** |
| 2051 | | * Force the JavaScript translation file(s) to be refreshed. |
| 2052 | | * |
| 2053 | | * This function sets a refresh flag for a specified language, or all |
| 2054 | | * languages except English, if none specified. JavaScript translation |
| 2055 | | * files are rebuilt (with locale_update_js_files()) the next time a |
| 2056 | | * request is served in that language. |
| 2057 | | * |
| 2058 | | * @param $langcode |
| 2059 | | * The language code for which the file needs to be refreshed. |
| 2060 | | * @return |
| 2061 | | * New content of the 'javascript_parsed' variable. |
| 2062 | | */ |
| 2063 | 35 | function _locale_invalidate_js($langcode = NULL) { |
| 2064 | 16 | $parsed = variable_get('javascript_parsed', array()); |
| 2065 | | |
| 2066 | 16 | if (empty($langcode)) { |
| 2067 | | // Invalidate all languages. |
| 2068 | 13 | $languages = language_list(); |
| 2069 | 13 | unset($languages['en']); |
| 2070 | 13 | foreach ($languages as $lcode => $data) { |
| 2071 | 7 | $parsed['refresh:' . $lcode] = 'waiting'; |
| 2072 | 7 | } |
| 2073 | 13 | } |
| 2074 | | else { |
| 2075 | | // Invalidate single language. |
| 2076 | 4 | $parsed['refresh:' . $langcode] = 'waiting'; |
| 2077 | | } |
| 2078 | | |
| 2079 | 16 | variable_set('javascript_parsed', $parsed); |
| 2080 | 16 | return $parsed; |
| 2081 | 0 | } |
| 2082 | | |
| 2083 | | /** |
| 2084 | | * (Re-)Creates the JavaScript translation file for a language. |
| 2085 | | * |
| 2086 | | * @param $language |
| 2087 | | * The language, the translation file should be (re)created for. |
| 2088 | | */ |
| 2089 | 35 | function _locale_rebuild_js($langcode = NULL) { |
| 2090 | 2 | if (!isset($langcode)) { |
| 2091 | 1 | global $language; |
| 2092 | 1 | } |
| 2093 | | else { |
| 2094 | | // Get information about the locale. |
| 2095 | 1 | $languages = language_list(); |
| 2096 | 1 | $language = $languages[$langcode]; |
| 2097 | | } |
| 2098 | | |
| 2099 | | // Construct the array for JavaScript translations. |
| 2100 | | // We sort on plural so that we have all plural forms before singular
forms. |
| 2101 | 2 | $result = db_query("SELECT s.lid, s.source, t.plid, t.plural,
t.translation |
| 2102 | | FROM {locales_source} s |
| 2103 | | LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language =
:language |
| 2104 | | WHERE s.location LIKE '%.js%' |
| 2105 | | AND s.textgroup = 'default' |
| 2106 | 2 | ORDER BY t.plural DESC", array(':language' => $language->language)); |
| 2107 | | |
| 2108 | 2 | $translations = $plurals = array(); |
| 2109 | 2 | while ($data = db_fetch_object($result)) { |
| 2110 | | // Only add this to the translations array when there is actually a
translation. |
| 2111 | 2 | if (!empty($data->translation)) { |
| 2112 | 0 | if ($data->plural) { |
| 2113 | | // When the translation is a plural form, first add it to another
array and |
| 2114 | | // wait for the singular (parent) translation. |
| 2115 | 0 | if (!isset($plurals[$data->plid])) { |
| 2116 | 0 | $plurals[$data->plid] = array($data->plural =>
$data->translation); |
| 2117 | 0 | } |
| 2118 | | else { |
| 2119 | 0 | $plurals[$data->plid] += array($data->plural =>
$data->translation); |
| 2120 | | } |
| 2121 | 0 | } |
| 2122 | 0 | elseif (isset($plurals[$data->lid])) { |
| 2123 | | // There are plural translations for this translation, so get them
from |
| 2124 | | // the plurals array and add them to the final translations array. |
| 2125 | 0 | $translations[$data->source] = array($data->plural =>
$data->translation) + $plurals[$data->lid]; |
| 2126 | 0 | unset($plurals[$data->lid]); |
| 2127 | 0 | } |
| 2128 | | else { |
| 2129 | | // There are no plural forms for this translation, so just add it
to |
| 2130 | | // the translations array. |
| 2131 | 0 | $translations[$data->source] = $data->translation; |
| 2132 | | } |
| 2133 | 0 | } |
| 2134 | 2 | } |
| 2135 | | |
| 2136 | | // Construct the JavaScript file, if there are translations. |
| 2137 | 2 | $data = $status = ''; |
| 2138 | 2 | if (!empty($translations)) { |
| 2139 | | |
| 2140 | 0 | $data = "Drupal.locale = { "; |
| 2141 | | |
| 2142 | 0 | if (!empty($language->formula)) { |
| 2143 | 0 | $data .= "'pluralFormula': function(\$n) { return
Number({$language->formula}); }, "; |
| 2144 | 0 | } |
| 2145 | | |
| 2146 | 0 | $data .= "'strings': " . drupal_to_js($translations) . " };"; |
| 2147 | 0 | $data_hash = md5($data); |
| 2148 | 0 | } |
| 2149 | | |
| 2150 | | // Construct the filepath where JS translation files are stored. |
| 2151 | | // There is (on purpose) no front end to edit that variable. |
| 2152 | 2 | $dir = file_create_path(variable_get('locale_js_directory',
'languages')); |
| 2153 | | |
| 2154 | | // Delete old file, if we have no translations anymore, or a different
file to be saved. |
| 2155 | 2 | if (!empty($language->javascript) && (!$data || $language->javascript !=
$data_hash)) { |
| 2156 | 0 | file_delete(file_create_path($dir . '/' . $language->language . '_' .
$language->javascript . '.js')); |
| 2157 | 0 | $language->javascript = ''; |
| 2158 | 0 | $status = 'deleted'; |
| 2159 | 0 | } |
| 2160 | | |
| 2161 | | // Only create a new file if the content has changed. |
| 2162 | 2 | if ($data && $language->javascript != $data_hash) { |
| 2163 | | // Ensure that the directory exists and is writable, if possible. |
| 2164 | 0 | file_check_directory($dir, TRUE); |
| 2165 | | |
| 2166 | | // Save the file. |
| 2167 | 0 | $dest = $dir . '/' . $language->language . '_' . $data_hash . '.js'; |
| 2168 | 0 | if (file_unmanaged_save_data($data, $dest)) { |
| 2169 | 0 | $language->javascript = $data_hash; |
| 2170 | 0 | $status = ($status == 'deleted') ? 'updated' : 'created'; |
| 2171 | 0 | } |
| 2172 | | else { |
| 2173 | 0 | $language->javascript = ''; |
| 2174 | 0 | $status = 'error'; |
| 2175 | | } |
| 2176 | 0 | } |
| 2177 | | |
| 2178 | | // Save the new JavaScript hash (or an empty value if the file |
| 2179 | | // just got deleted). Act only if some operation was executed. |
| 2180 | 2 | if ($status) { |
| 2181 | 0 | db_query("UPDATE {languages} SET javascript = '%s' WHERE language =
'%s'", $language->javascript, $language->language); |
| 2182 | | |
| 2183 | | // Update the default language variable if the default language has
been altered. |
| 2184 | | // This is necessary to keep the variable consistent with the database |
| 2185 | | // version of the language and to prevent checking against an outdated
hash. |
| 2186 | 0 | $default_langcode = language_default('language'); |
| 2187 | 0 | if ($default_langcode == $language->language) { |
| 2188 | 0 | $default = db_fetch_object(db_query("SELECT * FROM {languages} WHERE
language = '%s'", $default_langcode)); |
| 2189 | 0 | variable_set('language_default', $default); |
| 2190 | 0 | } |
| 2191 | 0 | } |
| 2192 | | |
| 2193 | | // Log the operation and return success flag. |
| 2194 | | switch ($status) { |
| 2195 | 2 | case 'updated': |
| 2196 | 0 | watchdog('locale', 'Updated JavaScript translation file for the
language %language.', array('%language' => t($language->name))); |
| 2197 | 0 | return TRUE; |
| 2198 | 2 | case 'created': |
| 2199 | 0 | watchdog('locale', 'Created JavaScript translation file for the
language %language.', array('%language' => t($language->name))); |
| 2200 | 0 | return TRUE; |
| 2201 | 2 | case 'deleted': |
| 2202 | 0 | watchdog('locale', 'Removed JavaScript translation file for the
language %language, because no translations currently exist for that
language.', array('%language' => t($language->name))); |
| 2203 | 0 | return TRUE; |
| 2204 | 2 | case 'error': |
| 2205 | 0 | watchdog('locale', 'An error occurred during creation of the
JavaScript translation file for the language %language.', array('%language'
=> t($language->name)), WATCHDOG_ERROR); |
| 2206 | 0 | return FALSE; |
| 2207 | 2 | default: |
| 2208 | | // No operation needed. |
| 2209 | 2 | return TRUE; |
| 2210 | 2 | } |
| 2211 | 0 | } |
| 2212 | | |
| 2213 | | /** |
| 2214 | | * List languages in search result table |
| 2215 | | */ |
| 2216 | 35 | function _locale_translate_language_list($translation, $limit_language) { |
| 2217 | | // Add CSS |
| 2218 | 2 | drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css',
array('preprocess' => FALSE)); |
| 2219 | | |
| 2220 | 2 | $languages = language_list(); |
| 2221 | 2 | unset($languages['en']); |
| 2222 | 2 | $output = ''; |
| 2223 | 2 | foreach ($languages as $langcode => $language) { |
| 2224 | 2 | if (!$limit_language || $limit_language == $langcode) { |
| 2225 | 2 | $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "<em
class=\"locale-untranslated\">$langcode</em> "; |
| 2226 | 2 | } |
| 2227 | 2 | } |
| 2228 | | |
| 2229 | 2 | return $output; |
| 2230 | 0 | } |
| 2231 | | /** |
| 2232 | | * @} End of "locale-api-seek" |
| 2233 | | */ |
| 2234 | | |
| 2235 | | /** |
| 2236 | | * @defgroup locale-api-predefined List of predefined languages |
| 2237 | | * @{ |
| 2238 | | */ |
| 2239 | | |
| 2240 | | /** |
| 2241 | | * Prepares the language code list for a select form item with only the
unsupported ones |
| 2242 | | */ |
| 2243 | 35 | function _locale_prepare_predefined_list() { |
| 2244 | 6 | $languages = language_list(); |
| 2245 | 6 | $predefined = _locale_get_predefined_list(); |
| 2246 | 6 | foreach ($predefined as $key => $value) { |
| 2247 | 6 | if (isset($languages[$key])) { |
| 2248 | 6 | unset($predefined[$key]); |
| 2249 | 6 | continue; |
| 2250 | 0 | } |
| 2251 | | // Include native name in output, if possible |
| 2252 | 6 | if (count($value) > 1) { |
| 2253 | 6 | $tname = t($value[0]); |
| 2254 | 6 | $predefined[$key] = ($tname == $value[1]) ? $tname : "$tname
($value[1])"; |
| 2255 | 6 | } |
| 2256 | | else { |
| 2257 | 6 | $predefined[$key] = t($value[0]); |
| 2258 | | } |
| 2259 | 6 | } |
| 2260 | 6 | asort($predefined); |
| 2261 | 6 | return $predefined; |
| 2262 | 0 | } |
| 2263 | | |
| 2264 | | /** |
| 2265 | | * Some of the common languages with their English and native names |
| 2266 | | * |
| 2267 | | * Based on ISO 639 and http://people.w3.org/rishida/names/languages.html |
| 2268 | | */ |
| 2269 | 35 | function _locale_get_predefined_list() { |
| 2270 | | return array( |
| 2271 | 6 | "aa" => array("Afar"), |
| 2272 | 6 | "ab" => array("Abkhazian", "аҧсуа бызшәа"), |
| 2273 | 6 | "ae" => array("Avestan"), |
| 2274 | 6 | "af" => array("Afrikaans"), |
| 2275 | 6 | "ak" => array("Akan"), |
| 2276 | 6 | "am" => array("Amharic", "አማርኛ"), |
| 2277 | 6 | "ar" => array("Arabic", /* Left-to-right marker "" */
"العربية", LANGUAGE_RTL), |
| 2278 | 6 | "as" => array("Assamese"), |
| 2279 | 6 | "av" => array("Avar"), |
| 2280 | 6 | "ay" => array("Aymara"), |
| 2281 | 6 | "az" => array("Azerbaijani", "azərbaycan"), |
| 2282 | 6 | "ba" => array("Bashkir"), |
| 2283 | 6 | "be" => array("Belarusian", "Беларуская"), |
| 2284 | 6 | "bg" => array("Bulgarian", "Български"), |
| 2285 | 6 | "bh" => array("Bihari"), |
| 2286 | 6 | "bi" => array("Bislama"), |
| 2287 | 6 | "bm" => array("Bambara", "Bamanankan"), |
| 2288 | 6 | "bn" => array("Bengali"), |
| 2289 | 6 | "bo" => array("Tibetan"), |
| 2290 | 6 | "br" => array("Breton"), |
| 2291 | 6 | "bs" => array("Bosnian", "Bosanski"), |
| 2292 | 6 | "ca" => array("Catalan", "Català"), |
| 2293 | 6 | "ce" => array("Chechen"), |
| 2294 | 6 | "ch" => array("Chamorro"), |
| 2295 | 6 | "co" => array("Corsican"), |
| 2296 | 6 | "cr" => array("Cree"), |
| 2297 | 6 | "cs" => array("Czech", "Čeština"), |
| 2298 | 6 | "cu" => array("Old Slavonic"), |
| 2299 | 6 | "cv" => array("Chuvash"), |
| 2300 | 6 | "cy" => array("Welsh", "Cymraeg"), |
| 2301 | 6 | "da" => array("Danish", "Dansk"), |
| 2302 | 6 | "de" => array("German", "Deutsch"), |
| 2303 | 6 | "dv" => array("Maldivian"), |
| 2304 | 6 | "dz" => array("Bhutani"), |
| 2305 | 6 | "ee" => array("Ewe", "Ɛʋɛ"), |
| 2306 | 6 | "el" => array("Greek", "Ελληνικά"), |
| 2307 | 6 | "en" => array("English"), |
| 2308 | 6 | "eo" => array("Esperanto"), |
| 2309 | 6 | "es" => array("Spanish", "Español"), |
| 2310 | 6 | "et" => array("Estonian", "Eesti"), |
| 2311 | 6 | "eu" => array("Basque", "Euskera"), |
| 2312 | 6 | "fa" => array("Persian", /* Left-to-right marker "" */ "فارسی",
LANGUAGE_RTL), |
| 2313 | 6 | "ff" => array("Fulah", "Fulfulde"), |
| 2314 | 6 | "fi" => array("Finnish", "Suomi"), |
| 2315 | 6 | "fj" => array("Fiji"), |
| 2316 | 6 | "fo" => array("Faeroese"), |
| 2317 | 6 | "fr" => array("French", "Français"), |
| 2318 | 6 | "fy" => array("Frisian", "Frysk"), |
| 2319 | 6 | "ga" => array("Irish", "Gaeilge"), |
| 2320 | 6 | "gd" => array("Scots Gaelic"), |
| 2321 | 6 | "gl" => array("Galician", "Galego"), |
| 2322 | 6 | "gn" => array("Guarani"), |
| 2323 | 6 | "gu" => array("Gujarati"), |
| 2324 | 6 | "gv" => array("Manx"), |
| 2325 | 6 | "ha" => array("Hausa"), |
| 2326 | 6 | "he" => array("Hebrew", /* Left-to-right marker "" */ "עברית",
LANGUAGE_RTL), |
| 2327 | 6 | "hi" => array("Hindi", "हिन्दी"), |
| 2328 | 6 | "ho" => array("Hiri Motu"), |
| 2329 | 6 | "hr" => array("Croatian", "Hrvatski"), |
| 2330 | 6 | "hu" => array("Hungarian", "Magyar"), |
| 2331 | 6 | "hy" => array("Armenian", "Հայերեն"), |
| 2332 | 6 | "hz" => array("Herero"), |
| 2333 | 6 | "ia" => array("Interlingua"), |
| 2334 | 6 | "id" => array("Indonesian", "Bahasa Indonesia"), |
| 2335 | 6 | "ie" => array("Interlingue"), |
| 2336 | 6 | "ig" => array("Igbo"), |
| 2337 | 6 | "ik" => array("Inupiak"), |
| 2338 | 6 | "is" => array("Icelandic", "Íslenska"), |
| 2339 | 6 | "it" => array("Italian", "Italiano"), |
| 2340 | 6 | "iu" => array("Inuktitut"), |
| 2341 | 6 | "ja" => array("Japanese", "日本語"), |
| 2342 | 6 | "jv" => array("Javanese"), |
| 2343 | 6 | "ka" => array("Georgian"), |
| 2344 | 6 | "kg" => array("Kongo"), |
| 2345 | 6 | "ki" => array("Kikuyu"), |
| 2346 | 6 | "kj" => array("Kwanyama"), |
| 2347 | 6 | "kk" => array("Kazakh", "Қазақ"), |
| 2348 | 6 | "kl" => array("Greenlandic"), |
| 2349 | 6 | "km" => array("Cambodian"), |
| 2350 | 6 | "kn" => array("Kannada", "ಕನ್ನಡ"), |
| 2351 | 6 | "ko" => array("Korean", "한국어"), |
| 2352 | 6 | "kr" => array("Kanuri"), |
| 2353 | 6 | "ks" => array("Kashmiri"), |
| 2354 | 6 | "ku" => array("Kurdish", "Kurdî"), |
| 2355 | 6 | "kv" => array("Komi"), |
| 2356 | 6 | "kw" => array("Cornish"), |
| 2357 | 6 | "ky" => array("Kirghiz", "Кыргыз"), |
| 2358 | 6 | "la" => array("Latin", "Latina"), |
| 2359 | 6 | "lb" => array("Luxembourgish"), |
| 2360 | 6 | "lg" => array("Luganda"), |
| 2361 | 6 | "ln" => array("Lingala"), |
| 2362 | 6 | "lo" => array("Laothian"), |
| 2363 | 6 | "lt" => array("Lithuanian", "Lietuvių"), |
| 2364 | 6 | "lv" => array("Latvian", "Latviešu"), |
| 2365 | 6 | "mg" => array("Malagasy"), |
| 2366 | 6 | "mh" => array("Marshallese"), |
| 2367 | 6 | "mi" => array("Maori"), |
| 2368 | 6 | "mk" => array("Macedonian", "Македонски"), |
| 2369 | 6 | "ml" => array("Malayalam", "മലയാളം"), |
| 2370 | 6 | "mn" => array("Mongolian"), |
| 2371 | 6 | "mo" => array("Moldavian"), |
| 2372 | 6 | "mr" => array("Marathi"), |
| 2373 | 6 | "ms" => array("Malay", "Bahasa Melayu"), |
| 2374 | 6 | "mt" => array("Maltese", "Malti"), |
| 2375 | 6 | "my" => array("Burmese"), |
| 2376 | 6 | "na" => array("Nauru"), |
| 2377 | 6 | "nd" => array("North Ndebele"), |
| 2378 | 6 | "ne" => array("Nepali"), |
| 2379 | 6 | "ng" => array("Ndonga"), |
| 2380 | 6 | "nl" => array("Dutch", "Nederlands"), |
| 2381 | 6 | "nb" => array("Norwegian Bokmål", "Bokmål"), |
| 2382 | 6 | "nn" => array("Norwegian Nynorsk", "Nynorsk"), |
| 2383 | 6 | "nr" => array("South Ndebele"), |
| 2384 | 6 | "nv" => array("Navajo"), |
| 2385 | 6 | "ny" => array("Chichewa"), |
| 2386 | 6 | "oc" => array("Occitan"), |
| 2387 | 6 | "om" => array("Oromo"), |
| 2388 | 6 | "or" => array("Oriya"), |
| 2389 | 6 | "os" => array("Ossetian"), |
| 2390 | 6 | "pa" => array("Punjabi"), |
| 2391 | 6 | "pi" => array("Pali"), |
| 2392 | 6 | "pl" => array("Polish", "Polski"), |
| 2393 | 6 | "ps" => array("Pashto", /* Left-to-right marker "" */ "پښتو",
LANGUAGE_RTL), |
| 2394 | 6 | "pt-pt" => array("Portuguese, Portugal", "Português"), |
| 2395 | 6 | "pt-br" => array("Portuguese, Brazil", "Português"), |
| 2396 | 6 | "qu" => array("Quechua"), |
| 2397 | 6 | "rm" => array("Rhaeto-Romance"), |
| 2398 | 6 | "rn" => array("Kirundi"), |
| 2399 | 6 | "ro" => array("Romanian", "Română"), |
| 2400 | 6 | "ru" => array("Russian", "Русский"), |
| 2401 | 6 | "rw" => array("Kinyarwanda"), |
| 2402 | 6 | "sa" => array("Sanskrit"), |
| 2403 | 6 | "sc" => array("Sardinian"), |
| 2404 | 6 | "sd" => array("Sindhi"), |
| 2405 | 6 | "se" => array("Northern Sami"), |
| 2406 | 6 | "sg" => array("Sango"), |
| 2407 | 6 | "sh" => array("Serbo-Croatian"), |
| 2408 | 6 | "si" => array("Singhalese"), |
| 2409 | 6 | "sk" => array("Slovak", "Slovenčina"), |
| 2410 | 6 | "sl" => array("Slovenian", "Slovenščina"), |
| 2411 | 6 | "sm" => array("Samoan"), |
| 2412 | 6 | "sn" => array("Shona"), |
| 2413 | 6 | "so" => array("Somali"), |
| 2414 | 6 | "sq" => array("Albanian", "Shqip"), |
| 2415 | 6 | "sr" => array("Serbian", "Српски"), |
| 2416 | 6 | "ss" => array("Siswati"), |
| 2417 | 6 | "st" => array("Sesotho"), |
| 2418 | 6 | "su" => array("Sudanese"), |
| 2419 | 6 | "sv" => array("Swedish", "Svenska"), |
| 2420 | 6 | "sw" => array("Swahili", "Kiswahili"), |
| 2421 | 6 | "ta" => array("Tamil", "தமிழ்"), |
| 2422 | 6 | "te" => array("Telugu", "తెలుగు"), |
| 2423 | 6 | "tg" => array("Tajik"), |
| 2424 | 6 | "th" => array("Thai", "ภาษาไทย"), |
| 2425 | 6 | "ti" => array("Tigrinya"), |
| 2426 | 6 | "tk" => array("Turkmen"), |
| 2427 | 6 | "tl" => array("Tagalog"), |
| 2428 | 6 | "tn" => array("Setswana"), |
| 2429 | 6 | "to" => array("Tonga"), |
| 2430 | 6 | "tr" => array("Turkish", "Türkçe"), |
| 2431 | 6 | "ts" => array("Tsonga"), |
| 2432 | 6 | "tt" => array("Tatar", "Tatarça"), |
| 2433 | 6 | "tw" => array("Twi"), |
| 2434 | 6 | "ty" => array("Tahitian"), |
| 2435 | 6 | "ug" => array("Uighur"), |
| 2436 | 6 | "uk" => array("Ukrainian", "Українська"), |
| 2437 | 6 | "ur" => array("Urdu", /* Left-to-right marker "" */ "اردو",
LANGUAGE_RTL), |
| 2438 | 6 | "uz" => array("Uzbek", "o'zbek"), |
| 2439 | 6 | "ve" => array("Venda"), |
| 2440 | 6 | "vi" => array("Vietnamese", "Tiếng Việt"), |
| 2441 | 6 | "wo" => array("Wolof"), |
| 2442 | 6 | "xh" => array("Xhosa", "isiXhosa"), |
| 2443 | 6 | "yi" => array("Yiddish"), |
| 2444 | 6 | "yo" => array("Yoruba", "Yorùbá"), |
| 2445 | 6 | "za" => array("Zhuang"), |
| 2446 | 6 | "zh-hans" => array("Chinese, Simplified", "简体中文"), |
| 2447 | 6 | "zh-hant" => array("Chinese, Traditional", "繁體中文"), |
| 2448 | 6 | "zu" => array("Zulu", "isiZulu"), |
| 2449 | 6 | ); |
| 2450 | 0 | } |
| 2451 | | /** |
| 2452 | | * @} End of "locale-api-languages-predefined" |
| 2453 | | */ |
| 2454 | | |
| 2455 | | /** |
| 2456 | | * @defgroup locale-autoimport Automatic interface translation import |
| 2457 | | * @{ |
| 2458 | | */ |
| 2459 | | |
| 2460 | | /** |
| 2461 | | * Prepare a batch to import translations for all enabled |
| 2462 | | * modules in a given language. |
| 2463 | | * |
| 2464 | | * @param $langcode |
| 2465 | | * Language code to import translations for. |
| 2466 | | * @param $finished |
| 2467 | | * Optional finished callback for the batch. |
| 2468 | | * @param $skip |
| 2469 | | * Array of component names to skip. Used in the installer for the |
| 2470 | | * second pass import, when most components are already imported. |
| 2471 | | * @return |
| 2472 | | * A batch structure or FALSE if no files found. |
| 2473 | | */ |
| 2474 | 35 | function locale_batch_by_language($langcode, $finished = NULL, $skip =
array()) { |
| 2475 | | // Collect all files to import for all enabled modules and themes. |
| 2476 | 3 | $files = array(); |
| 2477 | 3 | $components = array(); |
| 2478 | 3 | $query = "SELECT name, filename FROM {system} WHERE status = 1"; |
| 2479 | 3 | if (count($skip)) { |
| 2480 | 0 | $query .= " AND name NOT IN (" . db_placeholders($skip, 'varchar') .
")"; |
| 2481 | 0 | } |
| 2482 | 3 | $result = db_query($query, $skip); |
| 2483 | 3 | while ($component = db_fetch_object($result)) { |
| 2484 | | // Collect all files for all components, names as $langcode.po or |
| 2485 | | // with names ending with $langcode.po. This allows for filenames |
| 2486 | | // like node-module.de.po to let translators use small files and |
| 2487 | | // be able to import in smaller chunks. |
| 2488 | 3 | $files = array_merge($files,
file_scan_directory(dirname($component->filename) . '/translations',
'/(^|\.)' . $langcode . '\.po$/', array('.', '..', 'CVS'), 0, FALSE)); |
| 2489 | 3 | $components[] = $component->name; |
| 2490 | 3 | } |
| 2491 | | |
| 2492 | 3 | return _locale_batch_build($files, $finished, $components); |
| 2493 | 0 | } |
| 2494 | | |
| 2495 | | /** |
| 2496 | | * Prepare a batch to run when installing modules or enabling themes. |
| 2497 | | * This batch will import translations for the newly added components |
| 2498 | | * in all the languages already set up on the site. |
| 2499 | | * |
| 2500 | | * @param $components |
| 2501 | | * An array of component (theme and/or module) names to import |
| 2502 | | * translations for. |
| 2503 | | * @param $finished |
| 2504 | | * Optional finished callback for the batch. |
| 2505 | | */ |
| 2506 | 35 | function locale_batch_by_component($components, $finished =
'_locale_batch_system_finished') { |
| 2507 | 1 | $files = array(); |
| 2508 | 1 | $languages = language_list('enabled'); |
| 2509 | 1 | unset($languages[1]['en']); |
| 2510 | 1 | if (count($languages[1])) { |
| 2511 | 0 | $language_list = join('|', array_keys($languages[1])); |
| 2512 | | // Collect all files to import for all $components. |
| 2513 | 0 | $result = db_query("SELECT name, filename FROM {system} WHERE status =
1"); |
| 2514 | 0 | while ($component = db_fetch_object($result)) { |
| 2515 | 0 | if (in_array($component->name, $components)) { |
| 2516 | | // Collect all files for this component in all enabled languages,
named |
| 2517 | | // as $langcode.po or with names ending with $langcode.po. This
allows |
| 2518 | | // for filenames like node-module.de.po to let translators use
small |
| 2519 | | // files and be able to import in smaller chunks. |
| 2520 | 0 | $files = array_merge($files,
file_scan_directory(dirname($component->filename) . '/translations',
'/(^|\.)(' . $language_list . ')\.po$/', array('.', '..', 'CVS'), 0,
FALSE)); |
| 2521 | 0 | } |
| 2522 | 0 | } |
| 2523 | 0 | return _locale_batch_build($files, $finished); |
| 2524 | 0 | } |
| 2525 | 1 | return FALSE; |
| 2526 | 0 | } |
| 2527 | | |
| 2528 | | /** |
| 2529 | | * Build a locale batch from an array of files. |
| 2530 | | * |
| 2531 | | * @param $files |
| 2532 | | * Array of files to import |
| 2533 | | * @param $finished |
| 2534 | | * Optional finished callback for the batch. |
| 2535 | | * @param $components |
| 2536 | | * Optional list of component names the batch covers. Used in the
installer. |
| 2537 | | * @return |
| 2538 | | * A batch structure |
| 2539 | | */ |
| 2540 | 35 | function _locale_batch_build($files, $finished = NULL, $components =
array()) { |
| 2541 | 3 | $t = get_t(); |
| 2542 | 3 | if (count($files)) { |
| 2543 | 0 | $operations = array(); |
| 2544 | 0 | foreach ($files as $file) { |
| 2545 | | // We call _locale_batch_import for every batch operation. |
| 2546 | 0 | $operations[] = array('_locale_batch_import',
array($file->filename)); } |
| 2547 | | $batch = array( |
| 2548 | 0 | 'operations' => $operations, |
| 2549 | 0 | 'title' => $t('Importing interface translations'), |
| 2550 | 0 | 'init_message' => $t('Starting import'), |
| 2551 | 0 | 'error_message' => $t('Error importing interface translations'), |
| 2552 | 0 | 'file' => 'includes/locale.inc', |
| 2553 | | // This is not a batch API construct, but data passed along to the |
| 2554 | | // installer, so we know what did we import already. |
| 2555 | 0 | '#components' => $components, |
| 2556 | 0 | ); |
| 2557 | 0 | if (isset($finished)) { |
| 2558 | 0 | $batch['finished'] = $finished; |
| 2559 | 0 | } |
| 2560 | 0 | return $batch; |
| 2561 | 0 | } |
| 2562 | 3 | return FALSE; |
| 2563 | 0 | } |
| 2564 | | |
| 2565 | | /** |
| 2566 | | * Perform interface translation import as a batch step. |
| 2567 | | * |
| 2568 | | * @param $filepath |
| 2569 | | * Path to a file to import. |
| 2570 | | * @param $results |
| 2571 | | * Contains a list of files imported. |
| 2572 | | */ |
| 2573 | 35 | function _locale_batch_import($filepath, &$context) { |
| 2574 | | // The filename is either {langcode}.po or {prefix}.{langcode}.po, so |
| 2575 | | // we can extract the language code to use for the import from the end. |
| 2576 | 0 | if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) { |
| 2577 | 0 | $file = (object) array('filename' => basename($filepath), 'filepath' =>
$filepath); |
| 2578 | 0 | _locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP,
$langcode[2]); |
| 2579 | 0 | $context['results'][] = $filepath; |
| 2580 | 0 | } |
| 2581 | 0 | } |
| 2582 | | |
| 2583 | | /** |
| 2584 | | * Finished callback of system page locale import batch. |
| 2585 | | * Inform the user of translation files imported. |
| 2586 | | */ |
| 2587 | 35 | function _locale_batch_system_finished($success, $results) { |
| 2588 | 0 | if ($success) { |
| 2589 | 0 | drupal_set_message(format_plural(count($results), 'One translation file
imported for the newly installed modules.', '@count translation files
imported for the newly installed modules.')); |
| 2590 | 0 | } |
| 2591 | 0 | } |
| 2592 | | |
| 2593 | | /** |
| 2594 | | * Finished callback of language addition locale import batch. |
| 2595 | | * Inform the user of translation files imported. |
| 2596 | | */ |
| 2597 | 35 | function _locale_batch_language_finished($success, $results) { |
| 2598 | 0 | if ($success) { |
| 2599 | 0 | drupal_set_message(format_plural(count($results), 'One translation file
imported for the enabled modules.', '@count translation files imported for
the enabled modules.')); |
| 2600 | 0 | } |
| 2601 | 0 | } |
| 2602 | | |
| 2603 | | /** |
| 2604 | | * @} End of "locale-autoimport" |
| 2605 | | */ |
| 2606 | 35 | |