1<?php 2 3/** 4 * @file 5 * Install, update and uninstall functions for the locale module. 6 */ 7 8/** 9 * Implements hook_install(). 10 */ 11function locale_install() { 12 // locales_source.source and locales_target.target are not used as binary 13 // fields; non-MySQL database servers need to ensure the field type is text 14 // and that LIKE produces a case-sensitive comparison. 15 16 db_insert('languages') 17 ->fields(array( 18 'language' => 'en', 19 'name' => 'English', 20 'native' => 'English', 21 'direction' => 0, 22 'enabled' => 1, 23 'weight' => 0, 24 'javascript' => '', 25 )) 26 ->execute(); 27} 28 29/** 30 * @addtogroup updates-6.x-to-7.x 31 * @{ 32 */ 33 34/** 35 * Add context field index and allow longer location. 36 */ 37function locale_update_7000() { 38 db_drop_index('locales_source', 'source'); 39 db_add_index('locales_source', 'source_context', array(array('source', 30), 'context')); 40 41 // Also drop the 'textgroup_location' index added by the i18nstrings module 42 // of the i18n project, which prevents the below schema update from running. 43 if (db_index_exists('locales_source', 'textgroup_location')) { 44 db_drop_index('locales_source', 'textgroup_location'); 45 } 46 47 db_change_field('locales_source', 'location', 'location', array( 48 'type' => 'text', 49 'not null' => FALSE, 50 'size' => 'big', 51 'description' => 'Drupal path in case of online discovered translations or file path in case of imported strings.', 52 )); 53} 54 55/** 56 * Upgrade language negotiation settings. 57 */ 58function locale_update_7001() { 59 require_once DRUPAL_ROOT . '/includes/language.inc'; 60 require_once DRUPAL_ROOT . '/includes/locale.inc'; 61 require_once DRUPAL_ROOT . '/modules/locale/locale.module'; 62 63 switch (variable_get('language_negotiation', 0)) { 64 // LANGUAGE_NEGOTIATION_NONE. 65 case 0: 66 $negotiation = array(); 67 break; 68 69 // LANGUAGE_NEGOTIATION_PATH_DEFAULT. 70 case 1: 71 $negotiation = array(LOCALE_LANGUAGE_NEGOTIATION_URL); 72 // In Drupal 6 path prefixes are shown for the default language only when 73 // language negotiation is set to LANGUAGE_NEGOTIATION_PATH, while in 74 // Drupal 7 path prefixes are always shown if not empty. Hence we need to 75 // ensure that the default language has an empty prefix to avoid breaking 76 // the site URLs with a prefix that previously was missing. 77 $default = language_default(); 78 $default->prefix = ''; 79 variable_set('language_default', $default); 80 db_update('languages') 81 ->fields(array('prefix' => $default->prefix)) 82 ->condition('language', $default->language) 83 ->execute(); 84 break; 85 86 // LANGUAGE_NEGOTIATION_PATH. 87 case 2: 88 $negotiation = array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_USER, LOCALE_LANGUAGE_NEGOTIATION_BROWSER); 89 break; 90 91 // LANGUAGE_NEGOTIATION_DOMAIN. 92 case 3: 93 variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN); 94 $negotiation = array(LOCALE_LANGUAGE_NEGOTIATION_URL); 95 break; 96 } 97 98 // Save the new language negotiation options. 99 language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array_flip($negotiation)); 100 language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE => 0)); 101 language_negotiation_set(LANGUAGE_TYPE_URL, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0)); 102 103 // Save admininstration UI settings. 104 $type = LANGUAGE_TYPE_INTERFACE; 105 $provider_weights = array_flip(array_keys(locale_language_negotiation_info())); 106 variable_set("locale_language_providers_weight_$type", $provider_weights); 107 108 // Unset the old language negotiation system variable. 109 variable_del('language_negotiation'); 110 111 return array(); 112} 113 114/** 115 * Updates URL language negotiation by adding the URL fallback detection method. 116 */ 117function locale_update_7002() { 118 // language.inc may not have been included during bootstrap if there is not 119 // more than one language currently enabled. 120 require_once DRUPAL_ROOT . '/includes/language.inc'; 121 $language_types_info = language_types_info(); 122 $info = $language_types_info[LANGUAGE_TYPE_URL]; 123 if (isset($info['fixed'])) { 124 language_negotiation_set(LANGUAGE_TYPE_URL, array_flip($info['fixed'])); 125 } 126} 127 128/** 129 * @} End of "addtogroup updates-6.x-to-7.x". 130 */ 131 132/** 133 * @addtogroup updates-7.x-extra 134 * @{ 135 */ 136 137/** 138 * Update "language_count" variable. 139 */ 140function locale_update_7003() { 141 $languages = language_list('enabled'); 142 variable_set('language_count', count($languages[1])); 143} 144 145/** 146 * Remove duplicates in {locales_source}. 147 */ 148function locale_update_7004() { 149 // Look up all duplicates. For each set of duplicates, we select the row 150 // with the lowest lid as the "master" that will be preserved. 151 $result_source = db_query("SELECT MIN(lid) AS lid, source, context FROM {locales_source} WHERE textgroup = 'default' GROUP BY source, context HAVING COUNT(*) > 1"); 152 153 $conflict = FALSE; 154 foreach ($result_source as $source) { 155 // Find all rows in {locales_target} that are translations of the same 156 // string (incl. context). 157 $result_target = db_query("SELECT t.lid, t.language, t.plural, t.translation FROM {locales_source} s JOIN {locales_target} t ON s.lid = t.lid WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default' ORDER BY lid", array( 158 ':source' => $source->source, 159 ':context' => $source->context, 160 )); 161 162 $translations = array(); 163 $keep_lids = array($source->lid); 164 foreach ($result_target as $target) { 165 if (!isset($translations[$target->language])) { 166 $translations[$target->language] = $target->translation; 167 if ($target->lid != $source->lid) { 168 // Move translation to the master lid. 169 db_query('UPDATE {locales_target} SET lid = :new_lid WHERE lid = :old_lid', array( 170 ':new_lid' => $source->lid, 171 ':old_lid' => $target->lid)); 172 } 173 } 174 elseif ($translations[$target->language] == $target->translation) { 175 // Delete duplicate translation. 176 db_query('DELETE FROM {locales_target} WHERE lid = :lid AND language = :language', array( 177 ':lid' => $target->lid, 178 ':language' => $target->language)); 179 } 180 else { 181 // The same string is translated into several different strings in one 182 // language. We do not know which is the preferred, so we keep them all. 183 $keep_lids[] = $target->lid; 184 $conflict = TRUE; 185 } 186 } 187 188 // Delete rows in {locales_source} that are no longer referenced from 189 // {locales_target}. 190 db_delete('locales_source') 191 ->condition('source', $source->source) 192 ->condition('context', $source->context) 193 ->condition('textgroup', 'default') 194 ->condition('lid', $keep_lids, 'NOT IN') 195 ->execute(); 196 } 197 198 if ($conflict) { 199 $url = 'http://drupal.org/node/746240'; 200 drupal_set_message('Your {locales_source} table contains duplicates that could not be removed automatically. See <a href="' . $url .'" target="_blank">' . $url . '</a> for more information.', 'warning'); 201 } 202} 203 204/** 205 * Increase {locales_languages}.formula column's length. 206 */ 207function locale_update_7005() { 208 db_change_field('languages', 'formula', 'formula', array( 209 'type' => 'varchar', 210 'length' => 255, 211 'not null' => TRUE, 212 'default' => '', 213 'description' => 'Plural formula in PHP code to evaluate to get plural indexes.', 214 )); 215} 216 217/** 218 * @} End of "addtogroup updates-7.x-extra". 219 */ 220 221/** 222 * Implements hook_uninstall(). 223 */ 224function locale_uninstall() { 225 // Delete all JavaScript translation files. 226 $locale_js_directory = 'public://' . variable_get('locale_js_directory', 'languages'); 227 228 if (is_dir($locale_js_directory)) { 229 $files = db_query('SELECT language, javascript FROM {languages}'); 230 foreach ($files as $file) { 231 if (!empty($file->javascript)) { 232 file_unmanaged_delete($locale_js_directory . '/' . $file->language . '_' . $file->javascript . '.js'); 233 } 234 } 235 // Delete the JavaScript translations directory if empty. 236 if (!file_scan_directory($locale_js_directory, '/.*/')) { 237 drupal_rmdir($locale_js_directory); 238 } 239 } 240 241 // Clear variables. 242 variable_del('language_default'); 243 variable_del('language_count'); 244 variable_del('language_types'); 245 variable_del('locale_language_negotiation_url_part'); 246 variable_del('locale_language_negotiation_session_param'); 247 variable_del('language_content_type_default'); 248 variable_del('language_content_type_negotiation'); 249 variable_del('locale_cache_strings'); 250 variable_del('locale_js_directory'); 251 variable_del('javascript_parsed'); 252 variable_del('locale_field_language_fallback'); 253 variable_del('locale_cache_length'); 254 255 foreach (language_types() as $type) { 256 variable_del("language_negotiation_$type"); 257 variable_del("locale_language_providers_weight_$type"); 258 } 259 260 foreach (node_type_get_types() as $type => $content_type) { 261 $setting = variable_del("language_content_type_$type"); 262 } 263 264 // Switch back to English: with a $language->language value different from 'en' 265 // successive calls of t() might result in calling locale(), which in turn might 266 // try to query the unexisting {locales_source} and {locales_target} tables. 267 drupal_language_initialize(); 268 269} 270 271/** 272 * Implements hook_schema(). 273 */ 274function locale_schema() { 275 $schema['languages'] = array( 276 'description' => 'List of all available languages in the system.', 277 'fields' => array( 278 'language' => array( 279 'type' => 'varchar', 280 'length' => 12, 281 'not null' => TRUE, 282 'default' => '', 283 'description' => "Language code, e.g. 'de' or 'en-US'.", 284 ), 285 'name' => array( 286 'type' => 'varchar', 287 'length' => 64, 288 'not null' => TRUE, 289 'default' => '', 290 'description' => 'Language name in English.', 291 ), 292 'native' => array( 293 'type' => 'varchar', 294 'length' => 64, 295 'not null' => TRUE, 296 'default' => '', 297 'description' => 'Native language name.', 298 ), 299 'direction' => array( 300 'type' => 'int', 301 'not null' => TRUE, 302 'default' => 0, 303 'description' => 'Direction of language (Left-to-Right = 0, Right-to-Left = 1).', 304 ), 305 'enabled' => array( 306 'type' => 'int', 307 'not null' => TRUE, 308 'default' => 0, 309 'description' => 'Enabled flag (1 = Enabled, 0 = Disabled).', 310 ), 311 'plurals' => array( 312 'type' => 'int', 313 'not null' => TRUE, 314 'default' => 0, 315 'description' => 'Number of plural indexes in this language.', 316 ), 317 'formula' => array( 318 'type' => 'varchar', 319 'length' => 255, 320 'not null' => TRUE, 321 'default' => '', 322 'description' => 'Plural formula in PHP code to evaluate to get plural indexes.', 323 ), 324 'domain' => array( 325 'type' => 'varchar', 326 'length' => 128, 327 'not null' => TRUE, 328 'default' => '', 329 'description' => 'Domain to use for this language.', 330 ), 331 'prefix' => array( 332 'type' => 'varchar', 333 'length' => 128, 334 'not null' => TRUE, 335 'default' => '', 336 'description' => 'Path prefix to use for this language.', 337 ), 338 'weight' => array( 339 'type' => 'int', 340 'not null' => TRUE, 341 'default' => 0, 342 'description' => 'Weight, used in lists of languages.', 343 ), 344 'javascript' => array( 345 'type' => 'varchar', 346 'length' => 64, 347 'not null' => TRUE, 348 'default' => '', 349 'description' => 'Location of JavaScript translation file.', 350 ), 351 ), 352 'primary key' => array('language'), 353 'indexes' => array( 354 'list' => array('weight', 'name'), 355 ), 356 ); 357 358 $schema['locales_source'] = array( 359 'description' => 'List of English source strings.', 360 'fields' => array( 361 'lid' => array( 362 'type' => 'serial', 363 'not null' => TRUE, 364 'description' => 'Unique identifier of this string.', 365 ), 366 'location' => array( 367 'type' => 'text', 368 'not null' => FALSE, 369 'size' => 'big', 370 'description' => 'Drupal path in case of online discovered translations or file path in case of imported strings.', 371 ), 372 'textgroup' => array( 373 'type' => 'varchar', 374 'length' => 255, 375 'not null' => TRUE, 376 'default' => 'default', 377 'description' => 'A module defined group of translations, see hook_locale().', 378 ), 379 'source' => array( 380 'type' => 'text', 381 'mysql_type' => 'blob', 382 'not null' => TRUE, 383 'description' => 'The original string in English.', 384 ), 385 'context' => array( 386 'type' => 'varchar', 387 'length' => 255, 388 'not null' => TRUE, 389 'default' => '', 390 'description' => 'The context this string applies to.', 391 ), 392 'version' => array( 393 'type' => 'varchar', 394 'length' => 20, 395 'not null' => TRUE, 396 'default' => 'none', 397 'description' => 'Version of Drupal, where the string was last used (for locales optimization).', 398 ), 399 ), 400 'primary key' => array('lid'), 401 'indexes' => array( 402 'source_context' => array(array('source', 30), 'context'), 403 ), 404 ); 405 406 $schema['locales_target'] = array( 407 'description' => 'Stores translated versions of strings.', 408 'fields' => array( 409 'lid' => array( 410 'type' => 'int', 411 'not null' => TRUE, 412 'default' => 0, 413 'description' => 'Source string ID. References {locales_source}.lid.', 414 ), 415 'translation' => array( 416 'type' => 'text', 417 'mysql_type' => 'blob', 418 'not null' => TRUE, 419 'description' => 'Translation string value in this language.', 420 ), 421 'language' => array( 422 'type' => 'varchar', 423 'length' => 12, 424 'not null' => TRUE, 425 'default' => '', 426 'description' => 'Language code. References {languages}.language.', 427 ), 428 'plid' => array( 429 'type' => 'int', 430 'not null' => TRUE, // This should be NULL for no referenced string, not zero. 431 'default' => 0, 432 'description' => 'Parent lid (lid of the previous string in the plural chain) in case of plural strings. References {locales_source}.lid.', 433 ), 434 'plural' => array( 435 'type' => 'int', 436 'not null' => TRUE, 437 'default' => 0, 438 'description' => 'Plural index number in case of plural strings.', 439 ), 440 ), 441 'primary key' => array('language', 'lid', 'plural'), 442 'foreign keys' => array( 443 'locales_source' => array( 444 'table' => 'locales_source', 445 'columns' => array('lid' => 'lid'), 446 ), 447 ), 448 'indexes' => array( 449 'lid' => array('lid'), 450 'plid' => array('plid'), 451 'plural' => array('plural'), 452 ), 453 ); 454 455 return $schema; 456} 457 458