locale.install 13.3 KB
Newer Older
1 2
<?php

3 4 5 6 7
/**
 * @file
 * Install, update and uninstall functions for the locale module.
 */

8 9 10 11 12 13 14 15 16 17 18 19
/**
 * Implements hook_install().
 *
 * Enable URL language negotiation by default in order to have a basic working
 * system on multilingual sites without needing any preliminary configuration.
 */
function locale_install() {
  require_once DRUPAL_ROOT . '/core/includes/language.inc';

  // We cannot rely on language negotiation hooks here, because locale module is
  // not enabled yet. Therefore language_negotiation_set() cannot be used.
  $info = locale_language_negotiation_info();
20 21
  $method = $info[LANGUAGE_NEGOTIATION_URL];
  $method_fields = array('callbacks', 'file', 'cache');
22 23 24
  $negotiation = array();

  // Store only the needed data.
25 26 27
  foreach ($method_fields as $field) {
    if (isset($method[$field])) {
      $negotiation[LANGUAGE_NEGOTIATION_URL][$field] = $method[$field];
28 29 30 31
    }
  }

  // Enable URL language detection for each (core) configurable language type.
32
  foreach (language_types_get_configurable() as $type) {
33 34 35 36
    variable_set("language_negotiation_$type", $negotiation);
  }
}

37
/**
38 39 40 41 42
 * Fill in the path prefixes and domains when enabled.
 *
 * Language module might change the list of languages, so we need to sync our
 * configuration for domains and paths with the current language list. This
 * should run every time the module is enabled.
43
 */
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
function locale_enable() {
  require_once DRUPAL_ROOT . '/core/includes/locale.inc';

  $languages = language_list();
  $prefixes_old = locale_language_negotiation_url_prefixes();
  $domains_old = locale_language_negotiation_url_domains();

  $prefixes = array();
  $domains = array();
  foreach ($languages as $langcode => $language) {
    // Keep the old prefix or fill in based on whether the language is default.
    $prefixes[$langcode] = empty($prefixes_old[$langcode]) ? (empty($language->default) ? $langcode : '') : $prefixes_old[$langcode];
    // Keep the old domain or fill in empty value.
    $domains[$langcode] = empty($domains_old[$langcode]) ? '' : $domains_old[$langcode];
  }

  locale_language_negotiation_url_prefixes_save($prefixes);
  locale_language_negotiation_url_domains_save($domains);
62
}
63 64

/**
65
 * Implements hook_uninstall().
66 67
 */
function locale_uninstall() {
68
  // Delete all JavaScript translation files.
69
  $locale_js_directory = 'public://' . variable_get('locale_js_directory', 'languages');
70 71

  if (is_dir($locale_js_directory)) {
72 73 74 75
    $locale_javascripts = variable_get('locale_translation_javascript', array());
    foreach ($locale_javascripts as $langcode => $file_suffix) {
      if (!empty($file_suffix)) {
        file_unmanaged_delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js');
76 77 78 79
      }
    }
    // Delete the JavaScript translations directory if empty.
    if (!file_scan_directory($locale_js_directory, '/.*/')) {
80
      drupal_rmdir($locale_js_directory);
81
    }
82
  }
83

84
  // Clear variables.
85 86
  variable_del('language_types');
  variable_del('locale_language_negotiation_url_part');
87 88
  variable_del('locale_language_negotiation_url_prefixes');
  variable_del('locale_language_negotiation_url_domains');
89
  variable_del('locale_language_negotiation_session_param');
90 91 92 93
  variable_del('language_content_type_default');
  variable_del('language_content_type_negotiation');
  variable_del('locale_cache_strings');
  variable_del('locale_js_directory');
94
  variable_del('javascript_parsed');
95
  variable_del('locale_field_language_fallback');
96
  variable_del('locale_cache_length');
97 98
  variable_del('locale_translation_plurals');
  variable_del('locale_translation_javascript');
99

100
  foreach (language_types_get_all() as $type) {
101
    variable_del("language_negotiation_$type");
102
    variable_del("locale_language_negotiation_methods_weight_$type");
103
  }
104

105 106 107 108 109
  // Remove all node type language variables. Node module might have been
  // enabled, but may be disabled, so use a wildcard delete.
  db_delete('variable')
    ->condition('name', db_like('language_content_type_') . '%', 'LIKE')
    ->execute();
110
}
111 112

/**
113
 * Implements hook_schema().
114 115 116
 */
