i18n_field.module

Tracking 7.x-1.x branch
  1. drupal
    1. 7 contributions/i18n/i18n_field/i18n_field.module

Internationalization (i18n) module - Field handling

For string keys we use:

  • field:[field_name]:[bundle]:property, when it is an instance property (linked to bundle)
  • field:[field_name]:#property..., when it is a field property (that may have multiple values)

Functions & methods

NameDescription
i18n_field_field_attach_formImplements hook_field_attach_form().
i18n_field_field_attach_view_alterImplements hook_field_attach_view_alter().
i18n_field_field_create_fieldImplements hook_field_create_field().
i18n_field_field_create_instanceImplements hook_field_create_instance().
i18n_field_field_delete_instanceImplements hook_field_delete_instance().
i18n_field_field_display_alterImplements hook_field_display_alter().
i18n_field_field_extra_fields_display_alter
i18n_field_field_formatter_infoImplements hook_field_formatter_info().
i18n_field_field_formatter_viewImplements hook_field_formatter_view().
i18n_field_field_info_alterImplements hook_field_info_alter().
i18n_field_field_storage_info_alter
i18n_field_field_update_fieldImplements hook_field_update_field().
i18n_field_field_update_instanceImplements hook_field_update_instance().
i18n_field_field_update_stringsUpdate field strings
i18n_field_field_widget_formField API callback to rewrite field element
i18n_field_field_widget_info_alterImplements hook_field_widget_info_alter().
i18n_field_field_widget_properties_alterImplements hook_field_widget_properties_alter().
i18n_field_hook_infoImplements hook_hook_info().
i18n_field_instance_update_stringsUpdate field instance strings
i18n_field_menuImplements hook_menu().
i18n_field_translate_allowed_valuesReturns the array of translated allowed values for a list field.
i18n_field_translate_defaultTranslate field default
i18n_field_translate_propertyTranslate field property
i18n_field_type_infoGet i18n information for fields

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Internationalization (i18n) module - Field handling
  5. *
  6. * For string keys we use:
  7. * - field:[field_name]:[bundle]:property, when it is an instance property (linked to bundle)
  8. * - field:[field_name]:#property..., when it is a field property (that may have multiple values)
  9. */
  10. /**
  11. * Implements hook_menu().
  12. */
  13. function i18n_field_menu() {
  14. $items = array();
  15. // Ensure the following is not executed until field_bundles is working and
  16. // tables are updated. Needed to avoid errors on initial installation.
  17. if (!module_exists('field_ui') || defined('MAINTENANCE_MODE')) {
  18. return $items;
  19. }
  20. // Create tabs for all possible bundles. From field_ui_menu().
  21. foreach (entity_get_info() as $entity_type => $entity_info) {
  22. if ($entity_info['fieldable']) {
  23. foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
  24. if (isset($bundle_info['admin'])) {
  25. // Extract path information from the bundle.
  26. $path = $bundle_info['admin']['path'];
  27. // Different bundles can appear on the same path (e.g. %node_type and
  28. // %comment_node_type). To allow field_ui_menu_load() to extract the
  29. // actual bundle object from the translated menu router path
  30. // arguments, we need to identify the argument position of the bundle
  31. // name string ('bundle argument') and pass that position to the menu
  32. // loader. The position needs to be casted into a string; otherwise it
  33. // would be replaced with the bundle name string.
  34. if (isset($bundle_info['admin']['bundle argument'])) {
  35. $bundle_arg = $bundle_info['admin']['bundle argument'];
  36. $bundle_pos = (string) $bundle_arg;
  37. }
  38. else {
  39. $bundle_arg = $bundle_name;
  40. $bundle_pos = '0';
  41. }
  42. // This is the position of the %field_ui_menu placeholder in the
  43. // items below.
  44. $field_position = count(explode('/', $path)) + 1;
  45. // Extract access information, providing defaults.
  46. $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
  47. $access += array(
  48. 'access callback' => 'user_access',
  49. 'access arguments' => array('administer site configuration'),
  50. );
  51. $items["$path/fields/%field_ui_menu/translate"] = array(
  52. 'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
  53. 'title' => 'Translate',
  54. 'page callback' => 'i18n_field_page_translate',
  55. 'page arguments' => array($field_position),
  56. 'file' => 'i18n_field.pages.inc',
  57. 'type' => MENU_LOCAL_TASK,
  58. ) + $access;
  59. $items["$path/fields/%field_ui_menu/translate/%i18n_language"] = array(
  60. 'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
  61. 'title' => 'Instance',
  62. 'page callback' => 'i18n_field_page_translate',
  63. 'page arguments' => array($field_position, $field_position + 2),
  64. 'file' => 'i18n_field.pages.inc',
  65. 'type' => MENU_CALLBACK,
  66. ) + $access;
  67. }
  68. }
  69. }
  70. }
  71. return $items;
  72. }
  73. /**
  74. * Implements hook_hook_info().
  75. */
  76. function i18n_field_hook_info() {
  77. $hooks['i18n_field_info'] = array(
  78. 'group' => 'i18n',
  79. );
  80. return $hooks;
  81. }
  82. /**
  83. * Implements hook_field_attach_form().
  84. *
  85. * After the form fields are built. Translate title and description for fields with multiple values.
  86. */
  87. function i18n_field_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  88. // Determine the list of instances to iterate on.
  89. list(, , $bundle) = entity_extract_ids($entity_type, $entity);
  90. $instances = field_info_instances($entity_type, $bundle);
  91. foreach ($instances as $field_name => $instance) {
  92. if (isset($form[$field_name])) {
  93. $langcode = $form[$field_name]['#language'];
  94. $field = &$form[$field_name];
  95. // Note: cardinality for unlimited fields is -1
  96. if (isset($field[$langcode]['#cardinality']) && $field[$langcode]['#cardinality'] != 1) {
  97. $translated = i18n_string_object_translate('field_instance', $instance);
  98. if (!empty($field[$langcode]['#title'])) {
  99. $field[$langcode]['#title'] = $translated['label'];
  100. }
  101. if (!empty($field[$langcode]['#description'])) {
  102. $field[$langcode]['#description'] = $translated['description'];
  103. }
  104. }
  105. }
  106. }
  107. }
  108. /**
  109. * Implements hook_field_info_alter().
  110. *
  111. * Cached, invoked only after field info is rebuilt
  112. */
  113. function i18n_field_field_info_alter(&$info) {
  114. }
  115. /**
  116. * Implements hook_field_formatter_info().
  117. */
  118. function i18n_field_field_formatter_info() {
  119. $types = array();
  120. foreach (i18n_field_type_info() as $type => $info) {
  121. if (!empty($info['translate_options'])) {
  122. $types[] = $type;
  123. }
  124. }
  125. return array(
  126. 'i18n_list_default' => array(
  127. 'label' => t('Default translated'),
  128. 'field types' => $types,
  129. ),
  130. );
  131. }
  132. /**
  133. * Implements hook_field_formatter_view().
  134. */
  135. function i18n_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  136. $element = array();
  137. switch ($display['type']) {
  138. case 'i18n_list_default':
  139. if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) {
  140. $allowed_values = $translate($field);
  141. }
  142. else {
  143. // Defaults to list_default behavior
  144. $allowed_values = list_allowed_values($field);
  145. }
  146. foreach ($items as $delta => $item) {
  147. if (isset($allowed_values[$item['value']])) {
  148. $output = field_filter_xss($allowed_values[$item['value']]);
  149. }
  150. else {
  151. // If no match was found in allowed values, fall back to the key.
  152. $output = field_filter_xss($item['value']);
  153. }
  154. $element[$delta] = array('#markup' => $output);
  155. }
  156. break;
  157. }
  158. return $element;
  159. }
  160. /**
  161. * Implements hook_field_widget_info_alter().
  162. *
  163. * Cached, invoked only after widget info is rebuilt
  164. */
  165. function i18n_field_field_widget_info_alter(&$info) {
  166. }
  167. /**
  168. * Implements hook_field_widget_properties_alter().
  169. *
  170. * This is called for the entity edit form and for the fields edit form
  171. */
  172. function i18n_field_field_widget_properties_alter(&$widget, $context) {
  173. // Skip the node type edit fields by checking for existing entity
  174. if (!empty($context['entity']) && !empty($widget['module']) && function_exists($function = $widget['module'] . '_field_widget_form')) {
  175. $widget['module'] = 'i18n_field';
  176. $widget['i18n_field_callbacks'][] = $function;
  177. }
  178. }
  179. function i18n_field_field_extra_fields_display_alter(&$displays, $context) {
  180. }
  181. /**
  182. * Implements hook_field_display_alter().
  183. *
  184. * Called only when refreshing cache
  185. */
  186. function i18n_field_field_display_alter(&$display, $context) {
  187. }
  188. function i18n_field_field_storage_info_alter(&$info) {
  189. }
  190. /**
  191. * Field API callback to rewrite field element
  192. *
  193. * Translate:
  194. * - Title (label)
  195. * - Description (help)
  196. * - Default value
  197. * - List options
  198. * @see field_default_form()
  199. */
  200. function i18n_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  201. global $language;
  202. // The field language may affect some variables (default) but not others (description will be in current page language)
  203. $i18n_langcode = empty($element['#language']) || $element['#language'] == LANGUAGE_NONE ? $language->language : $element['#language'];
  204. // Translate instance to current page language and set to form_state
  205. // so it will be used for validation messages later.
  206. $instance_current = i18n_string_object_translate('field_instance', $instance);
  207. if (isset($form_state['field'][$instance['field_name']][$langcode]['instance'])) {
  208. $form_state['field'][$instance['field_name']][$langcode]['instance'] = $instance_current;
  209. }
  210. // Now replace some form elements one at a time.
  211. $delta = $element['#delta'];
  212. // Translate field title if set
  213. if (!empty($instance_current['label']) && !empty($element['#title'])) {
  214. $element['#title'] = $instance_current['label'];
  215. }
  216. // Translate field description if set
  217. if (!empty($instance_current['description']) && !empty($element['#description'])) {
  218. $element['#description'] = $instance_current['description'];
  219. }
  220. // Translate default value if exists and the current value is the default
  221. if (($translate = i18n_field_type_info($field['type'], 'translate_default')) && !empty($instance['default_value'][$delta]['value']) && !empty($items[$delta]['value']) && $instance['default_value'][$delta]['value'] === $items[$delta]['value']) {
  222. $items[$delta]['value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
  223. }
  224. // Redirect through original module_field_widget_form()
  225. if (!empty($instance['widget']['i18n_field_callbacks'])) {
  226. foreach ($instance['widget']['i18n_field_callbacks'] as $function) {
  227. $element = $function($form, $form_state, $field, $instance_current, $langcode, $items, $delta, $element);
  228. }
  229. }
  230. // Translate list options
  231. if (($translate = i18n_field_type_info($field['type'], 'translate_options')) && !empty($field['settings']['allowed_values'])) {
  232. $element['#options'] = $translate($field, $i18n_langcode);
  233. if ($element['#properties']['empty_option']) {
  234. $label = theme('options_none', array('instance' => $instance, 'option' => $element['#properties']['empty_option']));
  235. $element['#options'] = array('_none' => $label) + $element['#options'];
  236. }
  237. if ($field['type'] == 'list_boolean' && !empty($element['#on_value'])) {
  238. $on_value = $element['#on_value'];
  239. $element['#title'] = $element['#options'][$on_value];
  240. if ($instance['widget']['settings']['display_label']) {
  241. $element['#title'] = $instance_current['label'];
  242. }
  243. }
  244. }
  245. return $element;
  246. }
  247. /**
  248. * Implements hook_field_attach_view_alter().
  249. */
  250. function i18n_field_field_attach_view_alter(&$output, $context) {
  251. foreach (element_children($output) as $field_name) {
  252. $element = &$output[$field_name];
  253. if (!empty($element['#entity_type']) && !empty($element['#field_name']) && !empty($element['#bundle'])) {
  254. $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
  255. // Translate field title if set
  256. if (!empty($instance['label'])) {
  257. $element['#title'] = i18n_field_translate_property($instance, 'label');
  258. }
  259. // Translate field description if set
  260. if (!empty($instance['description'])) {
  261. $element['#description'] = i18n_field_translate_property($instance, 'description');
  262. }
  263. }
  264. }
  265. }
  266. /**
  267. * Implements hook_field_create_field().
  268. */
  269. function i18n_field_field_create_field($field) {
  270. i18n_field_field_update_strings($field);
  271. }
  272. /**
  273. * Implements hook_field_create_instance().
  274. */
  275. function i18n_field_field_create_instance($instance) {
  276. i18n_field_instance_update_strings($instance);
  277. }
  278. /**
  279. * Implements hook_field_delete_instance().
  280. */
  281. function i18n_field_field_delete_instance($instance) {
  282. i18n_string_object_remove('field_instance', $instance);
  283. }
  284. /**
  285. * Implements hook_field_update_instance().
  286. */
  287. function i18n_field_field_update_instance($instance, $prior_instance) {
  288. i18n_field_instance_update_strings($instance);
  289. }
  290. /**
  291. * Implements hook_field_update_field().
  292. */
  293. function i18n_field_field_update_field($field) {
  294. i18n_field_field_update_strings($field);
  295. }
  296. /**
  297. * Update field strings
  298. */
  299. function i18n_field_field_update_strings($field) {
  300. i18n_string_object_update('field', $field);
  301. }
  302. /**
  303. * Update field instance strings
  304. */
  305. function i18n_field_instance_update_strings($instance) {
  306. i18n_string_object_update('field_instance', $instance);
  307. }
  308. /**
  309. * Returns the array of translated allowed values for a list field.
  310. *
  311. * The strings are not safe for output. Keys and values of the array should be
  312. * sanitized through field_filter_xss() before being displayed.
  313. *
  314. * @param $field
  315. * The field definition.
  316. *
  317. * @return
  318. * The array of allowed values. Keys of the array are the raw stored values
  319. * (number or text), values of the array are the display labels.
  320. */
  321. function i18n_field_translate_allowed_values($field, $langcode = NULL) {
  322. if (!empty($field['settings']['allowed_values'])) {
  323. return i18n_string_translate(array('field', $field['field_name'], '#allowed_values'), $field['settings']['allowed_values'], array('langcode' => $langcode));
  324. }
  325. else {
  326. return array();
  327. }
  328. }
  329. /**
  330. * Translate field default
  331. */
  332. function i18n_field_translate_default($instance, $value, $langcode = NULL) {
  333. return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode));
  334. }
  335. /**
  336. * Translate field property
  337. */
  338. function i18n_field_translate_property($instance, $property, $langcode = NULL) {
  339. // For performance reasons, we translate the whole instance once, which is cached.
  340. $instance = i18n_string_object_translate('field_instance', $instance, array('langcode' => $langcode));
  341. return $instance[$property];
  342. }
  343. /**
  344. * Get i18n information for fields
  345. */
  346. function i18n_field_type_info($type = NULL, $property = NULL) {
  347. $info = &drupal_static(__FUNCTION__);
  348. if (!isset($info)) {
  349. $info = module_invoke_all('i18n_field_info');
  350. drupal_alter('i18n_field_info', $info);
  351. }
  352. if ($property) {
  353. return isset($info[$type]) && isset($info[$type][$property]) ? $info[$type][$property] : NULL;
  354. }
  355. elseif ($type) {
  356. return isset($info[$type]) ? $info[$type] : array();
  357. }
  358. else {
  359. return $info;
  360. }
  361. }