i18n_block.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. /**
  3. * @file
  4. * Internationalization (i18n) submodule: Multilingual meta-blocks
  5. *
  6. * @author Jose A. Reyero, 2005
  7. *
  8. * @ TODO Add strings on block update.
  9. */
  10. /**
  11. * Implements hook_menu().
  12. *
  13. * Add translate tab to blocks.
  14. */
  15. function i18n_block_menu() {
  16. $items['admin/structure/block/manage/%/%/translate'] = array(
  17. 'title' => 'Translate',
  18. 'access callback' => 'i18n_block_translate_tab_access',
  19. 'access arguments' => array(4, 5),
  20. 'page callback' => 'i18n_block_translate_tab_page',
  21. 'page arguments' => array(4, 5),
  22. 'type' => MENU_LOCAL_TASK,
  23. 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
  24. 'weight' => 10,
  25. );
  26. $items['admin/structure/block/manage/%/%/translate/%i18n_language'] = array(
  27. 'title' => 'Translate',
  28. 'access callback' => 'i18n_block_translate_tab_access',
  29. 'access arguments' => array(4, 5),
  30. 'page callback' => 'i18n_block_translate_tab_page',
  31. 'page arguments' => array(4, 5, 7),
  32. 'type' => MENU_CALLBACK,
  33. 'weight' => 10,
  34. );
  35. return $items;
  36. }
  37. /**
  38. * Implements hook_permission().
  39. */
  40. function i18n_block_permission() {
  41. return array(
  42. 'translate blocks' => array(
  43. 'title' => t('Translate Blocks'),
  44. ),
  45. );
  46. }
  47. /**
  48. * Implement hook_menu_alter().
  49. *
  50. * Reorganize block tabs so that they make sense.
  51. */
  52. function i18n_block_menu_alter(&$items) {
  53. // Give the configure tab a short name and make it display.
  54. $items['admin/structure/block/manage/%/%/configure']['weight'] = -100;
  55. $items['admin/structure/block/manage/%/%/configure']['title'] = 'Configure';
  56. $items['admin/structure/block/manage/%/%/configure']['context'] = MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE;
  57. // Hide the delete tab. Not sure why this was even set a local task then
  58. // set to not show in any context...
  59. $items['admin/structure/block/manage/%/%/delete']['type'] = MENU_CALLBACK;
  60. }
  61. /**
  62. * Menu access callback function.
  63. *
  64. * Only let blocks translated which are configured to be translatable.
  65. */
  66. function i18n_block_translate_tab_access($module, $delta) {
  67. $block = block_load($module, $delta);
  68. return (user_access('translate interface') || user_access('translate blocks')) && $block && isset($block->i18n_mode) && ($block->i18n_mode == I18N_MODE_LOCALIZE);
  69. }
  70. /**
  71. * Build a translation page for the given block.
  72. */
  73. function i18n_block_translate_tab_page($module, $delta, $language = NULL) {
  74. $block = block_load($module, $delta);
  75. return i18n_string_object_translate_page('block', $block, $language);
  76. }
  77. /**
  78. * Implements hook_block_list_alter().
  79. *
  80. * Translate localizable blocks.
  81. *
  82. * To be run after all block visibility modules have run, just translate the blocks to be displayed
  83. */
  84. function i18n_block_block_list_alter(&$blocks) {
  85. global $theme_key, $language;
  86. // Build an array of node types for each block.
  87. $block_languages = array();
  88. $result = db_query('SELECT module, delta, language FROM {i18n_block_language}');
  89. foreach ($result as $record) {
  90. $block_languages[$record->module][$record->delta][$record->language] = TRUE;
  91. }
  92. foreach ($blocks as $key => $block) {
  93. if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
  94. // This block was added by a contrib module, leave it in the list.
  95. continue;
  96. }
  97. if (isset($block_languages[$block->module][$block->delta]) && !isset($block_languages[$block->module][$block->delta][$language->language])) {
  98. // Block not visible for this language
  99. unset($blocks[$key]);
  100. }
  101. }
  102. }
  103. /**
  104. * Implements hook_block_view().
  105. */
  106. function i18n_block_block_view_alter(&$data, $block) {
  107. if (!empty($block->i18n_mode)) {
  108. if (!empty($block->title) && $block->title != '<none>') {
  109. // Unfiltered, as $block->subject will be created later from the title.
  110. $data['title'] = i18n_string(array('blocks', $block->module, $block->delta, 'title'), $block->title, array('sanitize' => FALSE));
  111. }
  112. if ($block->module == 'block' && isset($data['content'])) {
  113. $data['content'] = i18n_string(array('blocks', $block->module, $block->delta, 'body'), $data['content']);
  114. }
  115. }
  116. }
  117. /**
  118. * Implements hook_context_block_info_alter().
  119. */
  120. function i18n_block_context_block_info_alter(&$block_info) {
  121. $theme_key = variable_get('theme_default', 'garland');
  122. $result = db_select('block')
  123. ->fields('block', array('module', 'delta', 'i18n_mode'))
  124. ->condition('theme', $theme_key)
  125. ->execute();
  126. foreach ($result as $row) {
  127. if (isset($block_info["{$row->module}-{$row->delta}"])) {
  128. $block_info["{$row->module}-{$row->delta}"]->i18n_mode = $row->i18n_mode;
  129. }
  130. }
  131. }
  132. /**
  133. * Implements hook_help().
  134. */
  135. function i18n_block_help($path, $arg) {
  136. switch ($path) {
  137. case 'admin/help#i18n_block':
  138. $output = '<p>' . t('This module provides support for multilingual blocks.') . '</p>';
  139. $output .= '<p>' . t('You can set up a language for a block or define it as translatable:') . '</p>';
  140. $output .= '<ul>';
  141. $output .= '<li>' . t('Blocks with a language will be displayed only in pages with that language.') . '</li>';
  142. $output .= '<li>' . t('Translatable blocks can be translated using the localization interface.') . '</li>';
  143. $output .= '</ul>';
  144. $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
  145. return $output;
  146. case 'admin/config/regional/i18n':
  147. $output = '<p>'. t('To set up multilingual options for blocks go to the <a href="@configure_blocks">Blocks administration page</a>.', array('@configure_blocks' => url('admin/structure/block'))) .'</p>';
  148. return $output;
  149. }
  150. }
  151. /**
  152. * Remove strings for deleted custom blocks.
  153. */
  154. function i18n_block_block_delete_submit(&$form, $form_state) {
  155. $delta = $form_state['values']['delta'];
  156. // Delete stored strings for the title and content fields.
  157. i18n_string_remove("blocks:block:$delta:title");
  158. i18n_string_remove("blocks:block:$delta:body");
  159. }
  160. /**
  161. * Implements block hook_form_FORM_ID_alter().
  162. *
  163. * Remove block title for multilingual blocks.
  164. */
  165. function i18n_block_form_block_add_block_form_alter(&$form, &$form_state, $form_id) {
  166. //i18n_block_alter_forms($form, $form_state, $form_id);
  167. i18n_block_form_block_admin_configure_alter($form, $form_state, $form_id);
  168. }
  169. /**
  170. * Implements block hook_form_FORM_ID_alter().
  171. *
  172. * Remove block title for multilingual blocks.
  173. */
  174. function i18n_block_form_block_admin_configure_alter(&$form, &$form_state, $form_id) {
  175. $form['i18n_block']['languages'] = array(
  176. '#type' => 'fieldset',
  177. '#title' => t('Languages'),
  178. '#collapsible' => TRUE,
  179. '#collapsed' => TRUE,
  180. '#group' => 'visibility',
  181. '#weight' => 5,
  182. '#attached' => array(
  183. 'js' => array(drupal_get_path('module', 'i18n_block') . '/i18n_block.js'),
  184. ),
  185. );
  186. // Add translatable option, just title for module blocks, title and content
  187. // for custom blocks.
  188. $description = '';
  189. $module = $form['module']['#value'];
  190. $delta = $form['delta']['#value'];
  191. // User created menus are exposed by the menu module, others by system.module.
  192. if ($module == 'menu' || ($module == 'system' && !in_array($delta, array('mail', 'help', 'powered-by')))) {
  193. $description = t('To translate the block content itself, <a href="@menu_translate_url">translate the menu</a> that is being shown.', array('@menu_translate_url' => url('admin/structure/menu/manage/' . $form['delta']['#value'])));
  194. }
  195. elseif ($module == 'views' && module_exists('i18nviews')) {
  196. $name = substr($delta, 0, strpos($delta, '-'));
  197. $description = t('To translate the block content itself, <a href="@views_translate_url">translate the view</a> that is being shown.', array('@views_translate_url' => url('admin/structure/views/view/' . $name . '/translate')));
  198. }
  199. elseif ($module != 'block') {
  200. $description = t('This block has generated content, only the title can be translated here.');
  201. }
  202. $block = block_load($form['module']['#value'], $form['delta']['#value']);
  203. $form['i18n_block']['languages']['i18n_mode'] = array(
  204. '#type' => 'checkbox',
  205. '#title' => t('Make this block translatable'),
  206. '#default_value' => isset($block->i18n_mode) ? $block->i18n_mode : I18N_MODE_NONE,
  207. '#description' => $description,
  208. );
  209. // Add option to select which language pages to show on.
  210. $default_options = db_query("SELECT language FROM {i18n_block_language} WHERE module = :module AND delta = :delta", array(
  211. ':module' => $form['module']['#value'],
  212. ':delta' => $form['delta']['#value'],
  213. ))->fetchCol();
  214. $form['i18n_block']['languages']['languages'] = array(
  215. '#type' => 'checkboxes',
  216. '#title' => t('Show this block for these languages'),
  217. '#default_value' => $default_options,
  218. '#options' => i18n_language_list(),
  219. '#description' => t('If no language is selected, block will show regardless of language.'),
  220. );
  221. if (user_access('translate interface') || user_access('translate blocks')) {
  222. $form['actions']['translate'] = array(
  223. '#type' => 'submit',
  224. '#name' => 'save_translate',
  225. '#value' => t('Save and translate'),
  226. '#states' => array(
  227. 'visible' => array(
  228. // The value must be a string so that the javascript comparison works.
  229. ":input[name=i18n_mode]" => array('checked' => TRUE),
  230. ),
  231. ),
  232. );
  233. }
  234. $form['#submit'][] = 'i18n_block_form_block_admin_configure_submit';
  235. }
  236. /**
  237. * Form submit handler for block configuration form.
  238. *
  239. * @see i18n_block_form_block_admin_configure_alter()
  240. */
  241. function i18n_block_form_block_admin_configure_submit(&$form, &$form_state) {
  242. $module = $form_state['values']['module'];
  243. $delta = $form_state['values']['delta'];
  244. // Update block languages
  245. db_delete('i18n_block_language')
  246. ->condition('module', $module)
  247. ->condition('delta', $delta)
  248. ->execute();
  249. $query = db_insert('i18n_block_language')->fields(array('language', 'module', 'delta'));
  250. foreach (array_filter($form_state['values']['languages']) as $language) {
  251. $query->values(array(
  252. 'language' => $language,
  253. 'module' => $module,
  254. 'delta' => $delta,
  255. ));
  256. }
  257. $query->execute();
  258. // Update block translation options and strings
  259. if (isset($form_state['values']['i18n_mode'])) {
  260. db_update('block')
  261. ->fields(array('i18n_mode' => $form_state['values']['i18n_mode']))
  262. ->condition('module', $module)
  263. ->condition('delta', $delta)
  264. ->execute();
  265. i18n_block_update_strings($form_state['values'], $form_state['values']['i18n_mode']);
  266. // If the save and translate button was clicked, redirect to the translate
  267. // tab instead of the block overview.
  268. if ($form_state['triggering_element']['#name'] == 'save_translate') {
  269. $form_state['redirect'] = 'admin/structure/block/manage/' . $module . '/' . $delta . '/translate';
  270. }
  271. }
  272. }
  273. /**
  274. * Update block strings
  275. */
  276. function i18n_block_update_strings($block, $i18n_mode = TRUE) {
  277. $title = $i18n_mode && $block['title'] !== '<none>' ? $block['title'] : '';
  278. i18n_string_update(array('blocks', $block['module'], $block['delta'], 'title'), $title);
  279. if (isset($block['body'])) {
  280. if ($i18n_mode) {
  281. i18n_string_update(array('blocks', $block['module'], $block['delta'], 'body'), $block['body']['value'], array('format' => $block['body']['format']));
  282. }
  283. else {
  284. i18n_string_remove(array('blocks', $block['module'], $block['delta'], 'body'));
  285. }
  286. }
  287. }
  288. /**
  289. * Implements hook_form_FORMID_alter().
  290. *
  291. * Adds node specific submit handler to delete custom block form.
  292. *
  293. * @see block_custom_block_delete()
  294. */
  295. function i18n_block_form_block_custom_block_delete_alter(&$form, &$form_state) {
  296. $form['#submit'][] = 'i18n_block_form_block_custom_block_delete_submit';
  297. }
  298. /**
  299. * Form submit handler for custom block delete form.
  300. *
  301. * @see node_form_block_custom_block_delete_alter()
  302. */
  303. function i18n_block_form_block_custom_block_delete_submit($form, &$form_state) {
  304. db_delete('i18n_block_language')
  305. ->condition('module', 'block')
  306. ->condition('delta', $form_state['values']['bid'])
  307. ->execute();
  308. // Remove related strings
  309. module_invoke('i18n_strings', 'remove',
  310. array('blocks', 'block', $form_state['values']['bid']),
  311. array('title', 'body')
  312. );
  313. }
  314. /**
  315. * Translate block.
  316. *
  317. * @param $block
  318. * Core block object
  319. */
  320. function i18n_block_translate_block($block) {
  321. if (!empty($block->content) && $localizable) {
  322. $block->content = i18n_string_text("blocks:$block->module:$block->delta:body", $block->content);
  323. }
  324. // If it has a custom title, localize it
  325. if (!empty($block->title) && $block->title != '<none>') {
  326. // Check plain here to allow module generated titles to keep any markup.
  327. $block->subject = i18n_string_plain("blocks:$block->module:$block->delta:title", $block->subject);
  328. }
  329. return $block;
  330. }