function locale_schema() {
  $schema['locales_source'] = array(
117
    'description' => 'List of English source strings.',
118
    'fields' => array(
119 120 121
      'lid' => array(
        'type' => 'serial',
        'not null' => TRUE,
122
        'description' => 'Unique identifier of this string.',
123 124
      ),
      'location' => array(
125 126 127
        'type' => 'text',
        'not null' => FALSE,
        'size' => 'big',
128
        'description' => 'Drupal path in case of online discovered translations or file path in case of imported strings.',
129 130 131
      ),
      'source' => array(
        'type' => 'text',
132
        'mysql_type' => 'blob',
133
        'not null' => TRUE,
134
        'description' => 'The original string in English.',
135
      ),
136 137 138 139 140 141 142
      'context' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The context this string applies to.',
      ),
143 144 145 146 147
      'version' => array(
        'type' => 'varchar',
        'length' => 20,
        'not null' => TRUE,
        'default' => 'none',
148
        'description' => 'Version of Drupal, where the string was last used (for locales optimization).',
149
      ),
150 151
    ),
    'primary key' => array('lid'),
152
    'indexes' => array(
153
      'source_context' => array(array('source', 30), 'context'),
154
    ),
155 156 157
  );

  $schema['locales_target'] = array(
158
    'description' => 'Stores translated versions of strings.',
159
    'fields' => array(
160 161 162 163
      'lid' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
164
        'description' => 'Source string ID. References {locales_source}.lid.',
165 166 167
      ),
      'translation' => array(
        'type' => 'text',
168
        'mysql_type' => 'blob',
169
        'not null' => TRUE,
170
        'description' => 'Translation string value in this language.',
171 172 173 174 175 176
      ),
      'language' => array(
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
177
        'description' => 'Language code. References {language}.langcode.',
178 179 180
      ),
      'plid' => array(
        'type' => 'int',
181
        'not null' => TRUE, // This should be NULL for no referenced string, not zero.
182
        'default' => 0,
183
        'description' => 'Parent lid (lid of the previous string in the plural chain) in case of plural strings. References {locales_source}.lid.',
184 185 186 187 188
      ),
      'plural' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
189
        'description' => 'Plural index number in case of plural strings.',
190
      ),
191
    ),
192
    'primary key' => array('language', 'lid', 'plural'),
193
    'foreign keys' => array(
194 195 196 197
      'locales_source' => array(
        'table' => 'locales_source',
        'columns' => array('lid' => 'lid'),
      ),
198
    ),
199
    'indexes' => array(
200 201 202
      'lid'      => array('lid'),
      'plid'     => array('plid'),
      'plural'   => array('plural'),
203 204 205 206 207
    ),
  );

  return $schema;
}
208 209 210 211 212 213

/**
 * @addtogroup updates-7.x-to-8.x
 * @{
 */

214 215 216 217 218 219 220
/**
 * Drop textgroup support.
 *
 * Update assumes i18n migrated this data before the update happened. Core
 * never used textgroups for anything, so it is not our job to find place
 * for the data elsewhere.
 */
