locale.install 14 KB

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