entity.i18n.inc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. /**
  3. * @file
  4. * Internationalization (i18n) integration.
  5. */
  6. /**
  7. * Gets the i18n controller for a given entity type.
  8. *
  9. * @return EntityDefaultI18nStringController|array|false
  10. * If a type is given, the controller for the given entity type. Else an array
  11. * of all enabled controllers keyed by entity type is returned.
  12. */
  13. function entity_i18n_controller($type = NULL) {
  14. $static = &drupal_static(__FUNCTION__);
  15. if (!isset($type)) {
  16. // Invoke the function for each type to ensure we have fully populated the
  17. // static variable.
  18. foreach (entity_get_info() as $entity_type => $info) {
  19. entity_i18n_controller($entity_type);
  20. }
  21. return array_filter($static);
  22. }
  23. if (!isset($static[$type])) {
  24. $info = entity_get_info($type);
  25. // Do not activate it by default. Modules have to explicitly enable it by
  26. // specifying EntityDefaultI18nStringController or their customization.
  27. $class = isset($info['i18n controller class']) ? $info['i18n controller class'] : FALSE;
  28. $static[$type] = $class ? new $class($type, $info) : FALSE;
  29. }
  30. return $static[$type];
  31. }
  32. /**
  33. * Implements hook_i18n_string_info().
  34. */
  35. function entity_i18n_string_info() {
  36. $groups = array();
  37. foreach (entity_i18n_controller() as $entity_type => $controller) {
  38. $groups += $controller->hook_string_info();
  39. }
  40. return $groups;
  41. }
  42. /**
  43. * Implements hook_i18n_object_info().
  44. */
  45. function entity_i18n_object_info() {
  46. $info = array();
  47. foreach (entity_i18n_controller() as $entity_type => $controller) {
  48. $info += $controller->hook_object_info();
  49. }
  50. return $info;
  51. }
  52. /**
  53. * Implements hook_i18n_string_objects().
  54. */
  55. function entity_i18n_string_objects($type) {
  56. if ($controller = entity_i18n_controller($type)) {
  57. return $controller->hook_string_objects();
  58. }
  59. }
  60. /**
  61. * Default controller handling i18n integration.
  62. *
  63. * Implements i18n string translation for all non-field properties marked as
  64. * 'translatable' and having the flag 'i18n string' set. This translation
  65. * approach fits in particular for translating configuration, i.e. exportable
  66. * entities.
  67. *
  68. * Requirements for the default controller:
  69. * - The entity type providing module must be specified using the 'module' key
  70. * in hook_entity_info().
  71. * - An 'entity class' derived from the provided class 'Entity' must be used.
  72. * - Properties must be declared as 'translatable' and the 'i18n string' flag
  73. * must be set to TRUE using hook_entity_property_info().
  74. * - i18n must be notified about changes manually by calling
  75. * i18n_string_object_update(), i18n_string_object_remove() and
  76. * i18n_string_update_context(). Ideally, this is done in a small integration
  77. * module depending on the entity API and i18n_string. Look at the provided
  78. * testing module "entity_test_i18n" for an example.
  79. * - If the entity API admin UI is used, the "translate" tab will be
  80. * automatically enabled and linked from the UI.
  81. * - There are helpers for getting translated values which work regardless
  82. * whether the i18n_string module is enabled, i.e. entity_i18n_string()
  83. * and Entity::getTranslation().
  84. *
  85. * Current limitations:
  86. * - Translatable property values cannot be updated via the metadata wrapper,
  87. * however reading works fine. See Entity::getTranslation().
  88. */
  89. class EntityDefaultI18nStringController {
  90. protected $entityType, $entityInfo;
  91. /**
  92. * The i18n textgroup we are using.
  93. */
  94. protected $textgroup;
  95. public function __construct($type) {
  96. $this->entityType = $type;
  97. $this->entityInfo = entity_get_info($type);
  98. // By default we go with the module name as textgroup.
  99. $this->textgroup = $this->entityInfo['module'];
  100. }
  101. /**
  102. * Implements hook_i18n_string_info() via entity_i18n_string_info().
  103. */
  104. public function hook_string_info() {
  105. $list = system_list('module_enabled');
  106. $info = $list[$this->textgroup]->info;
  107. $groups[$this->textgroup] = array(
  108. 'title' => $info['name'],
  109. 'description' => !empty($info['description']) ? $info['description'] : NULL,
  110. 'format' => FALSE,
  111. 'list' => TRUE,
  112. );
  113. return $groups;
  114. }
  115. /**
  116. * Implements hook_i18n_object_info() via entity_i18n_object_info().
  117. *
  118. * Go with the same default values as the admin UI as far as possible.
  119. */
  120. public function hook_object_info() {
  121. $wildcard = $this->menuWildcard();
  122. $id_key = !empty($this->entityInfo['entity keys']['name']) ? $this->entityInfo['entity keys']['name'] : $this->entityInfo['entity keys']['id'];
  123. $info[$this->entityType] = array(
  124. // Generic object title.
  125. 'title' => $this->entityInfo['label'],
  126. // The object key field.
  127. 'key' => $id_key,
  128. // Placeholders for automatic paths.
  129. 'placeholders' => array(
  130. $wildcard => $id_key,
  131. ),
  132. // Properties for string translation.
  133. 'string translation' => array(
  134. // Text group that will handle this object's strings.
  135. 'textgroup' => $this->textgroup,
  136. // Object type property for string translation.
  137. 'type' => $this->entityType,
  138. // Translatable properties of these objects.
  139. 'properties' => $this->translatableProperties(),
  140. ),
  141. );
  142. // Integrate the translate tab into the admin-UI if enabled.
  143. if ($base_path = $this->menuBasePath()) {
  144. $info[$this->entityType] += array(
  145. // To produce edit links automatically.
  146. 'edit path' => $base_path . '/manage/' . $wildcard,
  147. // Auto-generate translate tab.
  148. 'translate tab' => $base_path . '/manage/' . $wildcard . '/translate',
  149. );
  150. $info[$this->entityType]['string translation'] += array(
  151. // Path to translate strings to every language.
  152. 'translate path' => $base_path . '/manage/' . $wildcard . '/translate/%i18n_language',
  153. );
  154. }
  155. return $info;
  156. }
  157. /**
  158. * Defines the menu base path used by self::hook_object_info().
  159. */
  160. protected function menuBasePath() {
  161. return !empty($this->entityInfo['admin ui']['path']) ? $this->entityInfo['admin ui']['path'] : FALSE;
  162. }
  163. /**
  164. * Defines the menu wildcard used by self::hook_object_info().
  165. */
  166. protected function menuWildcard() {
  167. return isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
  168. }
  169. /**
  170. * Defines translatable properties used by self::hook_object_info().
  171. */
  172. protected function translatableProperties() {
  173. $list = array();
  174. foreach (entity_get_all_property_info($this->entityType) as $name => $info) {
  175. if (!empty($info['translatable']) && !empty($info['i18n string'])) {
  176. $list[$name] = array(
  177. 'title' => $info['label'],
  178. );
  179. }
  180. }
  181. return $list;
  182. }
  183. /**
  184. * Implements hook_i18n_string_objects() via entity_i18n_string_objects().
  185. */
  186. public function hook_string_objects() {
  187. return entity_load_multiple_by_name($this->entityType, FALSE);
  188. }
  189. }