221
function locale_update_8000() {
222 223 224 225 226 227 228 229 230 231 232 233
  $subquery = db_select('locales_source', 'ls')
    ->fields('ls', array('lid'))
    ->condition('ls.textgroup', 'default', '<>');
  db_delete('locales_target')
    ->condition('lid', $subquery, 'IN')
    ->execute();
  db_delete('locales_source')
    ->condition('textgroup', 'default', '<>')
    ->execute();
  db_drop_field('locales_source', 'textgroup');
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/**
 * Language type 'language' renamed to 'language_interface'.
 */
function locale_update_8001() {
  // Only change language_types if we had this setting saved. Keep order
  // of types because that is significant for value dependency.
  $types = variable_get('language_types', NULL);
  if (!empty($types) && isset($types['language'])) {
    $new_types = array();
    foreach ($types as $key => $type) {
      $new_types[$key == 'language' ? 'language_interface' : $key] = $type;
    }
    variable_set('language_types', $new_types);
  }

  // Rename language_negotiation_language setting if exists.
  $setting = variable_get('language_negotiation_language', NULL);
  if ($setting !== NULL) {
    variable_set('language_negotiation_language_interface', $setting);
    variable_del('language_negotiation_language');
  }

  // Rename locale_language_providers_weight_language setting if exists.
  $weight = variable_get('locale_language_providers_weight_language', NULL);
  if ($weight !== NULL) {
    variable_set('locale_language_providers_weight_language_interface', $weight);
    variable_del('locale_language_providers_weight_language');
  }

  // Update block data in all core block related tables. Contributed modules
  // storing data for blocks will need to update for themselves.
  $block_tables = array('block', 'block_node_type', 'block_role');
  foreach ($block_tables as $table) {
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
    // Perform the update only if the language switcher block data is available.
    $block_data = db_query_range('SELECT 1 FROM {' . $table . '} WHERE delta = :delta AND module = :module', 0, 1, array(':delta' => 'language', ':module' => 'locale'))
      ->fetchField();
    if ($block_data) {
      // If block information is rebuilt before performing the update, we might
      // already have data for the new delta. In this case we need to remove it
      // to avoid integrity constraint violation errors.
      db_delete($table)
        ->condition('delta', 'language_interface')
        ->condition('module', 'locale')
        ->execute();
      db_update($table)
        ->fields(array(
          'delta' => 'language_interface',
        ))
        ->condition('delta', 'language')
        ->condition('module', 'locale')
        ->execute();
    }
286 287 288
  }
}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
/**
 * Removes duplicates in {locales_source}.
 *
 * Aggressively removes duplicates that has not already been removed by
 * locale_update_7004() in Drupal 7.x.
 */
function locale_update_8002() {
  // Look up all duplicates.
  $results = db_query("SELECT source, context FROM {locales_source} GROUP BY source, context HAVING COUNT(*) > 1");

  // For each set of duplicates, select one row that should survive, preferably
  // one that has been translated, and delete the rest.
  foreach ($results as $row) {
    $lid = db_query("SELECT s.lid FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid WHERE s.source = :source AND s.context = :context ORDER BY translation IS NULL", array(
      ':source' => $row->source,
      ':context' => $row->context,
      ))->fetchField();
    db_delete('locales_source')
      ->condition('source', $row->source)
      ->condition('context', $row->context)
      ->condition('lid', $lid, '<>')
      ->execute();
  }

  // Finally remove any rows from {locales_target} that refer to non-existing
  // lids.
  $subquery = db_select('locales_source', 't')->fields('t', array('lid'));
  db_delete('locales_target')->condition('lid', $subquery, 'NOT IN')->execute();
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
/**
 * Converts language domains to new format.
 */
function locale_update_8003() {
  $message = '';
  $domains = variable_get('locale_language_negotiation_url_domains', array());
  // $used_domains keeps track of the domain names in use.
  $used_domains = array();
  foreach ($domains as $langcode => $domain) {
    // Domain names can not contain protocol and/or ports.
    if (!empty($domain)) {
      $host = 'http://' . str_replace(array('http://', 'https://'), '', $domain);
      if (parse_url($host, PHP_URL_HOST) != $domain) {
        $domains[$langcode] = parse_url($host, PHP_URL_HOST);
      }
      if (array_key_exists($domain, $used_domains)) {
        if (empty($message)) {
          $message = 'Some languages are using the same domain name, you should change these domain names at ' . l('URL language detection configuration', 'admin/config/regional/language/configure/url' . '.');
        }
      }
      else {
        $used_domains[$domain] = $domain;
      }
    }
  }
  variable_set('locale_language_negotiation_url_domains', $domains);

  if (!empty($message)) {
    return $message;
  }
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
/**
 * Rename language providers to language negotiation methods.
 */
function locale_update_8004() {
  $types = variable_get('language_types', NULL);
  if (!empty($types)) {
    foreach ($types as $type => $configurable) {
      // Rename the negotiation and language switch callback keys.
      $negotiation = variable_get('language_negotiation_' . $type, NULL);
      if (!empty($negotiation)) {
        foreach ($negotiation as $method_id => &$method) {
          $method['callbacks']['negotiation'] = $method['callbacks']['language'];
          unset($method['callbacks']['language']);
          if (isset($method['callbacks']['switcher'])) {
            $method['callbacks']['language_switch'] = $method['callbacks']['switcher'];
            unset($method['callbacks']['switcher']);
          }
        }
        variable_set('language_negotiation_' . $type, $negotiation);
      }

      // Rename the language negotiation methods weight variable.
      $weight = variable_get('locale_language_providers_weight_' . $type , NULL);
      if ($weight !== NULL) {
        variable_set('locale_language_negotiation_methods_weight_' . $type , $weight);
        variable_del('locale_language_providers_weight_' . $type);
      }
    }
  }
}

382 383 384 385
/**
 * @} End of "addtogroup updates-7.x-to-8.x"
 * The next series of updates should start at 9000.
 */