all contrib module security updates done
This commit is contained in:
@@ -1,6 +1,201 @@
|
||||
|
||||
Entity Translation 7.x-1.x, xxxx-xx-xx
|
||||
--------------------------------------
|
||||
|
||||
|
||||
Entity Translation 7.x-1.1, 2019-11-24
|
||||
--------------------------------------
|
||||
#2877103 by stefanos.petrakis, Simon Georges, Delphine Lepers, Andras_Szilagyi,
|
||||
drasgardian, Alexander Fedorenko, plach: Content can not be created in
|
||||
another language as the one chosen by configuration
|
||||
#2580231 by danyg, stefanos.petrakis, MariskaTh: Undefined index in
|
||||
entity_translation.views.inc
|
||||
#3085046 by stefanos.petrakis: Fix test breakage due to Title module
|
||||
7.x-1.0-beta1 version
|
||||
#3010146 by stefanos.petrakis: Expose extended ET-specific configuration for
|
||||
pathauto
|
||||
#3050434 by shital.mahajan, jweowu, stefanos.petrakis: Since PHP 7.0, functions
|
||||
inspecting arguments, like func_get_args(), no longer report the original
|
||||
value as passed to a parameter
|
||||
#3025770 by sudheeshps, patelmayank7552, stefanos.petrakis: PHP 7.0
|
||||
Compatibility
|
||||
#2536292 by plopesc, stefanos.petrakis: "Warning: array_merge(): Argument #2 is
|
||||
not an array in entity_translation_menu_alter" When 'page arguments' or
|
||||
'access arguments' are not defined in the original item
|
||||
#2921298 by Stevel, stefanos.petrakis: Missing test dependencies
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0, 2018-04-01
|
||||
--------------------------------------
|
||||
(no change)
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-rc1, 2018-03-21
|
||||
------------------------------------------
|
||||
#2927815 by Skabbkladden, dawehner: Infinite loop with language neutral alias
|
||||
and pathauto.
|
||||
#2890683 by tobiberlin, stefanos.petrakis: entity_translation_field_attach_form()
|
||||
adding unwanted fields to form.
|
||||
#2907366 by swati.nuna: Invalid hook hook_translation_info() mentioned in
|
||||
docblock for entity_translation_edit_form_info().
|
||||
#1799770 by plach, bforchhammer: Update id and bundle when setting a wrapped
|
||||
entity.
|
||||
#2339315 by plach: Added documentation for
|
||||
hook_entity_translation_source_field_state_alter().
|
||||
#1506054 by plach, Owen Barton: Taxonomy term reference: language-aware widget
|
||||
and autocomplete.
|
||||
#2907275 by jalpesh: CSS class name misspelled in entity_translation_overview().
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-beta7, 2017-08-21
|
||||
--------------------------------------------
|
||||
#2903195 by stefanos.petrakis, BAHbKA, plach: Enabling translation for not
|
||||
empty taxonomy vocabulary.
|
||||
#2425825 by jamesrward: Update from before beta4 fails.
|
||||
#2900094 by joseph.olstad: code sniffer drupal.org recommended code syntax
|
||||
fixes.
|
||||
#2883805 by mikran: entity_translation_update_7008 is broken when taxonomy
|
||||
module is not enabled.
|
||||
#2877074 by plach, stefanos.petrakis, joseph.olstad: Refactor the
|
||||
entity_translation_language() callback to make it bundle-specific.
|
||||
#2743685 by stefanos.petrakis, bwaindwain: Pathauto update for all translations
|
||||
for a single node.
|
||||
#2899658 by joseph.olstad, stefanos.petrakis: entity_translation_upgrade_do
|
||||
needs to automatically create the entity_translation row for the source
|
||||
language in entity_translation.
|
||||
#1800158 by meichr: Entity Translation Upgrade, drush extension.
|
||||
#2885858 by StephaneQ: Can't delete taxonomy term translation since beta6
|
||||
update.
|
||||
#2572203 by mattew: Wrong query generated when several Translations
|
||||
relationships are used.
|
||||
#2824255 by ccarrascal, plach: Notice: Undefined index for language in
|
||||
setTranslation().
|
||||
#2750179 by wadmiraal: Allowed default translation handler to be completely
|
||||
overridden.
|
||||
#2307937 by meichr, joseph.olstad: Batch API needs $context['results'] handled
|
||||
as an array, not a value.
|
||||
#1175170 by hgoto, Pisco, alberto56: Optionally enable disabled languages for
|
||||
entity translation.
|
||||
#2870524 by stefanos.petrakis, joseph.olstad: Field copy fails with content
|
||||
translation for fields with entity translation enabled.
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-beta6, 2017-03-03
|
||||
--------------------------------------------
|
||||
#2849464 by plach, czigor, stefanos.petrakis: Added an API to set the active
|
||||
language.
|
||||
#2438615 by David_Rothstein, matthiasm11: New nodes are always created using
|
||||
LANGUAGE_NONE (only changed to the correct language during form submission).
|
||||
#2820454 by Dylan Donkersgoed: PHP notice when attaching single field
|
||||
with field_attach_form.
|
||||
#2826297 by Thomas Cys, dwebpoint, stefanos.petrakis: 'clone' is a reserved
|
||||
keyword introduced in PHP version 5.0 and cannot be invoked as a function.
|
||||
#2189567 by stefanos.petrakis, jmuzz: Fixed problems enabling and disabling
|
||||
translation on a field with revisions enabled.
|
||||
#2798721 by gnucifer: Path scheme not initialized on delete translation
|
||||
confirmation form.
|
||||
#2215771 by colan, stefanos.petrakis: Fixed "Undefined index: translate in
|
||||
EntityTranslationDefaultHandler->entityForm()".
|
||||
#1829636 by bforchhammer, stefanos.petrakis: Fixed "All languages" suffix not
|
||||
working nicely with multi-value fields.
|
||||
#2746045 by fengtan: Converted dsm calls to watchdog calls for path scheme
|
||||
definition warnings.
|
||||
#2757601 by ctrnz, aks22: Fixed entity_translation_update_7009() assumes
|
||||
i18n_taxonomy is enabled.
|
||||
#1172104 by james.williams, daro.pl: Improved description of the language
|
||||
fallback option.
|
||||
#2734373 by plach, consulio: "entity_translation_update_7008" is broken when no
|
||||
term is translated.
|
||||
#2734301 by plach, sinasalek: Fixed Numeric value out of range: 1264 Out of
|
||||
range value for column 'i18n_mode'.
|
||||
#1661348 by plach, badrange, colan, candelas: Added "I18n Taxonomy" integration.
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-beta5, 2016-05-08
|
||||
--------------------------------------------
|
||||
#929402 by das-peter, bastnic, kristiaanvandeneynde, brockfanning, greenjuls,
|
||||
make77, steinmb: Added support for migrate module.
|
||||
#1291388 by Jax, poiu, plach: Added support for multilingual core search.
|
||||
#2339315 by loopduplicate, gnucifer, plopesc, james.williams: Fixed Source
|
||||
language prepopulated values should be also included in $form_state['field'].
|
||||
#2648062 by brunoric, cebasqueira, lucasr, mmchristoph: Fixed Fatal error: Call
|
||||
to a member function getFormLanguage() on a non-object.
|
||||
#S156840 by HelenaEksler, plach: Fixed XSS vulnerability in the translation
|
||||
form page title.
|
||||
#2444203 by Leksat, badrange, attiks: Show fallback information on the
|
||||
translation overview.
|
||||
#2458393 by catch, plach, douggreen: Fixed unindexed query in
|
||||
EntityTranslationDefaultHandler::loadMultiple().
|
||||
#2462909 by GeduR: Misspelling translation exists help in views handler
|
||||
definition.
|
||||
#2166157 by rsmylski, ciss: entity translation handler id for new entities not
|
||||
unique enough - can cause infinite/circular child reference.
|
||||
#2452279 by GeduR: Duplicated variable $language in
|
||||
entity_translation_edit_access().
|
||||
#2423661 by marcoscano: Module api documentation has wrong example ("path sets"
|
||||
instead of "path schemes").
|
||||
#2305547 by das-peter: Fixed undefined index name.
|
||||
#1989084 by jsacksick, candelas: Fixed <nolink> and validation.
|
||||
#2203801 by idflood: Impossible to update taxonomy term after enabling
|
||||
i18n_taxonomy.
|
||||
#2415189 by catch: Translation deletion bypasses entity saving.
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-beta4, 2015-01-23
|
||||
--------------------------------------------
|
||||
#2396103 by plach: Fixed [DATA LOSS] Translations deleted.
|
||||
#2218087 by plach: Fixed PHP Fatal error: Unsupported operand types in
|
||||
modules/field_ui/field_ui.admin.inc on line 477.
|
||||
#2396653 by SchnWalter, mongolito404: Fix validation handler for the entity
|
||||
language widget.
|
||||
#2395327 by damiankloip: FATAL error when requesting an edit path for a
|
||||
non-existent entity.
|
||||
#2342787 by jcisio: Language widget submit handler runs on disabled entity
|
||||
types.
|
||||
#2389945 by rafal.enden: Tests don't pass when using different source than
|
||||
original node language.
|
||||
#2185523 by leon.nk: Document that entity_translation_enabled_bundle() does not
|
||||
check whether the entity type is translatable.
|
||||
#2378195 by Johnny vd Laar: Node edit notice when author of a translation is
|
||||
deleted.
|
||||
#2382713 by seanr: entity_translation_update_7006 fails because of SQL error
|
||||
before variable_set('entity_translation_revision_enabled', FALSE); can run.
|
||||
#1046282 by plach, good_man, miro_dietiker, Gábor Hojtsy: Make the module work
|
||||
with revisions.
|
||||
#2179183 by dkingofpa, Jānis Bebrītis | SocialNicheGuru: Fixed Fatal error:
|
||||
Unsupported operand types in /entity_translation.module on line 188.
|
||||
#1605406 by joelrosen, Jose Reyero, paulihuhtiniemi, guillaumev | mindaugasd:
|
||||
Exposed translated field content to Views so that multiple language
|
||||
translations can be shown simultaneously.
|
||||
#2160559 by w3wfr: Warning because of empty value line 675.
|
||||
#2151503 by SebCorbin: Check for translation access for Add links.
|
||||
#2130091 by peximo: Media edit dialog integration bug.
|
||||
#2065221 by catch, plach: Fixed Static cache for handlers should key by $vid.
|
||||
#1516202 by fabsor, spotzero: Added a CTools access plugin for checking if a
|
||||
translation is available.
|
||||
#2027513 by GaëlG, jcisio: Added support for nested values for 'edit form' in
|
||||
hook_entity_info().
|
||||
#2073231 by plach: Rename entity_translation_form_language() to
|
||||
entity_translation_get_existing_language().
|
||||
#2072865 by douggreen: Fixed Only replace 'delete' button with
|
||||
'delete translation', don't add one that doesn't exist.
|
||||
#1865244 by plach | bojanz, agoradesign, ciss, interdruper, joel_osc: Added
|
||||
support for multiple translation handlers on the same form.
|
||||
#2055491 by plach: Fixed Synced image fields cannot be emptied.
|
||||
#1866076 by grndlvl: Added support for non-entity load tokens when declaring
|
||||
translations.
|
||||
|
||||
|
||||
Entity Translation 7.x-1.0-beta3, 2013-07-23
|
||||
--------------------------------------------
|
||||
#2037789 by Jelle_S: Fixed Default to user_access() access callback like drupal
|
||||
menu does.
|
||||
#1982140 by bforchhammer | carligraph: Fixed Menu item is deleted when adding a
|
||||
third translation.
|
||||
#2000926 by GiorgosK, bforchhammer: Fixed entity_translation_i18n_menu should
|
||||
check if entity is node before deleting menu links.
|
||||
#1991452 by twistor: Fixed Strict warnings on testing page.
|
||||
#1992588 by bforchhammer: Fixed PHP notices on node page (menu-related).
|
||||
#1979406 by milesw: Field values for node translations not filled correctly when
|
||||
making a field translatable.
|
||||
#1969366 by plach | EdNet: Fixed unsupported operand types in
|
||||
@@ -21,7 +216,7 @@ Entity Translation 7.x-1.x, xxxx-xx-xx
|
||||
#1947764 by plach: Improve bundle translatability checks.
|
||||
#1370900 by mojzis, plach | mgladding: Fixed Fatal Error: Cannot access empty
|
||||
property in translation.handler.inc.
|
||||
Issue #1924088 by plach | drzraf: Fixed Wrong form 'language' value in case of
|
||||
#1924088 by plach | drzraf: Fixed Wrong form 'language' value in case of
|
||||
content-translation enabled bundles.
|
||||
#1933742 by das-peter: Fixed Delete translation doesn't flush the entity cache.
|
||||
#1903024 by fabsor | dimitrileonidas: Fixed SQL error in Views when adding an
|
||||
@@ -223,7 +418,7 @@ Entity Translation 7.x-1.0-alpha1, 2011-09-07
|
||||
editing workflow.
|
||||
#1031370 by joostvdl | rfay: Fixed Translation publishing status is hidden under
|
||||
collapsed 'publishing options' fieldset (and is hidden by default).
|
||||
#1003876 by plach, good_man: (follow-up) Fixed original label translation.
|
||||
#1003876 by plach, good_man: (follow-up) Fixed original label translation.
|
||||
#1003876 by good_man, plach: Introduced support for translated entity labels.
|
||||
#936646 by good_man, fietserwin, plach, klonos: Fixed Undefined index access
|
||||
callback/arguments in entity_translation_menu().
|
||||
|
||||
@@ -9,8 +9,9 @@ Project maintainers
|
||||
The Entity translation maintainers oversee the development of the project as a
|
||||
whole. The project maintainers for Entity translation are:
|
||||
|
||||
- Francesco Placella 'plach' <http://drupal.org/user/183211>
|
||||
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
|
||||
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>
|
||||
- Daniel F. Kudwien 'sun' <https://www.drupal.org/u/sun>
|
||||
- Stefanos Petrakis 'stefanos.petrakis' <https://www.drupal.org/u/stefanospetrakis>
|
||||
|
||||
|
||||
Component maintainers
|
||||
@@ -25,14 +26,15 @@ http://drupal.org/node/363367 to find out how to become a component maintainer.
|
||||
Current component maintainers for Entity translation:
|
||||
|
||||
Base system
|
||||
- Francesco Placella 'plach' <http://drupal.org/user/183211>
|
||||
- Benedikt Forchhammer 'bforchhammer' <http://drupal.org/user/216396>
|
||||
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>
|
||||
- Benedikt Forchhammer 'bforchhammer' <https://www.drupal.org/u/bforchhammer>
|
||||
- Stefanos Petrakis 'stefanos.petrakis' <https://www.drupal.org/u/stefanospetrakis>
|
||||
|
||||
Menu integration
|
||||
- Benedikt Forchhammer 'bforchhammer' <http://drupal.org/user/216396>
|
||||
- Benedikt Forchhammer 'bforchhammer' <https://www.drupal.org/u/bforchhammer>
|
||||
|
||||
Views integration
|
||||
- Fabian Sörqvist 'fabsor' <http://drupal.org/user/255704>
|
||||
- Fabian Sörqvist 'fabsor' <https://www.drupal.org/u/fabsor>
|
||||
|
||||
Node translation upgrade
|
||||
- Francesco Placella 'plach' <http://drupal.org/user/183211>
|
||||
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>
|
||||
|
||||
@@ -29,9 +29,50 @@ None.
|
||||
* @todo
|
||||
|
||||
|
||||
-- DEVELOPERS --
|
||||
|
||||
--- PHP7 Compatibility detection ---
|
||||
|
||||
Use PHP Code Sniffer (via coder) with the PHPCompatibility sniffs installed.
|
||||
|
||||
* https://www.drupal.org/project/coder
|
||||
* https://github.com/PHPCompatibility/PHPCompatibility
|
||||
|
||||
Once phpcs is installed, a simple way to add PHPCompatibility is:
|
||||
|
||||
* Run 'phpcs -i' to confirm the current recognised coding standards.
|
||||
Assuming that PHPCompatibility is not already listed...
|
||||
|
||||
* Run 'phpcs --config-show' and take note of the "installed_paths" value.
|
||||
|
||||
* Run 'composer require phpcompatibility/php-compatibility'
|
||||
|
||||
* Establish the absolute /path/to/vendor/phpcompatibility/php-compatibility
|
||||
which you should now have somewhere.
|
||||
|
||||
* Run 'phpcs --config-set installed_paths OLD,NEW' where OLD is the original
|
||||
value, and NEW is the php-compatibility path.
|
||||
|
||||
* Run 'phpcs -i' to confirm that PHPCompatibility has been added to the list
|
||||
of coding standards.
|
||||
|
||||
* Run 'phpcs --standard=PHPCompatibility <files>' to check <files>
|
||||
|
||||
|
||||
--- PHP7 Compatibility hints ---
|
||||
|
||||
func_get_args() should be placed at the beginning of a method or function,
|
||||
before any modifications to the function's arguments take place. Its value
|
||||
should be assigned to a locally scoped variable - usually named $args by
|
||||
convention - to be used inside the function as needed. This is due to
|
||||
changes in PHP 7.0 and onwards.
|
||||
@see http://php.net/manual/en/function.func-get-args.php#refsect1-function.func-get-args-notes
|
||||
|
||||
|
||||
-- CONTACT --
|
||||
|
||||
Current maintainers:
|
||||
* Francesco Placella (plach) - http://drupal.org/user/183211
|
||||
* Daniel F. Kudwien (sun) - http://drupal.org/user/54136
|
||||
* Stefanos Petrakis (stefanos.petrakis) - https://www.drupal.org/u/stefanospetrakis
|
||||
|
||||
|
||||
+158
-24
@@ -13,11 +13,30 @@ function entity_translation_admin_form($form, $form_state) {
|
||||
|
||||
$form['locale_field_language_fallback'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable language fallback'),
|
||||
'#description' => t('When language fallback is enabled, if a translation is not available for the requested language an existing one will be displayed.'),
|
||||
'#title' => t('Allow language fallback'),
|
||||
'#description' => t('When language fallback is allowed, an alternative translation will be displayed if the requested language is not available. Other modules, such as <a href="https://www.drupal.org/project/language_hierarchy" target="_blank">Language Hierarchy</a>, are needed to manage which languages are used as fallbacks. Otherwise, existing translations will be chosen based on their <a href="!url">language weight</a>.', array('!url' => url('admin/config/regional/language'))),
|
||||
'#default_value' => variable_get('locale_field_language_fallback', TRUE),
|
||||
);
|
||||
|
||||
$form['entity_translation_languages_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Use only enabled languages'),
|
||||
'#description' => t('If checked, disabled languages will not show up as available languages. This setting does not apply to content types that are configured to use the Multilingual content (i18n_node) module as this module provides its own configuration.'),
|
||||
'#default_value' => variable_get('entity_translation_languages_enabled', FALSE),
|
||||
);
|
||||
|
||||
$form['entity_translation_show_fallback_on_overview_pages'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show fallback statuses on overview pages'),
|
||||
'#description' => t('Enable to the show fallback information on the entity overview pages.'),
|
||||
'#default_value' => variable_get('entity_translation_show_fallback_on_overview_pages', FALSE),
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="locale_field_language_fallback"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['entity_translation_shared_labels'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Display shared labels'),
|
||||
@@ -33,7 +52,7 @@ function entity_translation_admin_form($form, $form_state) {
|
||||
);
|
||||
|
||||
$_null = NULL;
|
||||
list($items, ) = menu_router_build();
|
||||
list($items,) = menu_router_build();
|
||||
_entity_translation_validate_path_schemes($_null, FALSE, $items);
|
||||
|
||||
foreach (entity_get_info() as $entity_type => $info) {
|
||||
@@ -153,6 +172,46 @@ function entity_translation_admin_form($form, $form_state) {
|
||||
}
|
||||
}
|
||||
|
||||
if (module_exists('pathauto')) {
|
||||
$form['pathauto'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Pathauto'),
|
||||
'#collapsible' => FALSE,
|
||||
);
|
||||
|
||||
$form['pathauto']['entity_translation_pathauto_state_active_new_translation'] = array(
|
||||
'#title' => t('Pathauto state automatically activated on forms for new translations'),
|
||||
'#type' => 'checkbox',
|
||||
'#description' => t('When this option is enabled, the Pathauto state checkbox is automatically selected on the add-translation form. <br/><b>Attention:</b>When combining this option with the next one it may lead to cases where all aliases for all languages are auto-updated whenever a new translation is added.'),
|
||||
'#default_value' => variable_get('entity_translation_pathauto_state_active_new_translation', TRUE),
|
||||
);
|
||||
|
||||
// These are the possible options when pathauto state is active.
|
||||
$mode_options = array(
|
||||
'generate_alias_active_language' => t('Generate alias only for current language'),
|
||||
'generate_missing_aliases_all_languages' => t('Generate missing aliases for all languages'),
|
||||
'generate_all_aliases_all_languages' => t('Generate all aliases for all languages'),
|
||||
);
|
||||
|
||||
// Single entity update mode.
|
||||
$form['pathauto']['entity_translation_pathauto_state_mode_update'] = array(
|
||||
'#title' => t('Pathauto state mode for single update'),
|
||||
'#type' => 'select',
|
||||
'#description' => t('When the Pathauto state is activated, the mode defined here will be used for alias generation, on a single update, e.g. when editing a node using the node edit form.'),
|
||||
'#options' => $mode_options,
|
||||
'#default_value' => variable_get('entity_translation_pathauto_state_mode_update', 'generate_alias_active_language'),
|
||||
);
|
||||
|
||||
// Bulk entity updates mode.
|
||||
$form['pathauto']['entity_translation_pathauto_state_mode_bulkupdate'] = array(
|
||||
'#title' => t('Pathauto state mode for bulk updates'),
|
||||
'#type' => 'select',
|
||||
'#description' => t('When the Pathauto state is activated, the mode defined here will be used for alias generation, on bulk updates.'),
|
||||
'#options' => $mode_options,
|
||||
'#default_value' => variable_get('entity_translation_pathauto_state_mode_bulkupdate', 'generate_all_aliases_all_languages'),
|
||||
);
|
||||
}
|
||||
|
||||
$form = system_settings_form($form);
|
||||
|
||||
// Menu rebuilding needs to be performed after the system settings are saved.
|
||||
@@ -198,12 +257,17 @@ function entity_translation_settings_init($entity_type, $settings = array()) {
|
||||
* Translations overview page callback.
|
||||
*/
|
||||
function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
$args = func_get_args();
|
||||
|
||||
// Entity translation and node translation share the same system path.
|
||||
if ($callback && entity_translation_node($entity_type, $entity)) {
|
||||
return entity_translation_overview_callback($callback, $entity);
|
||||
if ($callback && !entity_translation_enabled($entity_type, $entity)) {
|
||||
$callback_args = array_slice($args, 3);
|
||||
return entity_translation_overview_callback($callback, $callback_args);
|
||||
}
|
||||
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
// Ensure $entity holds an entity object and not an id.
|
||||
$entity = $handler->getEntity();
|
||||
$handler->initPathScheme();
|
||||
|
||||
// Initialize translations if they are empty.
|
||||
@@ -217,8 +281,7 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
// languages.
|
||||
if ($handler->initOriginalTranslation()) {
|
||||
// FIXME!
|
||||
field_attach_presave($entity_type, $entity);
|
||||
field_attach_update($entity_type, $entity);
|
||||
entity_translation_entity_save($entity_type, $entity);
|
||||
}
|
||||
|
||||
$header = array(t('Language'), t('Source language'), t('Translation'), t('Status'), t('Operations'));
|
||||
@@ -234,6 +297,8 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
$links = EntityTranslationDefaultHandler::languageSwitchLinks($path);
|
||||
}
|
||||
|
||||
$show_fallback = variable_get('locale_field_language_fallback', TRUE) && variable_get('entity_translation_show_fallback_on_overview_pages', FALSE);
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$classes = array();
|
||||
$options = array();
|
||||
@@ -284,7 +349,7 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
// No such translation in the set yet: help user to create it.
|
||||
$row_title = $source_name = t('n/a');
|
||||
|
||||
if ($source != $langcode && $handler->getAccess('update')) {
|
||||
if ($source != $langcode && $handler->getAccess('update') && $handler->getTranslationAccess($langcode)) {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$translatable = FALSE;
|
||||
|
||||
@@ -300,9 +365,21 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
$link = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array('href' => $add_path, 'language' => $language);
|
||||
$link['query'] = isset($_GET['destination']) ? drupal_get_destination() : FALSE;
|
||||
$options[] = $translatable ? l(t('add'), $link['href'], $link) : t('No translatable fields');
|
||||
$classes[] = $translatable ? '' : 'non-traslatable';
|
||||
$classes[] = $translatable ? '' : 'non-translatable';
|
||||
}
|
||||
$status = t('Not translated');
|
||||
// Show fallback information if required.
|
||||
if ($show_fallback) {
|
||||
$language_fallback_candidates = _entity_translation_language_fallback_get_candidates($language);
|
||||
$fallback_candidates = array_intersect_key(drupal_map_assoc($language_fallback_candidates), $translations->data);
|
||||
$fallback_langcode = reset($fallback_candidates);
|
||||
if ($fallback_langcode !== FALSE) {
|
||||
$status = t('Fallback from @language', array('@language' => $languages[$fallback_langcode]->name));
|
||||
$label = _entity_translation_label($entity_type, $entity, $fallback_langcode);
|
||||
$link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array('href' => $path, 'language' => $langcode);
|
||||
$row_title = l($label, $link['href'], $link);
|
||||
}
|
||||
}
|
||||
}
|
||||
$rows[] = array(
|
||||
'data' => array($language_name, $source_name, $row_title, $status, implode(" | ", $options)),
|
||||
@@ -329,13 +406,13 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
|
||||
/**
|
||||
* Calls the appropriate translation overview callback.
|
||||
*/
|
||||
function entity_translation_overview_callback($callback, $entity) {
|
||||
function entity_translation_overview_callback($callback, $args) {
|
||||
if (module_exists($callback['module'])) {
|
||||
if (isset($callback['file'])) {
|
||||
$path = isset($callback['file path']) ? $callback['file path'] : drupal_get_path('module', $callback['module']);
|
||||
require_once DRUPAL_ROOT . '/' . $path . '/' . $callback['file'];
|
||||
}
|
||||
return $callback['page callback']($entity);
|
||||
return call_user_func_array($callback['page callback'], $args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +438,7 @@ function _entity_translation_label($entity_type, $entity, $langcode = NULL) {
|
||||
/**
|
||||
* Theme wrapper for the entity translation language page.
|
||||
*/
|
||||
function theme_entity_translation_overview($variables){
|
||||
function theme_entity_translation_overview($variables) {
|
||||
$rows = $variables['rows'];
|
||||
return theme('table', array(
|
||||
'rows' => $rows,
|
||||
@@ -372,7 +449,7 @@ function theme_entity_translation_overview($variables){
|
||||
/**
|
||||
* Theme wrapper for the entity translation language outdated translation.
|
||||
*/
|
||||
function theme_entity_translation_overview_outdated($variables){
|
||||
function theme_entity_translation_overview_outdated($variables) {
|
||||
$message = $variables['message'];
|
||||
return ' - <span class="marker">' . $message . '</span>';
|
||||
}
|
||||
@@ -382,12 +459,15 @@ function theme_entity_translation_overview_outdated($variables){
|
||||
*/
|
||||
function entity_translation_delete_confirm($form, $form_state, $entity_type, $entity, $langcode) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler->setActiveLanguage($langcode);
|
||||
$handler->initPathScheme();
|
||||
$languages = language_list();
|
||||
|
||||
$form = array(
|
||||
'#handler' => $handler,
|
||||
'#entity_type' => $entity_type,
|
||||
'#entity' => $entity,
|
||||
// Ensure $entity holds an entity object and not an id.
|
||||
'#entity' => $handler->getEntity(),
|
||||
'#language' => $langcode,
|
||||
);
|
||||
|
||||
@@ -416,13 +496,12 @@ function entity_translation_delete_confirm_submit($form, &$form_state) {
|
||||
|
||||
// Remove the translation entry and the related fields.
|
||||
$handler->removeTranslation($langcode);
|
||||
field_attach_presave($entity_type, $entity);
|
||||
field_attach_update($entity_type, $entity);
|
||||
entity_translation_entity_save($entity_type, $entity);
|
||||
|
||||
$form_state['redirect'] = $handler->getTranslatePath();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Confirm form for changing field translatability.
|
||||
*/
|
||||
function entity_translation_translatable_form($form, &$form_state, $field_name) {
|
||||
@@ -517,7 +596,7 @@ function entity_translation_translatable_form_submit($form, $form_state) {
|
||||
batch_set($batch);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Toggle translatability of the given field.
|
||||
*
|
||||
* This is called from a batch operation, but should only run once per field.
|
||||
@@ -548,6 +627,7 @@ function entity_translation_translatable_batch($translatable, $field_name, $copy
|
||||
$query = new EntityFieldQuery();
|
||||
$count = $query
|
||||
->fieldCondition($field_name)
|
||||
->age(FIELD_LOAD_REVISION)
|
||||
->count()
|
||||
->execute();
|
||||
|
||||
@@ -567,11 +647,32 @@ function entity_translation_translatable_batch($translatable, $field_name, $copy
|
||||
$result = $query
|
||||
->fieldCondition($field_name)
|
||||
->entityOrderBy('entity_id')
|
||||
->age(FIELD_LOAD_REVISION)
|
||||
->range($offset, $limit)
|
||||
->execute();
|
||||
|
||||
foreach ($result as $entity_type => $entities) {
|
||||
foreach (entity_load($entity_type, array_keys($entities)) as $entity) {
|
||||
foreach ($result as $entity_type => $partial_entities) {
|
||||
|
||||
// Load all revisionable entities' revisions.
|
||||
if (EntityTranslationDefaultHandler::isEntityTypeRevisionable($entity_type)) {
|
||||
$entities = array();
|
||||
// Extract the revision identifier from the entity's definition.
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$revision_id_key = $entity_info['entity keys']['revision'];
|
||||
// Load each revisionable entity's revision using $conditions, which
|
||||
// should include the revision id information.
|
||||
foreach ($partial_entities as $partial_entity) {
|
||||
$conditions = (array) $partial_entity;
|
||||
$revision_condition = array_intersect_key($conditions, array($revision_id_key => $revision_id_key));
|
||||
$entity_revisions = entity_load($entity_type, FALSE, $revision_condition);
|
||||
$entities[] = reset($entity_revisions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$entities = entity_load($entity_type, array_keys($partial_entities));
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
$context['sandbox']['progress']++;
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$langcode = $handler->getLanguage();
|
||||
@@ -590,9 +691,10 @@ function entity_translation_translatable_batch($translatable, $field_name, $copy
|
||||
if ($translatable && isset($entity->{$field_name}[LANGUAGE_NONE])) {
|
||||
// If the field is being switched to translatable and has data for
|
||||
// LANGUAGE_NONE then we need to move the data to the right language.
|
||||
|
||||
// In case the 'Copy translations' option was selected, move the
|
||||
// available LANGUAGE_NONE field data into all existing translation
|
||||
// languages, otherwise only into the entity's language.
|
||||
$translations = $handler->getTranslations();
|
||||
|
||||
if ($copy_all_languages && !empty($translations->data)) {
|
||||
foreach ($translations->data as $translation) {
|
||||
$entity->{$field_name}[$translation['language']] = $entity->{$field_name}[LANGUAGE_NONE];
|
||||
@@ -650,8 +752,7 @@ function _entity_translation_update_field($entity_type, $entity, $field_name) {
|
||||
// otherwise any stored empty field value would be deleted. If this happens
|
||||
// the range queries would be messed up.
|
||||
if ($empty < count($entity->{$field_name})) {
|
||||
field_attach_presave($entity_type, $entity);
|
||||
field_attach_update($entity_type, $entity);
|
||||
entity_translation_entity_save($entity_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,3 +768,36 @@ function entity_translation_translatable_batch_done($success, $results, $operati
|
||||
drupal_set_message(t("Something went wrong while processing data. Some nodes may appear to have lost fields."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns language fallback candidates for a certain language.
|
||||
*
|
||||
* @param object $language
|
||||
* Drupal language object.
|
||||
*
|
||||
* @return array
|
||||
* An array of language codes in the fallback order.
|
||||
*/
|
||||
function _entity_translation_language_fallback_get_candidates($language) {
|
||||
// Save original fallback candidates.
|
||||
$language_fallback_original = drupal_static('language_fallback_get_candidates');
|
||||
// Replace all global languages with the given one. We need this because the
|
||||
// language_fallback_get_candidates() does not take the $language parameter,
|
||||
// however other modules (like language_fallback) may use current language(s)
|
||||
// when they alter the language fallback candidates.
|
||||
$languages_original = array();
|
||||
foreach (language_types() as $language_type) {
|
||||
$languages_original[$language_type] = $GLOBALS[$language_type];
|
||||
$GLOBALS[$language_type] = $language;
|
||||
}
|
||||
// Clear static cache, so that fallback candidates are recalculated.
|
||||
drupal_static_reset('language_fallback_get_candidates');
|
||||
$language_fallback_candidates = language_fallback_get_candidates();
|
||||
// Restore original data.
|
||||
$language_fallback =& drupal_static('language_fallback_get_candidates');
|
||||
$language_fallback = $language_fallback_original;
|
||||
foreach ($languages_original as $language_type => $_language) {
|
||||
$GLOBALS[$language_type] = $_language;
|
||||
}
|
||||
return $language_fallback_candidates;
|
||||
}
|
||||
|
||||
+29
-2
@@ -64,7 +64,7 @@
|
||||
* - skip original values access: A flag specifying whether skipping access
|
||||
* control when editing original values for this entity. Defaults to FALSE.
|
||||
* - bundle callback: A callback to check whether the passed bundle has entity
|
||||
* translation enabled. If empty all bundles are supposed to be enabled.
|
||||
* translation enabled. Defaults to TRUE for all bundles.
|
||||
* - default settings: The defaults to be applied to settings when an explicit
|
||||
* choice is missing.
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ function hook_entity_info() {
|
||||
'translation' => array(
|
||||
'entity_translation' => array(
|
||||
'class' => 'EntityTranslationCustomEntityHandler',
|
||||
'path sets' => array(
|
||||
'path schemes' => array(
|
||||
'default' => array(
|
||||
'base path' => 'custom_entity_2/%custom_entity',
|
||||
'path wildcard' => '%custom_entity',
|
||||
@@ -151,3 +151,30 @@ function hook_entity_translation_update($entity_type, $entity, $translation, $va
|
||||
*/
|
||||
function hook_entity_translation_delete($entity_type, $entity, $langcode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows modules to act when a revision translation is deleted.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $entity
|
||||
* The entity.
|
||||
* @param $langcode
|
||||
* The langcode of the revision translation which was deleted.
|
||||
*/
|
||||
function hook_entity_translation_delete_revision($entity_type, $entity, $langcode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to sets the right values in the form state when adding a translation.
|
||||
*/
|
||||
function hook_entity_translation_source_field_state_alter(&$field_state) {
|
||||
if (isset($field_state['entity'])) {
|
||||
module_load_include('inc', 'entity', 'includes/entity.ui');
|
||||
foreach ($field_state['entity'] as $delta => $entity) {
|
||||
if ($entity instanceof FieldCollectionItemEntity) {
|
||||
$field_state['entity'][$delta] = entity_ui_clone_entity('field_collection_item', $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,18 @@ core = 7.x
|
||||
configure = admin/config/regional/entity_translation
|
||||
dependencies[] = locale (>7.14)
|
||||
|
||||
test_dependencies[] = pathauto:pathauto
|
||||
; The version restriction in the following line could be
|
||||
; removed as soon as [#3085061] is fixed.
|
||||
test_dependencies[] = title:title (=7.x-1.0-alpha9)
|
||||
|
||||
files[] = includes/translation.handler_factory.inc
|
||||
files[] = includes/translation.handler.inc
|
||||
files[] = includes/translation.handler.comment.inc
|
||||
files[] = includes/translation.handler.node.inc
|
||||
files[] = includes/translation.handler.taxonomy_term.inc
|
||||
files[] = includes/translation.handler.user.inc
|
||||
files[] = includes/translation.migrate.inc
|
||||
|
||||
files[] = tests/entity_translation.test
|
||||
|
||||
@@ -19,10 +26,10 @@ files[] = views/entity_translation_handler_field_label.inc
|
||||
files[] = views/entity_translation_handler_filter_entity_type.inc
|
||||
files[] = views/entity_translation_handler_filter_language.inc
|
||||
files[] = views/entity_translation_handler_filter_translation_exists.inc
|
||||
files[] = views/entity_translation_handler_field_field.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-04-26
|
||||
version = "7.x-1.0-beta2+33-dev"
|
||||
; Information added by Drupal.org packaging script on 2019-11-24
|
||||
version = "7.x-1.1"
|
||||
core = "7.x"
|
||||
project = "entity_translation"
|
||||
datestamp = "1366981073"
|
||||
|
||||
datestamp = "1574630892"
|
||||
|
||||
+245
-1
@@ -9,6 +9,8 @@
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function entity_translation_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['entity_translation'] = array(
|
||||
'description' => 'Table to track entity translations',
|
||||
'fields' => array(
|
||||
@@ -25,7 +27,12 @@ function entity_translation_schema() {
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity id this translation relates to',
|
||||
),
|
||||
// @todo: Consider an integer field for 'language'.
|
||||
'revision_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity revision id this translation relates to',
|
||||
),
|
||||
'language' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
@@ -73,6 +80,78 @@ function entity_translation_schema() {
|
||||
),
|
||||
'primary key' => array('entity_type', 'entity_id', 'language'),
|
||||
);
|
||||
|
||||
$schema['entity_translation_revision'] = array(
|
||||
'description' => 'Table to track entity translation revisions',
|
||||
'fields' => array(
|
||||
'entity_type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The entity type this translation revision relates to',
|
||||
),
|
||||
'entity_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity id this translation revision relates to',
|
||||
),
|
||||
'revision_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity revision id this translation relates to',
|
||||
),
|
||||
'language' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The target language for this translation revision.',
|
||||
),
|
||||
'source' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The source language from which this translation revision was created.',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The author of this translation revision.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'translate' => array(
|
||||
'description' => 'A boolean indicating whether this translation revision needs to be updated.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'The Unix timestamp when the translation revision was created.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the translation revision was most recently saved.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('entity_type', 'revision_id', 'language'),
|
||||
'indexes'=> array('revision_id' => array('revision_id')),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
@@ -93,6 +172,12 @@ function entity_translation_install() {
|
||||
|
||||
// Make translation use the content language type.
|
||||
variable_set('translation_language_type', LANGUAGE_TYPE_CONTENT);
|
||||
|
||||
// Enable revision support for entity translation.
|
||||
variable_set('entity_translation_revision_enabled', TRUE);
|
||||
|
||||
// Enable taxonomy autocomplete support.
|
||||
variable_set('entity_translation_taxonomy_autocomplete', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,3 +339,162 @@ function entity_translation_update_7003() {
|
||||
function entity_translation_update_7004() {
|
||||
entity_info_cache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the class registry to pick up the translation handler factory class.
|
||||
*/
|
||||
function entity_translation_update_7005() {
|
||||
registry_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add revision schema for entity translation metadata.
|
||||
*/
|
||||
function entity_translation_update_7006() {
|
||||
// Create revision id column.
|
||||
$spec = array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
// If we have existing data we cannot enforce this to be NOT NULL.
|
||||
'not null' => FALSE,
|
||||
'description' => 'The entity revision id this translation relates to',
|
||||
);
|
||||
|
||||
// Create revision id column if it doesn't exist already.
|
||||
if (!db_field_exists('entity_translation', 'revision_id')) {
|
||||
db_add_field('entity_translation', 'revision_id', $spec);
|
||||
}
|
||||
|
||||
// Create the entity translation revision schema.
|
||||
$table = array(
|
||||
'description' => 'Table to track entity translation revisions',
|
||||
'fields' => array(
|
||||
'entity_type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The entity type this translation revision relates to',
|
||||
),
|
||||
'entity_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity id this translation revision relates to',
|
||||
),
|
||||
'revision_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The entity revision id this translation relates to',
|
||||
),
|
||||
'language' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The target language for this translation revision.',
|
||||
),
|
||||
'source' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The source language from which this translation revision was created.',
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The author of this translation revision.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'translate' => array(
|
||||
'description' => 'A boolean indicating whether this translation revision needs to be updated.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'The Unix timestamp when the translation revision was created.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the translation revision was most recently saved.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('entity_type', 'revision_id', 'language'),
|
||||
'indexes'=> array('revision_id' => array('revision_id')),
|
||||
);
|
||||
|
||||
// Create entity translation revision table if it doesn't exist already.
|
||||
if (!db_table_exists('entity_translation_revision')) {
|
||||
db_create_table('entity_translation_revision', $table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable revision support on existing installations.
|
||||
*/
|
||||
function entity_translation_update_7007() {
|
||||
// Revision support is not enabled by default on existing installations as
|
||||
// making it work implies copying translation metadata to the
|
||||
// {entity_translation_revision} table for all the existing translations.
|
||||
// Since this process cannot be reliably implemented in an update function,
|
||||
// we leave the choice of manually performing the upgrade to people with the
|
||||
// required skills to do so. Be aware that enabling revision support on sites
|
||||
// where data has not been manually migrated may cause translation metadata to
|
||||
// be permanently lost or corrupted. See https://www.drupal.org/node/2396103.
|
||||
variable_set('entity_translation_revision_enabled', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Entity Translation for taxonomy vocabularies having terms translated
|
||||
* through it.
|
||||
*/
|
||||
function entity_translation_update_7008() {
|
||||
if (!module_exists('taxonomy')) {
|
||||
return;
|
||||
}
|
||||
// According to EXPLAIN joining {taxonomy_vocabulary} here makes the query
|
||||
// perform way worse, so we just split into two quick ones.
|
||||
$query = "SELECT t.vid
|
||||
FROM {entity_translation} et
|
||||
JOIN {taxonomy_term_data} t ON et.entity_type = 'taxonomy_term' AND et.entity_id = t.tid AND et.source != ''
|
||||
GROUP BY t.vid";
|
||||
$vids = db_query($query)->fetchCol();
|
||||
|
||||
if ($vids) {
|
||||
$query = "SELECT v.machine_name FROM {taxonomy_vocabulary} v WHERE v.vid IN (:vids)";
|
||||
$names = db_query($query, array(':vids' => $vids))->fetchCol();
|
||||
|
||||
$info = variable_get('entity_translation_taxonomy', array());
|
||||
foreach ($names as $name) {
|
||||
$info[$name] = TRUE;
|
||||
}
|
||||
variable_set('entity_translation_taxonomy', $info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure "i18n_mode" is correctly set for vocabularies having entity
|
||||
* translation enabled.
|
||||
*/
|
||||
function entity_translation_update_7009() {
|
||||
$info = array_filter(variable_get('entity_translation_taxonomy', array()));
|
||||
if ($info && module_exists('i18n_taxonomy')) {
|
||||
$query = "UPDATE {taxonomy_vocabulary} SET i18n_mode = :et_mode WHERE machine_name IN (:names)";
|
||||
$names = array_keys(array_filter($info));
|
||||
db_query($query, array(':et_mode' => 32768, ':names' => $names));
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Implements a Migrate destination handler to automatically add translations
|
||||
* to entities with translatable fields.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_migrate_api().
|
||||
*/
|
||||
function entity_translation_migrate_api() {
|
||||
$api = array(
|
||||
'api' => 2,
|
||||
'destination handlers' => array('MigrateTranslationEntityHandler'),
|
||||
);
|
||||
return $api;
|
||||
}
|
||||
+361
-227
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
module_load_include('inc', 'entity_translation', 'entity_translation.node');
|
||||
module_load_include('inc', 'entity_translation', 'entity_translation.taxonomy');
|
||||
|
||||
|
||||
/**
|
||||
@@ -23,6 +24,11 @@ define('ENTITY_TRANSLATION_LANGUAGE_CURRENT', 'xx-et-current');
|
||||
*/
|
||||
define('ENTITY_TRANSLATION_LANGUAGE_AUTHOR', 'xx-et-author');
|
||||
|
||||
/**
|
||||
* Defines an i18n translation mode for Entity Translation.
|
||||
*/
|
||||
define('I18N_MODE_ENTITY_TRANSLATION', 32768);
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
@@ -105,8 +111,11 @@ function entity_translation_entity_info() {
|
||||
'translation' => array(
|
||||
'entity_translation' => array(
|
||||
'class' => 'EntityTranslationTaxonomyTermHandler',
|
||||
'access callback' => 'entity_translation_taxonomy_term_tab_access',
|
||||
'access arguments' => array(1),
|
||||
'base path' => 'taxonomy/term/%taxonomy_term',
|
||||
'edit form' => 'term',
|
||||
'bundle callback' => 'entity_translation_taxonomy_term_enabled_vocabulary',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -129,7 +138,7 @@ function entity_translation_entity_info() {
|
||||
* Processes the given path schemes and fill-in default values.
|
||||
*/
|
||||
function _entity_translation_process_path_schemes($entity_type, &$et_info) {
|
||||
$path_scheme_keys = array_flip(array('base path', 'view path', 'edit path', 'translate path', 'path wildcard', 'admin theme'));
|
||||
$path_scheme_keys = array_flip(array('base path', 'view path', 'edit path', 'translate path', 'path wildcard', 'admin theme', 'edit tabs'));
|
||||
|
||||
// Insert the default path scheme into the 'path schemes' array and remove
|
||||
// respective elements from the entity_translation info array.
|
||||
@@ -164,6 +173,7 @@ function _entity_translation_process_path_schemes($entity_type, &$et_info) {
|
||||
$et_info['path schemes'][$delta] += array(
|
||||
'admin theme' => TRUE,
|
||||
'path wildcard' => "%$entity_type",
|
||||
'edit tabs' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -184,6 +194,9 @@ function entity_translation_entity_info_alter(&$entity_info) {
|
||||
// not. As a matter of fact we might need them to correctly switch field
|
||||
// translatability when a field is shared across different entity types.
|
||||
$et_info += array('class' => 'EntityTranslationDefaultHandler');
|
||||
if (!isset($entity_info[$entity_type]['entity keys'])) {
|
||||
$entity_info[$entity_type]['entity keys'] = array();
|
||||
}
|
||||
$entity_info[$entity_type]['entity keys'] += array('translations' => 'translations');
|
||||
|
||||
if (entity_translation_enabled($entity_type, NULL, TRUE)) {
|
||||
@@ -231,6 +244,13 @@ function entity_translation_menu() {
|
||||
'file' => 'entity_translation.admin.inc',
|
||||
);
|
||||
|
||||
$items['entity_translation/taxonomy_term/autocomplete'] = array(
|
||||
'title' => 'Entity translation autocomplete',
|
||||
'page callback' => 'entity_translation_taxonomy_term_autocomplete',
|
||||
'access arguments' => array('access content'),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
@@ -286,7 +306,7 @@ function _entity_translation_validate_path_schemes(&$schemes, $entity_type_label
|
||||
if (empty($scheme['path wildcard'])) {
|
||||
if ($warnings) {
|
||||
$t_args = array('%scheme' => $delta, '%entity_type' => $entity_type_label);
|
||||
drupal_set_message(t('Entity Translation path scheme %scheme for entities of type %entity_type does not declare a path wildcard.', $t_args), 'warning');
|
||||
watchdog('entity_translation', 'Entity Translation path scheme %scheme for entities of type %entity_type does not declare a path wildcard.', $t_args);
|
||||
}
|
||||
unset($schemes[$delta]);
|
||||
continue;
|
||||
@@ -444,7 +464,11 @@ function entity_translation_menu_alter(&$items) {
|
||||
// Replace the main edit callback with our proxy implementation to set
|
||||
// form language to the current language and check access.
|
||||
$entity_position = array_search($scheme['path wildcard'], $edit_path_parts);
|
||||
$original_item = $edit_item;
|
||||
// Make sure incoming page and access arguments are arrays.
|
||||
$original_item = $edit_item + array(
|
||||
'page arguments' => array(),
|
||||
'access arguments' => array(),
|
||||
);
|
||||
$args = array($entity_type, $entity_position, FALSE, $original_item);
|
||||
$edit_item['page callback'] = 'entity_translation_edit_page';
|
||||
$edit_item['page arguments'] = array_merge($args, $original_item['page arguments']);
|
||||
@@ -452,21 +476,23 @@ function entity_translation_menu_alter(&$items) {
|
||||
$edit_item['access arguments'] = array_merge($args, $original_item['access arguments']);
|
||||
|
||||
// Edit translation callback.
|
||||
$translation_position = count($edit_path_parts);
|
||||
$args = array($entity_type, $entity_position, $translation_position, $original_item);
|
||||
$items["$edit_path/%entity_translation_language"] = array(
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'title callback' => 'entity_translation_edit_title',
|
||||
'title arguments' => array($translation_position),
|
||||
'page callback' => 'entity_translation_edit_page',
|
||||
'page arguments' => array_merge($args, $original_item['page arguments']),
|
||||
'access callback' => 'entity_translation_edit_access',
|
||||
'access arguments' => array_merge($args, $original_item['access arguments']),
|
||||
)
|
||||
// We need to inherit the remaining menu item keys, mostly 'module'
|
||||
// and 'file' to keep ajax callbacks working (see form_get_cache() and
|
||||
// drupal_retrieve_form()).
|
||||
+ $original_item;
|
||||
if ($scheme['edit tabs'] !== FALSE) {
|
||||
$translation_position = count($edit_path_parts);
|
||||
$args = array($entity_type, $entity_position, $translation_position, $original_item);
|
||||
$items["$edit_path/%entity_translation_language"] = array(
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'title callback' => 'entity_translation_edit_title',
|
||||
'title arguments' => array($translation_position),
|
||||
'page callback' => 'entity_translation_edit_page',
|
||||
'page arguments' => array_merge($args, $original_item['page arguments']),
|
||||
'access callback' => 'entity_translation_edit_access',
|
||||
'access arguments' => array_merge($args, $original_item['access arguments']),
|
||||
)
|
||||
// We need to inherit the remaining menu item keys, mostly 'module'
|
||||
// and 'file' to keep ajax callbacks working (see form_get_cache() and
|
||||
// drupal_retrieve_form()).
|
||||
+ $original_item;
|
||||
}
|
||||
|
||||
// Add translation callback.
|
||||
$add_path = "$edit_path/add/%entity_translation_language/%entity_translation_language";
|
||||
@@ -498,12 +524,13 @@ function entity_translation_menu_alter(&$items) {
|
||||
}
|
||||
|
||||
if ($translate_ui_attached == FALSE) {
|
||||
drupal_set_message(t('The entities of type %entity_type do not define a valid path scheme: it will not be possible to translate them.', array('%entity_type' => $info['label'])), 'warning');
|
||||
watchdog('entity_translation', 'The entities of type %entity_type do not define a valid path scheme: it will not be possible to translate them.', array('%entity_type' => $info['label']));
|
||||
}
|
||||
|
||||
// Node-specific menu alterations.
|
||||
if ($entity_type == 'node') {
|
||||
entity_translation_node_menu_alter($items, $backup);
|
||||
// Entity-type-specific menu alterations.
|
||||
$function = 'entity_translation_' . $entity_type . '_menu_alter';
|
||||
if (function_exists($function)) {
|
||||
$function($items, $backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -533,8 +560,8 @@ function entity_translation_edit_page() {
|
||||
// Set the current form language.
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler->initPathScheme();
|
||||
$langcode = entity_translation_form_language($langcode, $handler);
|
||||
$handler->setFormLanguage($langcode);
|
||||
$langcode = entity_translation_get_existing_language($entity_type, $entity, $langcode);
|
||||
$handler->setActiveLanguage($langcode);
|
||||
|
||||
// Display the entity edit form.
|
||||
return _entity_translation_callback($edit_form_item['page callback'], $args, $edit_form_item);
|
||||
@@ -548,10 +575,20 @@ function entity_translation_edit_access() {
|
||||
$entity_type = array_shift($args);
|
||||
$entity = array_shift($args);
|
||||
$langcode = array_shift($args);
|
||||
$edit_form_item = array_shift($args);
|
||||
$access_callback = isset($edit_form_item['access callback']) ? $edit_form_item['access callback'] : 'user_access';
|
||||
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
|
||||
// First, check a handler has been loaded. This could be empty if a
|
||||
// non-existent entity edit path has been requested, for example. Delegate
|
||||
// directly to the edit form item access callback in this case.
|
||||
if (empty($handler)) {
|
||||
return _entity_translation_callback($access_callback, $args, $edit_form_item);
|
||||
}
|
||||
|
||||
$translations = $handler->getTranslations();
|
||||
$langcode = entity_translation_form_language($langcode, $handler);
|
||||
$langcode = entity_translation_get_existing_language($entity_type, $entity, $langcode);
|
||||
|
||||
// The user must be explicitly allowed to access the original values if
|
||||
// workflow permissions are enabled.
|
||||
@@ -567,8 +604,7 @@ function entity_translation_edit_access() {
|
||||
// is language neutral we need to let editors access it.
|
||||
$enabled_languages = entity_translation_languages($entity_type, $entity);
|
||||
if (isset($enabled_languages[$langcode]) || $langcode == LANGUAGE_NONE) {
|
||||
$edit_form_item = array_shift($args);
|
||||
return _entity_translation_callback($edit_form_item['access callback'], $args, $edit_form_item);
|
||||
return _entity_translation_callback($access_callback, $args, $edit_form_item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,11 +614,6 @@ function entity_translation_edit_access() {
|
||||
/**
|
||||
* Determines the current form language.
|
||||
*
|
||||
* Based on the requested language and the translations available for the entity
|
||||
* being edited, determines the active form language. This takes into account
|
||||
* language fallback rules so that the translation being edited matches the one
|
||||
* being viewed.
|
||||
*
|
||||
* @param $langcode
|
||||
* The requested language code.
|
||||
* @param EntityTranslationHandlerInterface $handler
|
||||
@@ -590,8 +621,35 @@ function entity_translation_edit_access() {
|
||||
*
|
||||
* @return
|
||||
* A valid language code.
|
||||
*
|
||||
* @deprecated This is no longer used and will be removed in the first stable
|
||||
* release.
|
||||
*/
|
||||
function entity_translation_form_language($langcode, $handler) {
|
||||
return entity_translation_get_existing_language($handler->getEntity(), $handler->getEntityType(), $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines an existing translation language.
|
||||
*
|
||||
* Based on the requested language and the translations available for the given
|
||||
* entity, determines an existing translation language. This takes into account
|
||||
* language fallback rules.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of the entity.
|
||||
* @param $entity
|
||||
* The entity whose existing translation language has to be returned.
|
||||
* @param $langcode
|
||||
* (optional) The requested language code. Defaults to the current content
|
||||
* language.
|
||||
*
|
||||
* @return
|
||||
* A valid language code.
|
||||
*/
|
||||
function entity_translation_get_existing_language($entity_type, $entity, $langcode = NULL) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
|
||||
if (empty($langcode)) {
|
||||
$langcode = $GLOBALS['language_content']->language;
|
||||
}
|
||||
@@ -625,7 +683,8 @@ function entity_translation_add_access() {
|
||||
$enabled_languages = entity_translation_languages($entity_type, $entity);
|
||||
if (isset($enabled_languages[$langcode])) {
|
||||
$edit_form_item = array_shift($args);
|
||||
return _entity_translation_callback($edit_form_item['access callback'], $args, $edit_form_item);
|
||||
$access_callback = isset($edit_form_item['access callback']) ? $edit_form_item['access callback'] : 'user_access';
|
||||
return _entity_translation_callback($access_callback, $args, $edit_form_item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +704,7 @@ function entity_translation_add_page() {
|
||||
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler->initPathScheme();
|
||||
$handler->setFormLanguage($langcode);
|
||||
$handler->setActiveLanguage($langcode);
|
||||
$handler->setSourceLanguage($source);
|
||||
|
||||
// Display the entity edit form.
|
||||
@@ -669,7 +728,7 @@ function _entity_translation_callback($callback, $args, $info = array()) {
|
||||
function entity_translation_admin_paths() {
|
||||
$paths = array();
|
||||
foreach (entity_get_info() as $entity_type => $info) {
|
||||
if (entity_translation_enabled($entity_type, NULL, TRUE)) {
|
||||
if (isset($info['translation']['entity_translation']['path schemes']) && entity_translation_enabled($entity_type, NULL, TRUE)) {
|
||||
foreach ($info['translation']['entity_translation']['path schemes'] as $scheme) {
|
||||
if (!empty($scheme['admin theme'])) {
|
||||
if (isset($scheme['translate path'])) {
|
||||
@@ -693,8 +752,12 @@ function entity_translation_admin_paths() {
|
||||
*/
|
||||
function entity_translation_tab_access($entity_type, $entity) {
|
||||
if (drupal_multilingual() && (user_access('translate any entity') || user_access("translate $entity_type entities"))) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
// Ensure $entity holds an entity object and not an id.
|
||||
$entity = $handler->getEntity();
|
||||
|
||||
$enabled = entity_translation_enabled($entity_type, $entity);
|
||||
return $enabled && entity_translation_get_handler($entity_type, $entity)->getLanguage() != LANGUAGE_NONE;
|
||||
return $enabled && $handler->getLanguage() != LANGUAGE_NONE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -866,7 +929,7 @@ function entity_translation_field_language_alter(&$display_language, $context) {
|
||||
if (isset($translations->data[$context['language']]) && !entity_translation_access($entity_type, $translations->data[$context['language']])) {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$instances = field_info_instances($entity_type, $bundle);
|
||||
$entity = clone($entity);
|
||||
$entity = clone $entity;
|
||||
|
||||
foreach ($translations->data as $langcode => $translation) {
|
||||
if ($langcode == $context['language'] || !entity_translation_access($entity_type, $translations->data[$langcode])) {
|
||||
@@ -1058,6 +1121,10 @@ function entity_translation_sync($entity_type, $entity) {
|
||||
|
||||
// If an item has been removed we do not store its translations.
|
||||
if ($removed) {
|
||||
// Ensure items are actually removed.
|
||||
if (!isset($entity->{$field_name}[$langcode])) {
|
||||
$entity->{$field_name}[$langcode] = array();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// If a synchronized column has changed we need to override the full
|
||||
@@ -1118,14 +1185,27 @@ function entity_translation_field_attach_delete($entity_type, $entity) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_delete_revision().
|
||||
*/
|
||||
function entity_translation_field_attach_delete_revision($entity_type, $entity) {
|
||||
if (entity_translation_enabled($entity_type, $entity)) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler->removeRevisionTranslations();
|
||||
$handler->saveTranslations();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_field_attach_form().
|
||||
*/
|
||||
function entity_translation_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
|
||||
// Skip recursing into the source form.
|
||||
if (empty($form['#entity_translation_source_form']) && ($handler = entity_translation_entity_form_get_handler($form, $form_state))) {
|
||||
// Avoid recursing into the source form.
|
||||
list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
if (empty($form['#entity_translation_source_form']) && entity_translation_enabled($entity_type, $bundle)) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$langcode = !empty($langcode) ? $langcode : $handler->getLanguage();
|
||||
$form_langcode = $handler->getFormLanguage();
|
||||
$form_langcode = $handler->getActiveLanguage();
|
||||
$translations = $handler->getTranslations();
|
||||
$update_langcode = $form_langcode && ($form_langcode != $langcode);
|
||||
$source = $handler->getSourceLanguage();
|
||||
@@ -1140,8 +1220,6 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
|
||||
// with the correct form language and replace the field elements with the
|
||||
// correct ones.
|
||||
if ($update_langcode || ($source && !isset($translations->data[$form_langcode]) && isset($translations->data[$source]) && empty($form_state['rebuild']))) {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
foreach (field_info_instances($entity_type, $bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
@@ -1150,26 +1228,31 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
|
||||
// language information from source to target language, this way the
|
||||
// user can find the form items already populated with the source values
|
||||
// while the field form element holds the correct language information.
|
||||
if ($field['translatable']) {
|
||||
$form[$field_name]['#field_name'] = $field_name;
|
||||
$form[$field_name]['#source'] = $update_langcode ? $form_langcode : $source;
|
||||
$form[$field_name]['#previous'] = NULL;
|
||||
if ($field['translatable'] && isset($form[$field_name])) {
|
||||
$element = &$form[$field_name];
|
||||
$element['#entity_type'] = $entity_type;
|
||||
$element['#entity'] = $entity;
|
||||
$element['#entity_id'] = $id;
|
||||
$element['#field_name'] = $field_name;
|
||||
$element['#source'] = $update_langcode ? $form_langcode : $source;
|
||||
$element['#previous'] = NULL;
|
||||
$element['#form_parents'] = $form['#parents'];
|
||||
|
||||
// If we are updating the form language we need to make sure that the
|
||||
// wrong language is unset and the right one is stored in the field
|
||||
// element (see entity_translation_prepare_element()).
|
||||
if ($update_langcode) {
|
||||
$form[$field_name]['#previous'] = $form[$field_name]['#language'];
|
||||
$form[$field_name]['#language'] = $form_langcode;
|
||||
$element['#previous'] = $element['#language'];
|
||||
$element['#language'] = $form_langcode;
|
||||
}
|
||||
|
||||
// Swap default values during form processing to avoid recursion. We
|
||||
// try to act before any other callback so that the correct values are
|
||||
// already in place for them.
|
||||
if (!isset($form[$field_name]['#process'])) {
|
||||
$form[$field_name]['#process'] = array();
|
||||
if (!isset($element['#process'])) {
|
||||
$element['#process'] = array();
|
||||
}
|
||||
array_unshift($form[$field_name]['#process'], 'entity_translation_prepare_element');
|
||||
array_unshift($element['#process'], 'entity_translation_prepare_element');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1181,20 +1264,23 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
foreach (field_info_instances($entity_type, $bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
// If access is not set or is granted we check whether the user has
|
||||
// access to shared fields.
|
||||
$form[$field_name]['#access'] = (!isset($form[$field_name]['#access']) || $form[$field_name]['#access']) && ($field['translatable'] || $shared_access);
|
||||
$form[$field_name]['#multilingual'] = (boolean) $field['translatable'];
|
||||
// Check if a field is part of the form array.
|
||||
if (isset($form[$field_name])) {
|
||||
$field = field_info_field($field_name);
|
||||
// If access is not set or is granted we check whether the user has
|
||||
// access to shared fields.
|
||||
$form[$field_name]['#access'] = (!isset($form[$field_name]['#access']) || $form[$field_name]['#access']) && ($field['translatable'] || $shared_access);
|
||||
$form[$field_name]['#multilingual'] = (boolean) $field['translatable'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a translation is being created and no path alias exists for its
|
||||
// language, by default an alias needs to be generated. The standard
|
||||
// behavior is defaulting to FALSE when an entity already exists, hence we
|
||||
// need to override it here.
|
||||
// If a translation is being created, an alias may be generated if needed.
|
||||
// The standard behavior is defaulting to FALSE when an entity already
|
||||
// exists, hence we need to override it here.
|
||||
// If needed, a variable is provided for reverting to the default behavior.
|
||||
if (module_exists('pathauto') && $handler->getSourceLanguage()) {
|
||||
$entity->path['pathauto'] = TRUE;
|
||||
$entity->path['pathauto'] = variable_get('entity_translation_pathauto_state_active_new_translation', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1203,18 +1289,37 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
|
||||
* Form element process callback.
|
||||
*/
|
||||
function entity_translation_prepare_element($element, &$form_state) {
|
||||
$source_form = &drupal_static(__FUNCTION__, array());
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast = &drupal_static(__FUNCTION__, array(
|
||||
'source_forms' => array(),
|
||||
'source_form_states' => array(),
|
||||
));
|
||||
}
|
||||
|
||||
$source_forms = &$drupal_static_fast['source_forms'];
|
||||
$source_form_states = &$drupal_static_fast['source_form_states'];
|
||||
$form = $form_state['complete form'];
|
||||
$build_id = $form['#build_id'];
|
||||
$source = $element['#source'];
|
||||
$entity_type = $element['#entity_type'];
|
||||
$id = $element['#entity_id'];
|
||||
|
||||
if (!isset($source_form[$build_id][$source])) {
|
||||
$source_form[$build_id][$source] = array('#entity_translation_source_form' => TRUE);
|
||||
// Key the source form cache per entity type and entity id to allow for
|
||||
// multiple entities on the same entity form.
|
||||
if (!isset($source_forms[$build_id][$source][$entity_type][$id])) {
|
||||
$source_form = array(
|
||||
'#entity_translation_source_form' => TRUE,
|
||||
'#parents' => $element['#form_parents'],
|
||||
);
|
||||
$source_form_state = $form_state;
|
||||
$info = entity_translation_edit_form_info($form, $form_state);
|
||||
field_attach_form($info['entity type'], $info['entity'], $source_form[$build_id][$source], $source_form_state, $source);
|
||||
field_attach_form($entity_type, $element['#entity'], $source_form, $source_form_state, $source);
|
||||
$source_forms[$build_id][$source][$entity_type][$id] = &$source_form;
|
||||
$source_form_states[$build_id][$source][$entity_type][$id] = &$source_form_state;
|
||||
}
|
||||
|
||||
$source_form = &$source_forms[$build_id][$source][$entity_type][$id];
|
||||
$source_form_state = $source_form_states[$build_id][$source][$entity_type][$id];
|
||||
$langcode = $element['#language'];
|
||||
$field_name = $element['#field_name'];
|
||||
|
||||
@@ -1222,15 +1327,45 @@ function entity_translation_prepare_element($element, &$form_state) {
|
||||
// language information from source to target language, this way the user can
|
||||
// find the form items already populated with the source values while the
|
||||
// field form element holds the correct language information.
|
||||
if (isset($source_form[$build_id][$source][$field_name][$source])) {
|
||||
$element[$langcode] = $source_form[$build_id][$source][$field_name][$source];
|
||||
if (isset($source_form[$field_name][$source])) {
|
||||
$element[$langcode] = $source_form[$field_name][$source];
|
||||
entity_translation_form_element_language_replace($element, $source, $langcode);
|
||||
entity_translation_form_element_state_replace($element, $source_form[$field_name], $field_name, $source_form_state, $form_state);
|
||||
unset($element[$element['#previous']]);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Sets the right values in $form_state['field'] when using
|
||||
* source language values as defaults.
|
||||
*/
|
||||
function entity_translation_form_element_state_replace($element, $source_element, $field_name, $source_form_state, &$form_state) {
|
||||
if (isset($source_element['#language'])) {
|
||||
$source = $source_element['#language'];
|
||||
|
||||
// Iterate through the form structure recursively.
|
||||
foreach (element_children($element) as $key) {
|
||||
if (isset($source_element[$key])) {
|
||||
entity_translation_form_element_state_replace($element[$key], $source_element[$key], $key, $source_form_state, $form_state);
|
||||
}
|
||||
elseif (isset($source_element[$source])) {
|
||||
entity_translation_form_element_state_replace($element[$key], $source_element[$source], $key, $source_form_state, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($source_element[$source]['#field_parents'])) {
|
||||
$source_parents = $source_element[$source]['#field_parents'];
|
||||
$langcode = $element['#language'];
|
||||
$parents = $element[$langcode]['#field_parents'];
|
||||
$source_state = field_form_get_state($source_parents, $field_name, $source, $source_form_state);
|
||||
drupal_alter('entity_translation_source_field_state', $source_state);
|
||||
field_form_set_state($parents, $field_name, $langcode, $form_state, $source_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Recursively replaces the source language with the given one.
|
||||
*/
|
||||
@@ -1243,7 +1378,7 @@ function entity_translation_form_element_language_replace(&$element, $source, $l
|
||||
// Replace specific occurrences of the source language with the target
|
||||
// language.
|
||||
foreach (element_properties($element) as $key) {
|
||||
if ($key === '#language') {
|
||||
if ($key === '#language' && $element[$key] != LANGUAGE_NONE) {
|
||||
$element[$key] = $langcode;
|
||||
}
|
||||
elseif ($key === '#parents' || $key === '#field_parents') {
|
||||
@@ -1343,6 +1478,10 @@ function _entity_translation_element_title_append(&$element, $suffix) {
|
||||
if (isset($element['#type']) && isset($fapi_title_elements[$element['#type']]) && isset($element['#title'])) {
|
||||
$element['#title'] .= $suffix;
|
||||
}
|
||||
// If this is a multi-valued field, apply the suffix to the container.
|
||||
elseif (isset($element['#title']) && isset($element['#cardinality']) && $element['#cardinality'] != 1) {
|
||||
$element['#title'] .= $suffix;
|
||||
}
|
||||
// If the current element does not have a (valid) title, try child elements.
|
||||
elseif ($children = element_children($element)) {
|
||||
foreach ($children as $delta) {
|
||||
@@ -1360,30 +1499,32 @@ function _entity_translation_element_title_append(&$element, $suffix) {
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function entity_translation_form_alter(&$form, &$form_state) {
|
||||
if ($handler = entity_translation_entity_form_get_handler($form, $form_state)) {
|
||||
if (!$handler->isNewEntity()) {
|
||||
$handler->entityForm($form, $form_state);
|
||||
$translations = $handler->getTranslations();
|
||||
$form_langcode = $handler->getFormLanguage();
|
||||
if (!isset($translations->data[$form_langcode]) || count($translations->data) > 1) {
|
||||
// Hide shared form elements if the user is not allowed to edit them.
|
||||
$handler->entityFormSharedElements($form);
|
||||
if ($info = entity_translation_edit_form_info($form, $form_state)) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
if (entity_translation_enabled($info['entity type'], $info['entity'])) {
|
||||
if (!$handler->isNewEntity()) {
|
||||
$handler->entityForm($form, $form_state);
|
||||
$translations = $handler->getTranslations();
|
||||
$form_langcode = $handler->getActiveLanguage();
|
||||
if (!isset($translations->data[$form_langcode]) || count($translations->data) > 1) {
|
||||
// Hide shared form elements if the user is not allowed to edit them.
|
||||
$handler->entityFormSharedElements($form);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$handler->entityFormLanguageWidget($form, $form_state);
|
||||
}
|
||||
// We need to process the posted form as early as possible to update the
|
||||
// form language value.
|
||||
array_unshift($form['#validate'], 'entity_translation_entity_form_validate');
|
||||
}
|
||||
// We might have an entity form for an entity or a bundle not enabled for
|
||||
// translation. In this case we might need to deal with entity and field
|
||||
// languages anyway, since fields may be shared among different bundles and
|
||||
// entity types.
|
||||
else {
|
||||
$handler->entityFormLanguageWidget($form, $form_state);
|
||||
}
|
||||
// We need to process the posted form as early as possible to update the
|
||||
// form language value.
|
||||
array_unshift($form['#validate'], 'entity_translation_entity_form_validate');
|
||||
}
|
||||
// We might have an entity form for an entity or a bundle not enabled for
|
||||
// translation. In this case we might need to deal with entity and field
|
||||
// languages anyway, since fields may be shared among different bundles and
|
||||
// entity types.
|
||||
elseif ($info = entity_translation_edit_form_info($form, $form_state)) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
$handler->entityFormLanguageWidget($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1393,7 +1534,7 @@ function entity_translation_form_alter(&$form, &$form_state) {
|
||||
function entity_translation_entity_form_source_language_submit($form, &$form_state) {
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
$langcode = $form_state['values']['source_language']['language'];
|
||||
$path = "{$handler->getEditPath()}/add/$langcode/{$handler->getFormLanguage()}";
|
||||
$path = "{$handler->getEditPath()}/add/$langcode/{$handler->getActiveLanguage()}";
|
||||
$options = array();
|
||||
if (isset($_GET['destination'])) {
|
||||
$options['query'] = drupal_get_destination();
|
||||
@@ -1409,7 +1550,7 @@ function entity_translation_entity_form_source_language_submit($form, &$form_sta
|
||||
*/
|
||||
function entity_translation_entity_form_delete_translation_submit($form, &$form_state) {
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
$path = "{$handler->getTranslatePath()}/delete/{$handler->getFormLanguage()}";
|
||||
$path = "{$handler->getTranslatePath()}/delete/{$handler->getActiveLanguage()}";
|
||||
$options = array();
|
||||
if (isset($_GET['destination'])) {
|
||||
$options['query'] = drupal_get_destination();
|
||||
@@ -1429,16 +1570,22 @@ function entity_translation_entity_form_validate($form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the entity language widget.
|
||||
* Validation handler for the entity language widget.
|
||||
*/
|
||||
function entity_translation_language_widget_submit($form, &$form_state) {
|
||||
function entity_translation_entity_form_language_update($element, &$form_state, $form) {
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
// On non-translatable entities, we need to handle just the entity and field
|
||||
// language.
|
||||
if (empty($handler) && ($info = entity_translation_edit_form_info($form, $form_state))) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
// Ensure the handler form language match the actual one. This is mainly
|
||||
// needed when responding to an AJAX request where the languages cannot be set
|
||||
// from the usual page callback.
|
||||
if (!empty($form_state['entity_translation']['form_langcode'])) {
|
||||
$handler->setActiveLanguage($form_state['entity_translation']['form_langcode']);
|
||||
}
|
||||
// When responding to an AJAX request we should ignore any change in the
|
||||
// language widget as it may alter the field language expected by the AJAX
|
||||
// callback.
|
||||
if (empty($form_state['triggering_element']['#ajax'])) {
|
||||
$handler->entityFormLanguageWidgetSubmit($form, $form_state);
|
||||
}
|
||||
$handler->entityFormLanguageWidgetSubmit($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1460,10 +1607,13 @@ function entity_translation_entity_form_submit($form, &$form_state) {
|
||||
* Mark translations as outdated if the submitted value is true.
|
||||
*/
|
||||
function entity_translation_field_attach_submit($entity_type, $entity, $form, &$form_state) {
|
||||
if ($handler = entity_translation_entity_form_get_handler($form, $form_state)) {
|
||||
if (($handler = entity_translation_entity_form_get_handler($form, $form_state)) && entity_translation_enabled($entity_type, $entity)) {
|
||||
// Update the wrapped entity with the submitted values.
|
||||
$handler->setEntity($entity);
|
||||
$handler->entityFormSubmit($form, $form_state);
|
||||
|
||||
// Process in-place translations for the taxonomy autocomplete widget.
|
||||
entity_translation_taxonomy_term_field_attach_submit($entity_type, $entity, $form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1538,7 +1688,7 @@ function theme_entity_translation_language_tabs($variables) {
|
||||
* Adds an option to enable field synchronization.
|
||||
* Enable a selector to choose whether a field is translatable.
|
||||
*/
|
||||
function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_state) {
|
||||
function entity_translation_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
|
||||
$instance = $form['#instance'];
|
||||
$entity_type = $instance['entity_type'];
|
||||
$field_name = $instance['field_name'];
|
||||
@@ -1558,7 +1708,8 @@ function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_st
|
||||
$label = t('Field translation');
|
||||
$title = t('Users may translate all occurrences of this field:') . _entity_translation_field_desc($field);
|
||||
|
||||
if (field_has_data($field)) {
|
||||
$form_state['field_has_data'] = field_has_data($field);
|
||||
if ($form_state['field_has_data']) {
|
||||
$path = "admin/config/regional/entity_translation/translatable/$field_name";
|
||||
$status = $translatable ? $title : (t('All occurrences of this field are untranslatable:') . _entity_translation_field_desc($field));
|
||||
$link_title = !$translatable ? t('Enable translation') : t('Disable translation');
|
||||
@@ -1586,6 +1737,11 @@ function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_st
|
||||
'#default_value' => $translatable,
|
||||
);
|
||||
}
|
||||
|
||||
$function = 'entity_translation_form_field_ui_field_edit_' . $instance['widget']['type'] . '_form_alter';
|
||||
if (function_exists($function)) {
|
||||
$function($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1646,6 +1802,9 @@ function entity_translation_enabled($entity_type, $entity = NULL, $skip_handler
|
||||
/**
|
||||
* Determines whether the given entity bundle is translatable.
|
||||
*
|
||||
* NOTE: Does not check for whether the entity type is translatable.
|
||||
* Consider using entity_translation_enabled() instead.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type the bundle to be checked belongs to.
|
||||
* @param $bundle
|
||||
@@ -1685,54 +1844,37 @@ function entity_translation_settings($entity_type, $bundle) {
|
||||
* Entity language callback.
|
||||
*
|
||||
* This callback changes the entity language from the actual one to the active
|
||||
* form language. This overriding allows to obtain language dependent form
|
||||
* widgets where multilingual values are supported (e.g. field or path alias
|
||||
* widgets) even if the code was not originally written with supporting multiple
|
||||
* values per language in mind.
|
||||
* language. This overriding allows to obtain language dependent form widgets
|
||||
* where multilingual values are supported (e.g. field or path alias widgets)
|
||||
* even if the code was not originally written with supporting multiple values
|
||||
* per language in mind.
|
||||
*
|
||||
* The main drawback of this approach is that code needing to access the actual
|
||||
* language in the entity form build/validation/submit workflow cannot rely on
|
||||
* the entity_language() function. On the other hand in these scenarios assuming
|
||||
* the presence of Entity translation should be safe, thus being able to rely on
|
||||
* the EntityTranslationHandlerInterface::getLanguage() method.
|
||||
* the presence of Entity translation should be safe, thus developers can rely
|
||||
* on the EntityTranslationHandlerInterface::getLanguage() method.
|
||||
*
|
||||
* @param $entity_type
|
||||
* @param string $entity_type
|
||||
* The the type of the entity.
|
||||
* @param $entity
|
||||
* @param object $entity
|
||||
* The entity whose language has to be returned.
|
||||
*
|
||||
* @return
|
||||
* @return string
|
||||
* A valid language code.
|
||||
*/
|
||||
function entity_translation_language($entity_type, $entity) {
|
||||
// If a form has been post, we need to check its state to verify if any form
|
||||
// translation handler is stored there. This is mainly needed when responding
|
||||
// to an AJAX request where the form language cannot be set from the page
|
||||
// callback.
|
||||
$handler = entity_translation_current_form_get_handler();
|
||||
|
||||
// Make sure we always have a translation handler instance available.
|
||||
if (empty($handler)) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
if (!$handler) {
|
||||
return LANGUAGE_NONE;
|
||||
}
|
||||
// If a translation handler associated to the current form is found, we need
|
||||
// to update the wrapped entity. This way submitted values will be picked up.
|
||||
// Other entities may be created or saved while submitting the current one,
|
||||
// hence we need to check we are dealing with it.
|
||||
elseif ($handler->isWrappedEntity($entity_type, $entity)) {
|
||||
$langcode = $handler->getLanguage();
|
||||
$handler->setEntity($entity);
|
||||
$submitted_langcode = $handler->getLanguage();
|
||||
// If the entity language has changed we are editing the original values. In
|
||||
// this case we need to update the current form language with the submitted
|
||||
// one.
|
||||
if ($submitted_langcode != $langcode) {
|
||||
$handler->setFormLanguage($submitted_langcode);
|
||||
}
|
||||
if (entity_translation_enabled($entity_type, $entity)) {
|
||||
$langcode = $handler->getActiveLanguage();
|
||||
return $langcode ? $langcode : $handler->getLanguage();
|
||||
}
|
||||
else {
|
||||
return $handler->getLanguage();
|
||||
}
|
||||
|
||||
$langcode = $handler->getFormLanguage();
|
||||
return !empty($langcode) ? $langcode : $handler->getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1743,61 +1885,13 @@ function entity_translation_language($entity_type, $entity) {
|
||||
* @param $entity
|
||||
* (optional) The entity to be translated. A bundle name may be passed to
|
||||
* instantiate an empty entity.
|
||||
* @param $update
|
||||
* (optional) Instances are statically cached: if this is TRUE the wrapped
|
||||
* entity will be replaced by the passed one.
|
||||
*
|
||||
* @return EntityTranslationHandlerInterface
|
||||
* A class implementing EntityTranslationHandlerInterface.
|
||||
*/
|
||||
function entity_translation_get_handler($entity_type = NULL, $entity = NULL, $update = FALSE) {
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast['handlers'])) {
|
||||
$drupal_static_fast['handlers'] = &drupal_static(__FUNCTION__, array());
|
||||
}
|
||||
$handlers = &$drupal_static_fast['handlers'];
|
||||
|
||||
// Workaround the lack of a context object.
|
||||
if (empty($entity)) {
|
||||
if (isset($handlers[$entity_type]['#current'])) {
|
||||
return $handlers[$entity_type]['#current'];
|
||||
}
|
||||
elseif (empty($entity_type) && isset($handlers['#current']['#current'])) {
|
||||
return $handlers['#current']['#current'];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
elseif (is_string($entity)) {
|
||||
$entity = entity_create_stub_entity($entity_type, array(NULL, NULL, $entity));
|
||||
}
|
||||
|
||||
list($entity_id) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
if (!isset($handlers[$entity_type][$entity_id])) {
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$class = $entity_info['translation']['entity_translation']['class'];
|
||||
// @todo remove fourth parameter once 3rd-party translation handlers have
|
||||
// been fixed and no longer require the deprecated entity_id parameter.
|
||||
$handler = new $class($entity_type, $entity_info, $entity, NULL);
|
||||
|
||||
// If the entity id is empty we cannot cache the translation handler
|
||||
// instance.
|
||||
if (empty($entity_id)) {
|
||||
return $handler;
|
||||
}
|
||||
else {
|
||||
$handlers[$entity_type][$entity_id] = $handler;
|
||||
}
|
||||
}
|
||||
elseif ($update) {
|
||||
$handlers[$entity_type][$entity_id]->setEntity($entity);
|
||||
}
|
||||
|
||||
$handlers[$entity_type]['#current'] = $handlers[$entity_type][$entity_id];
|
||||
$handlers['#current']['#current'] = $handlers[$entity_type][$entity_id];
|
||||
return $handlers[$entity_type][$entity_id];
|
||||
function entity_translation_get_handler($entity_type = NULL, $entity = NULL) {
|
||||
$factory = EntityTranslationHandlerFactory::getInstance();
|
||||
return empty($entity) ? $factory->getLastHandler($entity_type) : $factory->getHandler($entity_type, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1811,24 +1905,11 @@ function entity_translation_get_handler($entity_type = NULL, $entity = NULL, $up
|
||||
* @return EntityTranslationHandlerInterface
|
||||
* A class implementing EntityTranslationHandlerInterface.
|
||||
*/
|
||||
function entity_translation_entity_form_get_handler($form, &$form_state) {
|
||||
function entity_translation_entity_form_get_handler($form, $form_state) {
|
||||
$handler = FALSE;
|
||||
$entity_type = isset($form['#entity_type']) && is_string($form['#entity_type']) ? $form['#entity_type'] : FALSE;
|
||||
|
||||
if ($entity_type) {
|
||||
if (empty($form_state['storage']['entity_translation']['handler'][$entity_type])) {
|
||||
if ($info = entity_translation_edit_form_info($form, $form_state)) {
|
||||
if (entity_translation_enabled($info['entity type'], $info['entity'])) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
$form_state['storage']['entity_translation']['handler'][$info['entity type']] = $handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$handler = $form_state['storage']['entity_translation']['handler'][$entity_type];
|
||||
}
|
||||
if ($info = entity_translation_edit_form_info($form, $form_state)) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
@@ -1837,21 +1918,25 @@ function entity_translation_entity_form_get_handler($form, &$form_state) {
|
||||
*
|
||||
* @return EntityTranslationHandlerInterface
|
||||
* A translation handler instance if available, FALSE oterwise.
|
||||
*
|
||||
* @deprecated This is no longer used and will be removed in the first stable
|
||||
* release.
|
||||
*/
|
||||
function entity_translation_current_form_get_handler() {
|
||||
$handler = FALSE;
|
||||
|
||||
if (!empty($_POST['form_build_id'])) {
|
||||
$form_state = form_state_defaults();
|
||||
$form = form_get_cache($_POST['form_build_id'], $form_state);
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
if ($form = form_get_cache($_POST['form_build_id'], $form_state)) {
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of edit form info as defined in hook_translation_info().
|
||||
* Returns an array of edit form info as defined in hook_entity_info().
|
||||
*
|
||||
* @param $form
|
||||
* The entity edit form.
|
||||
@@ -1869,11 +1954,13 @@ function entity_translation_edit_form_info($form, $form_state) {
|
||||
if ($entity_type) {
|
||||
$entity_info = entity_get_info($form['#entity_type']);
|
||||
if (!empty($entity_info['translation']['entity_translation']['edit form'])) {
|
||||
$entity_key = $entity_info['translation']['entity_translation']['edit form'];
|
||||
if (isset($form_state[$entity_key])) {
|
||||
$entity_keys = explode('][', $entity_info['translation']['entity_translation']['edit form']);
|
||||
$key_exists = FALSE;
|
||||
$entity = drupal_array_get_nested_value($form_state, $entity_keys, $key_exists);
|
||||
if ($key_exists) {
|
||||
$info = array(
|
||||
'entity type' => $form['#entity_type'],
|
||||
'entity' => (object) $form_state[$entity_key],
|
||||
'entity' => (object) $entity,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1961,31 +2048,50 @@ function entity_translation_entity_uuid_presave(&$entity, $entity_type) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_pathauto_alias_alter().
|
||||
* Implements hook_pathauto_alias_alter().
|
||||
*
|
||||
* When bulk-updating aliases for nodes automatically create a path for every
|
||||
* translation.
|
||||
* When updating or bulk-updating aliases for nodes, automatically create a path
|
||||
* for every translation, dependent on related configuration.
|
||||
* @see entity_translation_admin_form()
|
||||
*/
|
||||
function entity_translation_pathauto_alias_alter(&$alias, array &$context) {
|
||||
$info = entity_get_info();
|
||||
$entity_type = $context['module'];
|
||||
$operations_modes = array(
|
||||
'bulkupdate' => variable_get('entity_translation_pathauto_state_mode_bulkupdate', 'generate_all_aliases_all_languages'),
|
||||
'update' => variable_get('entity_translation_pathauto_state_mode_update', 'generate_alias_active_language'),
|
||||
);
|
||||
$operation = $context['op'];
|
||||
|
||||
// Ensure that we are dealing with a bundle having entity translation enabled.
|
||||
if ($context['op'] == 'bulkupdate' && !empty($info[$entity_type]['token type']) && !empty($context['data'][$info[$entity_type]['token type']])) {
|
||||
$entity = $context['data'][$info[$entity_type]['token type']];
|
||||
if (entity_translation_enabled($entity_type, $entity)) {
|
||||
$translations = entity_translation_get_handler($entity_type, $entity)->getTranslations();
|
||||
// Only create extra aliases if we are working on the original language to
|
||||
// avoid infinite recursion.
|
||||
if ($context['language'] == $translations->original) {
|
||||
foreach ($translations->data as $language => $translation) {
|
||||
// We already have an alias for the original language, so let's not
|
||||
// create another one.
|
||||
if ($language == $translations->original) {
|
||||
continue;
|
||||
}
|
||||
pathauto_create_alias($entity_type, $context['op'], $context['source'], $context['data'], $context['type'], $language);
|
||||
}
|
||||
// Nothing to do when we don't have a replacement pattern or incoming data.
|
||||
if (!in_array($operation, array_keys($operations_modes)) || empty($info[$entity_type]['token type']) || empty($context['data'][$info[$entity_type]['token type']])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not run additional 'update' or 'bulkupdate' operations if the update
|
||||
// mode is set to generate an alias only for the active language.
|
||||
if (in_array($operation, array_keys($operations_modes)) &&
|
||||
$operations_modes[$operation] === 'generate_alias_active_language') {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity = $context['data'][$info[$entity_type]['token type']];
|
||||
// Ensure that we are dealing with an entity bundle having entity translation
|
||||
// enabled.
|
||||
if (entity_translation_enabled($entity_type, $entity)) {
|
||||
$translations = entity_translation_get_handler($entity_type, $entity)->getTranslations();
|
||||
foreach ($translations->data as $language => $translation) {
|
||||
// We already have an alias for the starting language, so let's not create
|
||||
// another one.
|
||||
if ($language === $context['language']) {
|
||||
continue;
|
||||
}
|
||||
$no_existing_alias = (FALSE === _pathauto_existing_alias_data($context['source'], $language));
|
||||
if ($operations_modes[$operation] === 'generate_all_aliases_all_languages' ||
|
||||
($no_existing_alias && $operations_modes[$operation] === 'generate_missing_aliases_all_languages')) {
|
||||
// Modify the context['op'] by appending '_translation', to avoid infinite
|
||||
// recursion.
|
||||
pathauto_create_alias($entity_type, $operation . '_translation', $context['source'], $context['data'], $context['type'], $language);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1999,3 +2105,31 @@ function path_entity_translation_delete($entity_type, $entity, $langcode) {
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
path_delete(array('source' => $handler->getViewPath(), 'language' => $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for entity_save().
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $entity
|
||||
* The entity object.
|
||||
*/
|
||||
function entity_translation_entity_save($entity_type, $entity) {
|
||||
// Entity module isn't required, but use it if it's available.
|
||||
if (module_exists('entity')) {
|
||||
entity_save($entity_type, $entity);
|
||||
}
|
||||
// Fall back to field_attach_* functions otherwise.
|
||||
else {
|
||||
field_attach_presave($entity_type, $entity);
|
||||
field_attach_update($entity_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_prepare_translation_alter().
|
||||
*/
|
||||
function entity_translation_field_attach_prepare_translation_alter(&$entity, $context) {
|
||||
$handler = entity_translation_get_handler('node', $entity);
|
||||
$handler->setActiveLanguage($context['langcode']);
|
||||
}
|
||||
|
||||
+24
-2
@@ -43,14 +43,15 @@ function entity_translation_node_menu_alter(&$items, $backup) {
|
||||
$callback['file'] = $item['file'];
|
||||
$callback['module'] = $item['module'];
|
||||
$access_arguments = array_merge(array(1, $item['access callback']), $item['access arguments']);
|
||||
$page_arguments = array_merge(array('node', 1, $callback), $item['page arguments']);
|
||||
}
|
||||
else {
|
||||
$callback = FALSE;
|
||||
$access_arguments = array(1);
|
||||
$page_arguments = array('node', 1);
|
||||
}
|
||||
|
||||
$items['node/%node/translate']['page callback'] = 'entity_translation_overview';
|
||||
$items['node/%node/translate']['page arguments'] = array('node', 1, $callback);
|
||||
$items['node/%node/translate']['page arguments'] = $page_arguments;
|
||||
$items['node/%node/translate']['access arguments'] = $access_arguments;
|
||||
$items['node/%node/translate']['access callback'] = 'entity_translation_node_tab_access';
|
||||
$items['node/%node/translate']['file'] = 'entity_translation.admin.inc';
|
||||
@@ -249,3 +250,24 @@ function entity_translation_query_comment_filter_alter(QueryAlterableInterface $
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update_index().
|
||||
*
|
||||
* Add translated node content to search index.
|
||||
*/
|
||||
function entity_translation_node_update_index($node) {
|
||||
$text = '';
|
||||
$langcodes = array_keys(language_list());
|
||||
$translations = entity_translation_get_handler('node', $node)->getTranslations();
|
||||
foreach ($langcodes as $langcode) {
|
||||
// Skip the default language (already indexed by search.module).
|
||||
if ($GLOBALS[LANGUAGE_TYPE_CONTENT]->language != $langcode && isset($translations->data[$langcode])) {
|
||||
// Render the node in each language.
|
||||
node_build_content($node, 'search_index', $langcode);
|
||||
$rendered = drupal_render($node->content);
|
||||
$text .= '<h1>' . check_plain($node->title) . '</h1>' . $rendered;
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The taxonomy specific translation functions and hook implementations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns whether the given taxonomy vocabulary has support for translations.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if translation is enabled, FALSE otherwise.
|
||||
*/
|
||||
function entity_translation_taxonomy_term_enabled_vocabulary($vocabulary_name) {
|
||||
$info = variable_get('entity_translation_taxonomy', array());
|
||||
return !empty($info[$vocabulary_name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Taxonomy-term-specific menu alterations.
|
||||
*/
|
||||
function entity_translation_taxonomy_term_menu_alter(&$items, $backup) {
|
||||
if (isset($backup['taxonomy_term'])) {
|
||||
$item = $backup['taxonomy_term'];
|
||||
// Preserve the menu router item defined by other modules.
|
||||
$callback['page callback'] = $item['page callback'];
|
||||
$callback['file'] = $item['file'];
|
||||
$callback['module'] = $item['module'];
|
||||
$access_arguments = array_merge(array(2, $item['access callback']), $item['access arguments']);
|
||||
$page_arguments = array_merge(array('taxonomy_term', 2, $callback), $item['page arguments']);
|
||||
}
|
||||
else {
|
||||
$access_arguments = array(2);
|
||||
$page_arguments = array('taxonomy_term', 2);
|
||||
}
|
||||
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['page callback'] = 'entity_translation_overview';
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['page arguments'] = $page_arguments;
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['access arguments'] = $access_arguments;
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['access callback'] = 'entity_translation_taxonomy_term_tab_access';
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['file'] = 'entity_translation.admin.inc';
|
||||
$items['taxonomy/term/%taxonomy_term/translate']['module'] = 'entity_translation';
|
||||
|
||||
// Delete translation callback.
|
||||
$items['taxonomy/term/%taxonomy_term/translate/delete/%entity_translation_language']['access arguments'] = $access_arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taxonomy term specific access callback.
|
||||
*/
|
||||
function entity_translation_taxonomy_term_tab_access() {
|
||||
$args = func_get_args();
|
||||
$term = array_shift($args);
|
||||
if (entity_translation_enabled('taxonomy_term', $term)) {
|
||||
return entity_translation_tab_access('taxonomy_term', $term);
|
||||
}
|
||||
else {
|
||||
$function = array_shift($args);
|
||||
return $function ? call_user_func_array($function, $args) : FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter()
|
||||
*/
|
||||
function entity_translation_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
|
||||
if (entity_translation_enabled('taxonomy_term')) {
|
||||
$name = $form_state['vocabulary']->machine_name;
|
||||
if (isset($form['i18n_translation']['i18n_mode'])) {
|
||||
$args = array('!url' => url('admin/config/regional/entity_translation'));
|
||||
$form['i18n_translation']['i18n_mode']['#options'][I18N_MODE_ENTITY_TRANSLATION] = t('Field translation. Term fields will be translated through the <a href="!url">Entity translation</a> module.', $args);
|
||||
if (entity_translation_enabled_bundle('taxonomy_term', $name)) {
|
||||
$form['i18n_translation']['i18n_mode']['#default_value'] = I18N_MODE_ENTITY_TRANSLATION;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$form['entity_translation_taxonomy'] = array(
|
||||
'#title' => t('Enable field translation'),
|
||||
'#type' => 'checkbox',
|
||||
'#prefix' => '<label>' . t('Translation') . '</label>',
|
||||
'#default_value' => entity_translation_enabled('taxonomy_term', $name),
|
||||
);
|
||||
}
|
||||
$form['#submit'][] = 'entity_translation_form_taxonomy_form_vocabulary_submit';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the taxonomy vocabulary form.
|
||||
*/
|
||||
function entity_translation_form_taxonomy_form_vocabulary_submit($form, &$form_state) {
|
||||
if (!empty($form_state['values']['i18n_mode']) && $form_state['values']['i18n_mode'] == I18N_MODE_ENTITY_TRANSLATION) {
|
||||
$form_state['values']['entity_translation_taxonomy'] = TRUE;
|
||||
}
|
||||
$info = variable_get('entity_translation_taxonomy', array());
|
||||
$info[$form_state['vocabulary']->machine_name] = !empty($form_state['values']['entity_translation_taxonomy']);
|
||||
variable_set('entity_translation_taxonomy', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translated label for the specified taxonomy term.
|
||||
*
|
||||
* @param object $term
|
||||
* A taxonomy term object.
|
||||
* @param string $langcode
|
||||
* The language the label should be translated in.
|
||||
*
|
||||
* @return string
|
||||
* The translated taxonomy term label.
|
||||
*
|
||||
* @internal This is supposed to be used only by the ET taxonomy integration
|
||||
* code, as it might be removed/replaced in any moment of the ET lifecycle.
|
||||
*/
|
||||
function _entity_translation_taxonomy_label($term, $langcode) {
|
||||
$entity_type = 'taxonomy_term';
|
||||
if (function_exists('title_entity_label')) {
|
||||
$label = title_entity_label($term, $entity_type, $langcode);
|
||||
}
|
||||
else {
|
||||
$label = entity_label($entity_type, $term);
|
||||
}
|
||||
return (string) $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements entity_translation_form_field_ui_field_edit_WIDGET_TYPE_form_alter().
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function entity_translation_form_field_ui_field_edit_taxonomy_autocomplete_form_alter(&$form, &$form_state) {
|
||||
$key = 'entity_translation_taxonomy_autocomplete_translate';
|
||||
$instance = $form['#instance'];
|
||||
$field_name = $instance['field_name'];
|
||||
$entity_type = $instance['entity_type'];
|
||||
$field = field_info_field($field_name);
|
||||
$translatable = field_is_translatable($entity_type, $field);
|
||||
$bundle = !empty($field['settings']['allowed_values'][0]['vocabulary']) ? $field['settings']['allowed_values'][0]['vocabulary'] : NULL;
|
||||
$access = variable_get('entity_translation_taxonomy_autocomplete', FALSE);
|
||||
|
||||
// Add a checkbox to toggle in-place translation for autocomplete widgets.
|
||||
$form['instance']['settings'][$key] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable in-place translation of terms'),
|
||||
'#description' => t('Check this option if you wish to use translation forms to perform in-place translation for terms entered in the original language.'),
|
||||
'#default_value' => !$translatable && !empty($form['#instance']['settings'][$key]),
|
||||
'#access' => $access && (!$form_state['field_has_data'] || !$translatable) && entity_translation_enabled('taxonomy_term', $bundle),
|
||||
'#states' => array(
|
||||
'visible' => array(':input[name="field[translatable]"]' => array('checked' => FALSE)),
|
||||
),
|
||||
'#weight' => -8,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether in-place translation is enabled for the autocomplete widget.
|
||||
*
|
||||
* @param array $element
|
||||
* The widget form element.
|
||||
* @param array $form_state
|
||||
* The form state array.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if in-place translation is enabled, FALSE otherwise.
|
||||
*/
|
||||
function _entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state) {
|
||||
$field = field_info_field($element['#field_name']);
|
||||
if (field_is_translatable($element['#entity_type'], $field)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
list($id, , $bundle) = entity_extract_ids($element['#entity_type'], $element['#entity']);
|
||||
if (!$id) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$entity_type = 'taxonomy_term';
|
||||
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
|
||||
$active_langcode = $parent_handler->getActiveLanguage();
|
||||
$translations = $parent_handler->getTranslations();
|
||||
$entity_langcode = isset($translations->original) ? $translations->original : LANGUAGE_NONE;
|
||||
$instance = field_info_instance($element['#entity_type'], $field['field_name'], $bundle);
|
||||
|
||||
// We need to make sure that we are not dealing with a translation form.
|
||||
// However checking the active language is not enough, because the user may
|
||||
// have changed the entity language.
|
||||
return
|
||||
(isset($form_state['entity_translation']['is_translation']) ?
|
||||
$form_state['entity_translation']['is_translation'] : ($active_langcode != $entity_langcode)) &&
|
||||
!empty($instance['settings']['entity_translation_taxonomy_autocomplete_translate']) &&
|
||||
(user_access('translate any entity') || user_access("translate $entity_type entities"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_WIDGET_TYPE_form_alter().
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function entity_translation_field_widget_taxonomy_autocomplete_form_alter(&$element, &$form_state, $context) {
|
||||
// The autocomplete widget is also displayed in the field configuration form,
|
||||
// in which case we do not need to perform any alteration. To preserve BC, by
|
||||
// default we enable our taxonomy autocomplete override only on new sites.
|
||||
if (!isset($element['#entity']) || !variable_get('entity_translation_taxonomy_autocomplete', FALSE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We will need to translate term names, if Title is enabled and configured
|
||||
// for this vocabulary.
|
||||
$entity_type = 'taxonomy_term';
|
||||
$field = field_widget_field($element, $form_state);
|
||||
$bundle = !empty($field['settings']['allowed_values'][0]['vocabulary']) ? $field['settings']['allowed_values'][0]['vocabulary'] : NULL;
|
||||
if ($bundle && entity_translation_enabled($entity_type, $bundle)) {
|
||||
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
|
||||
$langcode = $parent_handler->getActiveLanguage();
|
||||
$terms = array_values(_entity_translation_taxonomy_autocomplete_widget_get_terms($element));
|
||||
|
||||
// If we are using the regular autocomplete behavior also in translation
|
||||
// forms, we need to set our custom callback.
|
||||
if (!_entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state)) {
|
||||
$element['#autocomplete_path'] = 'entity_translation/' . $entity_type . '/autocomplete/' . $langcode . '/' . $element['#field_name'];
|
||||
$translations = $parent_handler->getTranslations();
|
||||
if (isset($translations->original) && $translations->original != $langcode) {
|
||||
$labels = array();
|
||||
foreach ($terms as $delta => $term) {
|
||||
$labels[] = _entity_translation_taxonomy_label($term, $langcode);
|
||||
}
|
||||
$element['#default_value'] = implode(', ', $labels);
|
||||
}
|
||||
}
|
||||
// Otherwise we just provide the in-place translation widget.
|
||||
else {
|
||||
$element['#type'] = 'fieldset';
|
||||
$element['#description'] = t('Enter one translation for each term');
|
||||
$element['#access'] = (bool) $terms;
|
||||
foreach ($terms as $delta => $term) {
|
||||
$element[$delta] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => _entity_translation_taxonomy_label($term, $langcode),
|
||||
'#required' => TRUE,
|
||||
'#tid' => $term->tid,
|
||||
);
|
||||
}
|
||||
$element['#process'][] = 'entity_translation_taxonomy_autocomplete_process';
|
||||
}
|
||||
|
||||
// The native term save logic is performed at widget validation level, so we
|
||||
// just replace the validation handler to provide our logic instead.
|
||||
$element['#element_validate'] = array_values(array_diff($element['#element_validate'], array('taxonomy_autocomplete_validate')));
|
||||
$element['#element_validate'][] = 'entity_translation_taxonomy_autocomplete_validate';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the terms referenced by the taxonomy autocomplete widget field.
|
||||
*
|
||||
* @param array $element
|
||||
* The taxonomy autocomplete form element.
|
||||
*
|
||||
* @return object[]
|
||||
* An associative array of taxonomy term object keyed by their identifiers.
|
||||
*/
|
||||
function _entity_translation_taxonomy_autocomplete_widget_get_terms($element) {
|
||||
$items = isset($element['#entity']->{$element['#field_name']}[$element['#language']]) ?
|
||||
$element['#entity']->{$element['#field_name']}[$element['#language']] : array();
|
||||
$tids = array_map(function ($item) { return $item['tid']; }, $items);
|
||||
return taxonomy_term_load_multiple($tids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process callback for the ET taxonomy autocomplete widget.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function entity_translation_taxonomy_autocomplete_process($element) {
|
||||
// The in-place translation widget makes sense only for untranslatable field,
|
||||
// which may have the "(all languages)" label suffix. In this case it would be
|
||||
// confusing so we need to revert that.
|
||||
$instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
|
||||
$element['#title'] = check_plain($instance['label']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity translation taxonomy autocomplete callback.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The input language.
|
||||
* @param string $field_name
|
||||
* The name of the term reference field.
|
||||
* @param string $tags_typed
|
||||
* (optional) A comma-separated list of term names entered in the
|
||||
* autocomplete form element. Only the last term is used for autocompletion.
|
||||
* Defaults to an empty string.
|
||||
*
|
||||
* @see taxonomy_autocomplete()
|
||||
*/
|
||||
function entity_translation_taxonomy_term_autocomplete($langcode = NULL, $field_name = '', $tags_typed = '') {
|
||||
// If the request has a '/' in the search text, then the menu system will have
|
||||
// split it into multiple arguments, recover the intended $tags_typed.
|
||||
$args = func_get_args();
|
||||
// Shift off the $langcode and $field_name arguments.
|
||||
array_shift($args);
|
||||
array_shift($args);
|
||||
$tags_typed = implode('/', $args);
|
||||
|
||||
// Make sure the field exists and is a taxonomy field.
|
||||
if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
|
||||
// Error string. The JavaScript handler will realize this is not JSON and
|
||||
// will display it as debugging information.
|
||||
print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name));
|
||||
exit;
|
||||
}
|
||||
|
||||
// The user enters a comma-separated list of tags. We only autocomplete the
|
||||
// last tag.
|
||||
$tags_typed = drupal_explode_tags($tags_typed);
|
||||
$tag_last = drupal_strtolower(array_pop($tags_typed));
|
||||
|
||||
$term_matches = array();
|
||||
if ($tag_last != '') {
|
||||
if (!isset($langcode) || $langcode == LANGUAGE_NONE) {
|
||||
$langcode = $GLOBALS['language_content']->language;
|
||||
}
|
||||
|
||||
// Part of the criteria for the query come from the field's own settings.
|
||||
$vocabulary = _entity_translation_taxonomy_reference_get_vocabulary($field);
|
||||
|
||||
$entity_type = 'taxonomy_term';
|
||||
$query = new EntityFieldQuery();
|
||||
$query->addTag('taxonomy_term_access');
|
||||
$query->entityCondition('entity_type', $entity_type);
|
||||
|
||||
// If the Title module is enabled and the taxonomy term name is replaced for
|
||||
// the current bundle, we can look for translated names, otherwise we fall
|
||||
// back to the regular name property.
|
||||
if (module_invoke('title', 'field_replacement_enabled', $entity_type, $vocabulary->machine_name, 'name')) {
|
||||
$name_field = 'name_field';
|
||||
$language_group = 0;
|
||||
// Do not select already entered terms.
|
||||
$column = 'value';
|
||||
if (!empty($tags_typed)) {
|
||||
$query->fieldCondition($name_field, $column, $tags_typed, 'NOT IN', NULL, $language_group);
|
||||
}
|
||||
$query->fieldCondition($name_field, $column, $tag_last, 'CONTAINS', NULL, $language_group);
|
||||
$query->fieldLanguageCondition($name_field, array($langcode, LANGUAGE_NONE), NULL, NULL, $language_group);
|
||||
}
|
||||
else {
|
||||
$name_field = 'name';
|
||||
// Do not select already entered terms.
|
||||
if (!empty($tags_typed)) {
|
||||
$query->propertyCondition($name_field, $tags_typed, 'NOT IN');
|
||||
}
|
||||
$query->propertyCondition($name_field, $tag_last, 'CONTAINS');
|
||||
}
|
||||
|
||||
// Select rows that match by term name.
|
||||
$query->propertyCondition('vid', $vocabulary->vid);
|
||||
$query->range(0, 10);
|
||||
$result = $query->execute();
|
||||
|
||||
// Populate the results array.
|
||||
$prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
|
||||
$terms = !empty($result[$entity_type]) ? taxonomy_term_load_multiple(array_keys($result[$entity_type])) : array();
|
||||
foreach ($terms as $tid => $term) {
|
||||
$name = _entity_translation_taxonomy_label($term, $langcode);
|
||||
$n = $name;
|
||||
// Term names containing commas or quotes must be wrapped in quotes.
|
||||
if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
|
||||
$n = '"' . str_replace('"', '""', $name) . '"';
|
||||
}
|
||||
$term_matches[$prefix . $n] = check_plain($name);
|
||||
}
|
||||
}
|
||||
|
||||
drupal_json_output($term_matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vocabulary enabled for the specified taxonomy reference field.
|
||||
*
|
||||
* @param array $field
|
||||
* A field definition.
|
||||
*
|
||||
* @return object|null
|
||||
* A vocabulary object or NULL if none is found.
|
||||
*/
|
||||
function _entity_translation_taxonomy_reference_get_vocabulary($field) {
|
||||
$vocabulary = NULL;
|
||||
if (!empty($field['settings']['allowed_values'])) {
|
||||
$vids = array();
|
||||
$vocabularies = taxonomy_vocabulary_get_names();
|
||||
foreach ($field['settings']['allowed_values'] as $tree) {
|
||||
$vids[] = $vocabularies[$tree['vocabulary']]->vid;
|
||||
}
|
||||
$vocabulary = taxonomy_vocabulary_load(reset($vids));
|
||||
}
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validate handler for taxonomy term autocomplete element.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see taxonomy_autocomplete_validate()
|
||||
*/
|
||||
function entity_translation_taxonomy_autocomplete_validate($element, &$form_state) {
|
||||
$value = array();
|
||||
list($id) = entity_extract_ids($element['#entity_type'], $element['#entity']);
|
||||
$is_new = !isset($id);
|
||||
|
||||
// This is the language of the parent entity, that we will be applying to new
|
||||
// terms.
|
||||
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
|
||||
$langcode = !empty($form_state['entity_translation']['form_langcode']) ?
|
||||
$form_state['entity_translation']['form_langcode'] : $parent_handler->getActiveLanguage();
|
||||
|
||||
// Handle in-place translation.
|
||||
if (_entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state)) {
|
||||
// The referenced terms cannot change, so we just need to collect their term
|
||||
// identifiers. We also build a map of the corresponding deltas for later
|
||||
// use.
|
||||
$deltas = array();
|
||||
foreach (element_children($element) as $delta) {
|
||||
$tid = $element[$delta]['#tid'];
|
||||
$deltas[$tid] = $delta;
|
||||
$value[$delta]['tid'] = $tid;
|
||||
}
|
||||
|
||||
// Save term translations.
|
||||
$entity_type = 'taxonomy_term';
|
||||
$name_field = 'name_field';
|
||||
$source_langcode = $parent_handler->getSourceLanguage();
|
||||
// This is a validation handler, so we must defer the actual save to the
|
||||
// submit phase.
|
||||
$terms_to_save = &$form_state['entity_translation']['taxonomy_autocomplete'][$element['#entity_type']][$id][$element['#field_name']];
|
||||
foreach (taxonomy_term_load_multiple(array_keys($deltas)) as $term) {
|
||||
// This is also the right context to perform validation.
|
||||
$term_translation = $element[$deltas[$term->tid]]['#value'];
|
||||
if (!$term_translation) {
|
||||
$instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
|
||||
drupal_set_message(t('The translations for %field_label cannot be empty.', array('%field_label' => $instance['label'])), 'error', FALSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
$handler = entity_translation_get_handler($entity_type, $term);
|
||||
$translations = $handler->getTranslations();
|
||||
$term_langcode = $handler->getLanguage();
|
||||
$typed_langcode = $term_langcode != LANGUAGE_NONE ? $langcode : $term_langcode;
|
||||
|
||||
// Create a new translation in the active language, if it is missing.
|
||||
if (!isset($translations->data[$typed_langcode]) && $typed_langcode != LANGUAGE_NONE) {
|
||||
$translation = array(
|
||||
'language' => $typed_langcode,
|
||||
'source' => $source_langcode,
|
||||
'uid' => $GLOBALS['user']->uid,
|
||||
'status' => 1,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
);
|
||||
$translation_values = array(
|
||||
$name_field => array($typed_langcode => array(array('value' => $term_translation))),
|
||||
);
|
||||
$handler->setTranslation($translation, $translation_values);
|
||||
$terms_to_save[] = $term;
|
||||
}
|
||||
// Otherwise we just update the existing translation, if it has changed.
|
||||
// If the term is language-neutral, we just update its main value. This is
|
||||
// expected to happen normally, but could when referencing existing terms.
|
||||
elseif ($term_translation != _entity_translation_taxonomy_label($term, $typed_langcode)) {
|
||||
$term->{$name_field}[$typed_langcode][0]['value'] = $term_translation;
|
||||
$terms_to_save[] = $term;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Autocomplete widgets do not send their tids in the form, so we must detect
|
||||
// them here and process them independently.
|
||||
elseif ($tags = $element['#value']) {
|
||||
$entity_type = 'taxonomy_term';
|
||||
$field = field_widget_field($element, $form_state);
|
||||
$vocabulary = _entity_translation_taxonomy_reference_get_vocabulary($field);
|
||||
$typed_tags = drupal_explode_tags($tags);
|
||||
|
||||
// Collect existing terms by name.
|
||||
$existing_terms = array();
|
||||
foreach (_entity_translation_taxonomy_autocomplete_widget_get_terms($element) as $term) {
|
||||
$name = _entity_translation_taxonomy_label($term, $langcode);
|
||||
$existing_terms[$name] = $term;
|
||||
}
|
||||
|
||||
// Select terms that match by the (translated) name.
|
||||
$query = new EntityFieldQuery();
|
||||
$query->addTag('taxonomy_term_access');
|
||||
$query->entityCondition('entity_type', $entity_type);
|
||||
$query->propertyCondition('vid', $vocabulary->vid);
|
||||
if ($langcode != LANGUAGE_NONE && module_invoke('title', 'field_replacement_enabled', $entity_type, $vocabulary->machine_name, 'name')) {
|
||||
$language_group = 0;
|
||||
// Do not select already entered terms.
|
||||
$name_field = 'name_field';
|
||||
$column = 'value';
|
||||
$query->fieldCondition($name_field, $column, $typed_tags, NULL, NULL, $language_group);
|
||||
// When we are creating a new entity, we cannot filter by active language,
|
||||
// as that may have not be applied to the autocomplete query.
|
||||
if (!$is_new) {
|
||||
$query->fieldLanguageCondition($name_field, array($langcode, LANGUAGE_NONE), NULL, NULL, $language_group);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$query->propertyCondition('name', $typed_tags);
|
||||
}
|
||||
$result = $query->execute();
|
||||
|
||||
// When we are creating a new entity, the language used for the autocomplete
|
||||
// query is the current content language, so we should use that to update
|
||||
// the map of existing terms.
|
||||
if (!empty($result[$entity_type])) {
|
||||
$typed_langcode = !$is_new ? $langcode : $GLOBALS['language_content']->language;
|
||||
foreach (taxonomy_term_load_multiple(array_keys($result[$entity_type])) as $term) {
|
||||
$name = _entity_translation_taxonomy_label($term, $typed_langcode);
|
||||
$existing_terms[$name] = $term;
|
||||
}
|
||||
}
|
||||
|
||||
// Now collect the identifiers for the various terms and update the taxonomy
|
||||
// reference field values.
|
||||
foreach ($typed_tags as $delta => $typed_tag) {
|
||||
// See if the term exists in the chosen vocabulary and return the tid.
|
||||
// Otherwise create a new 'autocreate' term for insert/update.
|
||||
if (isset($existing_terms[$typed_tag])) {
|
||||
$term = $existing_terms[$typed_tag];
|
||||
}
|
||||
else {
|
||||
$term = (object) array(
|
||||
'tid' => 'autocreate',
|
||||
'vid' => $vocabulary->vid,
|
||||
'name' => $typed_tag,
|
||||
'vocabulary_machine_name' => $vocabulary->machine_name,
|
||||
);
|
||||
$handler = entity_translation_get_handler($entity_type, $term);
|
||||
$handler->setOriginalLanguage($langcode);
|
||||
$handler->initTranslations();
|
||||
}
|
||||
$value[] = (array) $term;
|
||||
}
|
||||
}
|
||||
|
||||
form_set_value($element, $value, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Term-specific implementation of hook_field_attach_submit().
|
||||
*/
|
||||
function entity_translation_taxonomy_term_field_attach_submit($entity_type, $entity, $form, &$form_state) {
|
||||
// Finally save in-place translations
|
||||
if (!empty($form_state['entity_translation']['taxonomy_autocomplete'])) {
|
||||
foreach ($form_state['entity_translation']['taxonomy_autocomplete'] as $entity_type => $entity_type_data) {
|
||||
foreach ($entity_type_data as $id => $field_name_data) {
|
||||
foreach ($field_name_data as $field_name => $term_data) {
|
||||
if (!is_array($term_data)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($term_data as $term) {
|
||||
entity_translation_entity_save('taxonomy_term', $term);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -7,9 +7,8 @@ dependencies[] = i18n
|
||||
dependencies[] = i18n_menu
|
||||
files[] = entity_translation_i18n_menu.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-04-26
|
||||
version = "7.x-1.0-beta2+33-dev"
|
||||
; Information added by Drupal.org packaging script on 2019-11-24
|
||||
version = "7.x-1.1"
|
||||
core = "7.x"
|
||||
project = "entity_translation"
|
||||
datestamp = "1366981073"
|
||||
|
||||
datestamp = "1574630892"
|
||||
|
||||
+16
-10
@@ -58,11 +58,10 @@ function entity_translation_i18n_menu_node_presave($node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we change from menu localization to translation sets while working on
|
||||
// a translation (i.e. not the source node), then leave the source menu item
|
||||
// intact and create a new one.
|
||||
// When creating a new translation, leave the source menu item intact and
|
||||
// create a new one.
|
||||
$langcode = entity_language('node', $node);
|
||||
if (!empty($node->menu) && $tset && $node->menu['language'] == LANGUAGE_NONE && !empty($source_langcode)) {
|
||||
if (!empty($node->menu) && $tset && !empty($source_langcode)) {
|
||||
$node->source_menu = menu_link_load($node->menu['mlid']);
|
||||
unset($node->menu['mlid']);
|
||||
}
|
||||
@@ -165,7 +164,7 @@ function entity_translation_i18n_menu_form(&$form, &$form_state) {
|
||||
$default = isset($source_menu['language']) && $source_menu['language'] != LANGUAGE_NONE;
|
||||
$languages = language_list();
|
||||
$handler = entity_translation_entity_form_get_handler($form, $form_state);
|
||||
$langcode = $handler->getFormLanguage();
|
||||
$langcode = $handler->getActiveLanguage();
|
||||
$language_name = isset($languages[$langcode]) ? t($languages[$langcode]->name) : t('current');
|
||||
|
||||
$form['menu']['#multilingual'] = TRUE;
|
||||
@@ -197,8 +196,8 @@ function entity_translation_i18n_menu_form_menu_edit_item_validate($form, &$form
|
||||
$item = $form_state['values'];
|
||||
|
||||
// Localizable menu items should not be created when a translation set for the
|
||||
// same path already exists.
|
||||
if ($item['language'] == LANGUAGE_NONE) {
|
||||
// same path already exists (exluding special paths starting by <).
|
||||
if ($item['language'] == LANGUAGE_NONE && strpos($item['link_path'], '<') !== 0) {
|
||||
$count = db_select('menu_links', 'ml')
|
||||
->condition('ml.link_path', $item['link_path'])
|
||||
->condition('ml.i18n_tsid', 0, '<>')
|
||||
@@ -262,7 +261,7 @@ function entity_translation_i18n_menu_item($item) {
|
||||
* @param $node
|
||||
* A node object, with a menu item ($node->menu).
|
||||
* @param $langcode
|
||||
* The language into which the menu item should be translates.
|
||||
* The language into which the menu item should be translated.
|
||||
*/
|
||||
function entity_translation_i18n_menu_node_menu_item_translate($node, $langcode) {
|
||||
// Localization.
|
||||
@@ -272,7 +271,7 @@ function entity_translation_i18n_menu_node_menu_item_translate($node, $langcode)
|
||||
// used for the node menu form; i18n_menu_link_localize only localizes
|
||||
// rendered properties 'title' and 'localized_options.attributes.title'.
|
||||
$node->menu['link_title'] = $node->menu['title'];
|
||||
$node->menu['options']['attributes']['title'] = $node->menu['localized_options']['attributes']['title'];
|
||||
$node->menu['options']['attributes']['title'] = isset($node->menu['localized_options']['attributes']['title']) ? $node->menu['localized_options']['attributes']['title'] : '';
|
||||
}
|
||||
// Translation sets.
|
||||
else {
|
||||
@@ -288,7 +287,9 @@ function entity_translation_i18n_menu_node_menu_item_translate($node, $langcode)
|
||||
|
||||
// Make sure the menu item is not set to hidden; i18n_menu automatically
|
||||
// hides any menu items not matching the current interface language.
|
||||
$menu['hidden'] = FALSE;
|
||||
if (!empty($menu)) {
|
||||
$menu['hidden'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the menu item with the translated version, or null if there is
|
||||
@@ -368,6 +369,11 @@ function entity_translation_i18n_menu_entity_translation_upgrade($node, $transla
|
||||
* Implements hook_entity_translation_delete().
|
||||
*/
|
||||
function entity_translation_i18n_menu_entity_translation_delete($entity_type, $entity, $langcode) {
|
||||
// Make sure that we are working with an entity of type node.
|
||||
if ($entity_type != 'node') {
|
||||
return;
|
||||
}
|
||||
|
||||
list($entity_id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
// Clean-up all menu module links that point to this node.
|
||||
|
||||
+47
-16
@@ -26,6 +26,7 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
$this->login($this->getAdminUser(array('administer menu')));
|
||||
$this->addLanguage('en');
|
||||
$this->addLanguage('es');
|
||||
$this->addLanguage('it');
|
||||
$this->configureContentType();
|
||||
$this->configureMenu();
|
||||
$this->enableUrlLanguageDetection();
|
||||
@@ -45,13 +46,13 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
/**
|
||||
* Create page with menu item.
|
||||
*/
|
||||
function createPage($link_title, $langcode) {
|
||||
function createPage($link_title, $description, $langcode) {
|
||||
$edit = array();
|
||||
$edit['title'] = $this->randomName();
|
||||
$edit['language'] = $langcode;
|
||||
$edit['menu[enabled]'] = TRUE;
|
||||
$edit['menu[link_title]'] = $link_title;
|
||||
$edit['menu[description]'] = 'link language = ' . $langcode;
|
||||
$edit['menu[description]'] = $description ? $description : 'link language = ' . $langcode;
|
||||
$this->drupalPost('node/add/page', $edit, t('Save'));
|
||||
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $edit['title'])), t('Basic page created.'));
|
||||
|
||||
@@ -69,14 +70,16 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
/**
|
||||
* Create a translation with menu item.
|
||||
*/
|
||||
function createTranslation($node, $link_title, $langcode, $use_tsets = FALSE) {
|
||||
function createTranslation($node, $link_title, $description, $langcode, $use_tsets = FALSE) {
|
||||
$this->drupalGet('node/' . $node->nid . '/edit/add/' . $node->language . '/' . $langcode);
|
||||
|
||||
$edit = array();
|
||||
$edit['menu[enabled]'] = TRUE;
|
||||
$edit['menu[link_title]'] = $link_title;
|
||||
$edit['menu[description]'] = 'link language = ' . $langcode;
|
||||
$edit['menu[tset]'] = $use_tsets;
|
||||
$edit['menu[description]'] = $description ? $description : 'link language = ' . $langcode;
|
||||
if ($use_tsets != NULL) {
|
||||
$edit['menu[tset]'] = $use_tsets;
|
||||
}
|
||||
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
@@ -122,23 +125,36 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
$this->assertText($link_title, 'New menu link title found: ' . $link_title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a link with a given label and title is found in the page.
|
||||
*/
|
||||
function assertLinkWithTitleAttribute($label, $title) {
|
||||
$links = $this->xpath('//a[normalize-space(text())=:label and normalize-space(@title)=:title]', array(
|
||||
':label' => $label,
|
||||
':title' => $title,
|
||||
));
|
||||
$this->assert(isset($links[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if menu localization works.
|
||||
*/
|
||||
function testMenuLocalization() {
|
||||
// Create Basic page in English.
|
||||
$link_title_en = $this->randomName();
|
||||
$node = $this->createPage($link_title_en, 'en');
|
||||
$link_description_en = $this->randomName() . ' & htmlentity';
|
||||
$node = $this->createPage($link_title_en, $link_description_en, 'en');
|
||||
|
||||
// Submit translation in Spanish.
|
||||
$link_title_es = $this->randomName();
|
||||
$node_translation = $this->createTranslation($node, $link_title_es, 'es');
|
||||
$link_description_es = $this->randomName() . ' & htmlentity';
|
||||
$node_translation = $this->createTranslation($node, $link_title_es, $link_description_es, 'es');
|
||||
|
||||
// Check menu links in both languages.
|
||||
$this->get('en', '<front>');
|
||||
$this->assertText($link_title_en);
|
||||
$this->assertLinkWithTitleAttribute($link_title_en, $link_description_en);
|
||||
$this->get('es', '<front>');
|
||||
$this->assertText($link_title_es);
|
||||
$this->assertLinkWithTitleAttribute($link_title_es, $link_description_es);
|
||||
|
||||
// Edit English menu link.
|
||||
$link_title_en2 = $this->randomName();
|
||||
@@ -146,15 +162,19 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
|
||||
// Check that Spanish menu link has not changed.
|
||||
$this->get('es', '<front>');
|
||||
$this->assertText($link_title_es);
|
||||
$this->assertLinkWithTitleAttribute($link_title_es, $link_description_es);
|
||||
|
||||
// Edit Spanish menu link.
|
||||
$link_title_es2 = $this->randomName();
|
||||
$this->editPage($node, $link_title_es, $link_title_es2, 'es');
|
||||
|
||||
// Check that English menu link has not changed.
|
||||
// Check that English menu link has changed.
|
||||
$this->get('en', '<front>');
|
||||
$this->assertText($link_title_en2);
|
||||
$this->assertLinkWithTitleAttribute($link_title_en2, $link_description_en);
|
||||
|
||||
// Check that Spanish menu link has changed.
|
||||
$this->get('es', '<front>');
|
||||
$this->assertLinkWithTitleAttribute($link_title_es2, $link_description_es);
|
||||
|
||||
// Delete Spanish translation and check that the respective menu item has
|
||||
// been deleted as well.
|
||||
@@ -169,11 +189,11 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
function testMenuLocalizationCustomSourceLanguage() {
|
||||
// Create Basic page in Spanish.
|
||||
$link_title_es = $this->randomName();
|
||||
$node = $this->createPage($link_title_es, 'es');
|
||||
$node = $this->createPage($link_title_es, NULL, 'es');
|
||||
|
||||
// Submit translation in English.
|
||||
$link_title_en = $this->randomName();
|
||||
$node_translation = $this->createTranslation($node, $link_title_en, 'en');
|
||||
$node_translation = $this->createTranslation($node, $link_title_en, NULL, 'en');
|
||||
|
||||
// Check menu links in both languages.
|
||||
$this->get('es', '<front>');
|
||||
@@ -204,11 +224,11 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
function testMenuTranslation() {
|
||||
// Create Basic page in English.
|
||||
$link_title_en = $this->randomName();
|
||||
$node = $this->createPage($link_title_en, 'en');
|
||||
$node = $this->createPage($link_title_en, NULL, 'en');
|
||||
|
||||
// Submit translation in Spanish.
|
||||
$link_title_es = $this->randomName();
|
||||
$node_translation = $this->createTranslation($node, $link_title_es, 'es', TRUE);
|
||||
$node_translation = $this->createTranslation($node, $link_title_es, NULL, 'es', TRUE);
|
||||
|
||||
// Check menu links in both languages.
|
||||
$this->get('en', '<front>');
|
||||
@@ -232,6 +252,17 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
|
||||
$this->get('en', '<front>');
|
||||
$this->assertText($link_title_en2);
|
||||
|
||||
// Add another translation, and check that other menu items are not
|
||||
// affected. See https://drupal.org/node/1982140
|
||||
$link_title_it = $this->randomName();
|
||||
$node_translation = $this->createTranslation($node, $link_title_it, NULL, 'it', NULL);
|
||||
|
||||
// Check that Spanish and English menu links have not changed.
|
||||
$this->get('es', '<front>');
|
||||
$this->assertText($link_title_es2);
|
||||
$this->get('en', '<front>');
|
||||
$this->assertText($link_title_en2);
|
||||
|
||||
// Delete Spanish translation and check that the respective menu item has
|
||||
// been deleted as well.
|
||||
$this->removeTranslation($node, 'es');
|
||||
|
||||
+7
-3
@@ -34,8 +34,8 @@ function entity_translation_upgrade_start($types) {
|
||||
*/
|
||||
function entity_translation_upgrade_end($success, $results, $operations, $elapsed) {
|
||||
if (!empty($results)) {
|
||||
$message = format_plural($results, '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
|
||||
watchdog('entity translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results), WATCHDOG_INFO);
|
||||
$message = format_plural($results['total'], '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
|
||||
watchdog('entity translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results['total']), WATCHDOG_INFO);
|
||||
}
|
||||
else {
|
||||
$message = t('No node translation available for the upgrade.');
|
||||
@@ -91,6 +91,10 @@ function entity_translation_upgrade_do($types, &$context) {
|
||||
$node = $nodes[$nid];
|
||||
$original = $nodes[$node->tnid];
|
||||
$handler = entity_translation_get_handler('node', $original);
|
||||
// Instantiate the data and original properties of the translations.
|
||||
if (empty($handler->getTranslations()->data)) {
|
||||
$handler->initTranslations();
|
||||
}
|
||||
|
||||
if (!isset($instances[$node->type])) {
|
||||
$instances[$node->type] = field_info_instances('node', $node->type);
|
||||
@@ -185,7 +189,7 @@ function entity_translation_upgrade_do($types, &$context) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
|
||||
if ($context['finished'] >= 1) {
|
||||
$context['results'] = $context['sandbox']['total'];
|
||||
$context['results']['total'] = $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
function entity_translation_upgrade_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$items['entity-translation-upgrade'] = array(
|
||||
'description' => "Upgrades all nodes of the specified content type from Content Translation to Entity Translation.",
|
||||
'arguments' => array(
|
||||
'content_type' => 'Content type of nodes to be upgraded.',
|
||||
),
|
||||
'examples' => array(
|
||||
'drush entity-translation-upgrade article' => 'Upgrades all nodes of content type "article" from Content Translation to Entity Translation.',
|
||||
),
|
||||
'aliases' => array('etu'),
|
||||
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_drush_help().
|
||||
*
|
||||
* @param
|
||||
* A string with the help section
|
||||
*
|
||||
* @return
|
||||
* A string with the help text for the entity-translation-upgrade command
|
||||
*/
|
||||
function entity_translation_upgrade_drush_help($section) {
|
||||
switch ($section) {
|
||||
case 'drush:entity-translation-upgrade':
|
||||
return dt("Brief help for Drush command entity-translation-upgrade.");
|
||||
|
||||
case 'meta:entity_translation_upgrade:title':
|
||||
return dt("Entity Translation Upgrade commands");
|
||||
|
||||
case 'meta:entity_translation_upgrade:summary':
|
||||
return dt("Upgrading nodes to Entity Translation.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements drush_hook_COMMAND().
|
||||
*
|
||||
* @param
|
||||
* The content type of which the nodes shall be upgraded
|
||||
*
|
||||
* Runs the batch upgrading nodes of the specified content_type to Entity
|
||||
* Translation. Lets user choose content type from a list, if argument has
|
||||
* not been provided.
|
||||
*/
|
||||
function drush_entity_translation_upgrade($content_type = "") {
|
||||
// Get all installed content types.
|
||||
$available_types = array();
|
||||
$available_types_chose = array();
|
||||
$available_types_str = '';
|
||||
foreach (node_type_get_types() as $type) {
|
||||
$available_types[$type->type] = $type->type;
|
||||
$available_types_chose[$type->type] = $type->name;
|
||||
if (strlen($available_types_str) > 0) {
|
||||
$available_types_str .= ', ';
|
||||
}
|
||||
$available_types_str .= $type->type;
|
||||
}
|
||||
|
||||
// If argument content_type is empty, prompt user for content type.
|
||||
if (!$content_type) {
|
||||
$content_type = drush_choice($available_types_chose, dt('Choose the content type of the nodes to be upgraded to Entity Translation:'));
|
||||
}
|
||||
|
||||
// Do content type argument checks.
|
||||
if (!$content_type) {
|
||||
return TRUE;
|
||||
}
|
||||
if (strlen($available_types_str) == 0) {
|
||||
return drush_set_error(dt('Entity Translation Upgrade cannot run as no content type has been installed.'));
|
||||
}
|
||||
if (!in_array($content_type, $available_types)) {
|
||||
return drush_set_error(dt('"@content_type" is not a valid content type machine name. Please use one of these installed content types as argument: @available_types_str.', array('@content_type' => $content_type, '@available_types_str' => $available_types_str)));
|
||||
}
|
||||
|
||||
// Start batch to upgrade nodes of the specified content type.
|
||||
$types = array($content_type => $content_type);
|
||||
$batch = array(
|
||||
'operations' => array(
|
||||
array('entity_translation_upgrade_do', array($types)),
|
||||
array('entity_translation_upgrade_complete', array()),
|
||||
),
|
||||
'finished' => 'entity_translation_upgrade_drush_end',
|
||||
'file' => drupal_get_path('module', 'entity_translation_upgrade') . '/entity_translation_upgrade.admin.inc',
|
||||
'progressive' => FALSE,
|
||||
);
|
||||
batch_set($batch);
|
||||
drush_backend_batch_process();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the 'finished' batch callback, drush version.
|
||||
*/
|
||||
function entity_translation_upgrade_drush_end($success, $results, $operations, $elapsed) {
|
||||
// Print result messages.
|
||||
if (!empty($results)) {
|
||||
$message = format_plural($results['total'], '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
|
||||
$severity = 'ok';
|
||||
watchdog('Entity Translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results['total']), WATCHDOG_INFO);
|
||||
}
|
||||
else {
|
||||
$message = t('No node translation available for the upgrade.');
|
||||
$severity = 'warning';
|
||||
}
|
||||
|
||||
drush_log($message, $severity);
|
||||
}
|
||||
+4
-4
@@ -3,10 +3,10 @@ description = Provides an upgrade path from node-based translation to field-base
|
||||
package = Multilingual - Entity Translation
|
||||
core = 7.x
|
||||
dependencies[] = entity_translation
|
||||
files[] = entity_translation_upgrade.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-04-26
|
||||
version = "7.x-1.0-beta2+33-dev"
|
||||
; Information added by Drupal.org packaging script on 2019-11-24
|
||||
version = "7.x-1.1"
|
||||
core = "7.x"
|
||||
project = "entity_translation"
|
||||
datestamp = "1366981073"
|
||||
|
||||
datestamp = "1574630892"
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Tests for Entity Translation module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for the upgrade translation process.
|
||||
*/
|
||||
class EntityTranslationUpgradeTestCase extends EntityTranslationTestCase {
|
||||
|
||||
/**
|
||||
* Return the test information.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Translation upgrade',
|
||||
'description' => 'Tests for the upgrade from Content Translation to Entity Translation.',
|
||||
'group' => 'Entity translation',
|
||||
'dependencies' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('locale', 'translation', 'translation_test', 'entity_translation', 'entity_translation_upgrade');
|
||||
$this->getAdminUser(array(
|
||||
'toggle field translatability',
|
||||
'administer entity translation',
|
||||
));
|
||||
$this->getTranslatorUser(array(
|
||||
'translate content',
|
||||
));
|
||||
$this->login($this->getAdminUser());
|
||||
$this->addLanguage('en');
|
||||
$this->addLanguage('es');
|
||||
$this->configureContentTypeForContentTranslation();
|
||||
$this->enableUrlLanguageDetection();
|
||||
$this->login($this->getTranslatorUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the "Basic page" content type for entity translation tests.
|
||||
*/
|
||||
public function configureContentTypeForContentTranslation() {
|
||||
// Configure the "Basic page" content type to use multilingual support with
|
||||
// content translation.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = TRANSLATION_ENABLED;
|
||||
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle body field's translatability.
|
||||
*/
|
||||
public function makeBodyFieldTranslatable() {
|
||||
$edit = array();
|
||||
$this->drupalGet('admin/structure/types/manage/page/fields/body');
|
||||
$this->clickLink('Enable translation');
|
||||
$this->drupalPost(NULL, array(), t('Confirm'));
|
||||
$this->assertRaw(t('Data successfully processed.'), t('Body field have been made translatable.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslationTestCase::createPage
|
||||
*/
|
||||
function createContentTranslationPage($title, $body, $language = NULL) {
|
||||
$edit = array();
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$edit["title"] = $title;
|
||||
$edit["body[$langcode][0][value]"] = $body;
|
||||
if (!empty($language)) {
|
||||
$edit['language'] = $language;
|
||||
}
|
||||
$this->drupalPost('node/add/page', $edit, t('Save'));
|
||||
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), 'Basic page created.');
|
||||
|
||||
// Check to make sure the node was created.
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertTrue($node, 'Node found in database.');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TranslationTestCase::createTranslation
|
||||
*/
|
||||
function createContentTranslationTranslation($node, $title, $body, $language) {
|
||||
$this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $language)));
|
||||
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$body_key = "body[$langcode][0][value]";
|
||||
$this->assertFieldByXPath('//input[@id="edit-title"]', $node->title, "Original title value correctly populated.");
|
||||
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[LANGUAGE_NONE][0]['value'], "Original body value correctly populated.");
|
||||
|
||||
$edit = array();
|
||||
$edit["title"] = $title;
|
||||
$edit[$body_key] = $body;
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), 'Translation created.');
|
||||
|
||||
// Check to make sure that translation was successful.
|
||||
$translation = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertTrue($translation, 'Node found in database.');
|
||||
$this->assertTrue($translation->tnid == $node->nid, 'Translation set id correctly stored.');
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests copying of source node's body value in the add translation form page.
|
||||
*/
|
||||
public function testUpgradeContentToEntityTranslation() {
|
||||
// Create Basic page in English.
|
||||
$node_title = $this->randomName();
|
||||
$node_body = $this->randomName();
|
||||
$node = $this->createContentTranslationPage($node_title, $node_body, 'en');
|
||||
|
||||
// Submit translation in Spanish.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$node_translation_title = $this->randomName();
|
||||
$node_translation_body = $this->randomName();
|
||||
$node_translation = $this->createContentTranslationTranslation($node, $node_translation_title, $node_translation_body, 'es');
|
||||
|
||||
// Make Body field translatable before we run the upgrade.
|
||||
$this->login($this->getAdminUser());
|
||||
$this->makeBodyFieldTranslatable();
|
||||
|
||||
// Run the upgrade for all Page nodes.
|
||||
$edit = array(
|
||||
'types[page]' => 'page',
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Upgrade'));
|
||||
|
||||
// Switch to our translator user.
|
||||
$this->login($this->getTranslatorUser());
|
||||
|
||||
// Check that the unpublished target node triggers a redirect.
|
||||
$this->drupalGet('node/' . $node_translation->nid);
|
||||
$headers = $this->drupalGetHeaders(TRUE);
|
||||
list(, $status) = explode(' ', $headers[0][':status'], 3);
|
||||
$this->assertEqual($status, 301, 'Expected response code was sent.');
|
||||
$languages = language_list();
|
||||
$this->assertEqual($this->getUrl(), url('node/' . $node->nid, array('absolute' => TRUE, 'language' => $languages['es'])), 'entity_translation_upgrade_redirect() redirected to expected URL.');
|
||||
|
||||
// Check that the body is displayed when the active language is English.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->assertRaw($node_body, t('Body field displayed correctly in the source language.'));
|
||||
|
||||
// Check that the translated body is displayed when the active language is Spanish.
|
||||
$this->drupalGet('es/node/' . $node->nid);
|
||||
$this->assertRaw($node_translation_body, t('Body field displayed correctly in the target language.'));
|
||||
|
||||
// Check that the edit forms are initialized correctly in the target language.
|
||||
$this->drupalGet('node/' . $node->nid . '/edit');
|
||||
$this->assertFieldByXPath("//textarea[@name='body[en][0][value]']", $node_body, "Body field correctly instantiated with the value of the source language.");
|
||||
$this->drupalGet('es/node/' . $node->nid . '/edit');
|
||||
$this->assertFieldByXPath("//textarea[@name='body[es][0][value]']", $node_translation_body, "Body field correctly instantiated with the value of the target language.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+425
-128
@@ -14,6 +14,21 @@
|
||||
*/
|
||||
interface EntityTranslationHandlerInterface {
|
||||
|
||||
/**
|
||||
* Injects the translation handler factory.
|
||||
*/
|
||||
public function setFactory(EntityTranslationHandlerFactory $factory);
|
||||
|
||||
/**
|
||||
* Registers a child translation handler for the given entity.
|
||||
*/
|
||||
public function addChild($entity_type, $entity);
|
||||
|
||||
/**
|
||||
* Removes a previously registered child translation handler.
|
||||
*/
|
||||
public function removeChild($entity_type, $entity);
|
||||
|
||||
/**
|
||||
* Loads the translation data into the wrapped entity.
|
||||
*/
|
||||
@@ -62,6 +77,11 @@ interface EntityTranslationHandlerInterface {
|
||||
*/
|
||||
public function removeTranslations();
|
||||
|
||||
/**
|
||||
* Removes all translations from the current revision.
|
||||
*/
|
||||
public function removeRevisionTranslations();
|
||||
|
||||
/**
|
||||
* Initialize the language of the original field values.
|
||||
*
|
||||
@@ -78,6 +98,62 @@ interface EntityTranslationHandlerInterface {
|
||||
*/
|
||||
public function getLanguage();
|
||||
|
||||
/**
|
||||
* Sets the active language.
|
||||
*
|
||||
* This is the language that determines which translation should be considered
|
||||
* "active" for the wrapped entity. The "Entity Translation" module uses this
|
||||
* information to implement the UI-based translation workflow. Other modules
|
||||
* can rely on it to implement their own entity translation-related logic.
|
||||
*
|
||||
* This will affect which language is returned by the core "entity_language()"
|
||||
* function.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The active language code.
|
||||
*
|
||||
* @see entity_language()
|
||||
* @see entity_translation_language()
|
||||
* @see ::getActiveLanguage()
|
||||
*/
|
||||
public function setActiveLanguage($langcode);
|
||||
|
||||
/**
|
||||
* Returns the active language.
|
||||
*
|
||||
* @return string
|
||||
* The active language for the wrapped entity.
|
||||
*
|
||||
* @see ::setActiveLanguage()
|
||||
*/
|
||||
public function getActiveLanguage();
|
||||
|
||||
/**
|
||||
* Sets the active form language.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The active form language code.
|
||||
*
|
||||
* @deprecated in 7.x-1.0-beta6, will be removed before 7.x-1.0. Use
|
||||
* ::setActiveLanguage() instead.
|
||||
*
|
||||
* @see ::setActiveLanguage()
|
||||
*/
|
||||
public function setFormLanguage($langcode);
|
||||
|
||||
/**
|
||||
* Retrieves the active form language.
|
||||
*
|
||||
* @return string
|
||||
* The active form language code.
|
||||
*
|
||||
* @deprecated in 7.x-1.0-beta6, will be removed before 7.x-1.0. Use
|
||||
* ::getActiveLanguage() instead.
|
||||
*
|
||||
* @see ::getActiveLanguage()
|
||||
*/
|
||||
public function getFormLanguage();
|
||||
|
||||
/**
|
||||
* Returns the translation object key for the wrapped entity type.
|
||||
*/
|
||||
@@ -89,7 +165,7 @@ interface EntityTranslationHandlerInterface {
|
||||
public function getDefaultLanguage();
|
||||
|
||||
/**
|
||||
* Sets the language of the orginal translation.
|
||||
* Sets the language of the original translation.
|
||||
*
|
||||
* @param $langcode
|
||||
* The language code of the original content values.
|
||||
@@ -98,6 +174,9 @@ interface EntityTranslationHandlerInterface {
|
||||
|
||||
/**
|
||||
* Returns TRUE if the entity is currently being translated.
|
||||
*
|
||||
* @deprecated This is no longer used and will be removed before the first
|
||||
* stable release.
|
||||
*/
|
||||
public function isTranslating();
|
||||
|
||||
@@ -106,6 +185,9 @@ interface EntityTranslationHandlerInterface {
|
||||
*
|
||||
* @param $translating
|
||||
* A boolean value.
|
||||
*
|
||||
* @deprecated This is no longer used and will be removed before the first
|
||||
* stable release.
|
||||
*/
|
||||
public function setTranslating($translating);
|
||||
|
||||
@@ -114,6 +196,11 @@ interface EntityTranslationHandlerInterface {
|
||||
*/
|
||||
public function isRevision();
|
||||
|
||||
/**
|
||||
* Return TRUE if the entity type supports revisions.
|
||||
*/
|
||||
public function isRevisionable();
|
||||
|
||||
/**
|
||||
* Replaces the wrapped entity.
|
||||
*
|
||||
@@ -122,6 +209,22 @@ interface EntityTranslationHandlerInterface {
|
||||
*/
|
||||
public function setEntity($entity);
|
||||
|
||||
/**
|
||||
* Returns the wrapped entity.
|
||||
*
|
||||
* @param return
|
||||
* The wrapped entity.
|
||||
*/
|
||||
public function getEntity();
|
||||
|
||||
/**
|
||||
* Returns the wrapped entity type.
|
||||
*
|
||||
* @param return
|
||||
* The wrapped entity type.
|
||||
*/
|
||||
public function getEntityType();
|
||||
|
||||
/**
|
||||
* Checks that the wrapped entity matches the give entity
|
||||
*
|
||||
@@ -225,16 +328,6 @@ interface EntityTranslationHandlerInterface {
|
||||
*/
|
||||
public function isAliasEnabled();
|
||||
|
||||
/**
|
||||
* Sets the active form language.
|
||||
*/
|
||||
public function setFormLanguage($langcode);
|
||||
|
||||
/**
|
||||
* Retrieves the active form language.
|
||||
*/
|
||||
public function getFormLanguage();
|
||||
|
||||
/**
|
||||
* Sets the source language for the translation being created.
|
||||
*/
|
||||
@@ -301,20 +394,35 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
protected $entityInfo;
|
||||
protected $entityId;
|
||||
protected $bundle;
|
||||
protected $revisionable;
|
||||
|
||||
private $entityForm;
|
||||
private $translating;
|
||||
private $outdated;
|
||||
private $formLanguage;
|
||||
private $sourceLanguage;
|
||||
/**
|
||||
* The translation handler factory.
|
||||
*
|
||||
* @var EntityTranslationHandlerFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
private $pathScheme;
|
||||
private $pathWildcard;
|
||||
private $basePath;
|
||||
private $editPath;
|
||||
private $translatePath;
|
||||
private $viewPath;
|
||||
private $routerMap;
|
||||
/**
|
||||
* The translation handler hierarchy storage.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $children = array();
|
||||
|
||||
protected $entityForm;
|
||||
protected $translating;
|
||||
protected $outdated;
|
||||
protected $activeLanguage;
|
||||
protected $sourceLanguage;
|
||||
|
||||
protected $pathScheme;
|
||||
protected $pathWildcard;
|
||||
protected $basePath;
|
||||
protected $editPath;
|
||||
protected $translatePath;
|
||||
protected $viewPath;
|
||||
protected $routerMap;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the translation handler.
|
||||
@@ -334,7 +442,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
$this->entityForm = FALSE;
|
||||
$this->translating = FALSE;
|
||||
$this->outdated = FALSE;
|
||||
$this->formLanguage = FALSE;
|
||||
$this->activeLanguage = FALSE;
|
||||
$this->sourceLanguage = FALSE;
|
||||
$this->pathScheme = 'default';
|
||||
$this->routerMap = array();
|
||||
@@ -357,17 +465,29 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
return;
|
||||
}
|
||||
|
||||
$revisionable = self::isEntityTypeRevisionable($entity_type);
|
||||
$revisions_ids = array();
|
||||
foreach ($entities as $id => $entity) {
|
||||
$entities[$id]->{$translations_key} = self::emptyTranslations();
|
||||
if ($revisionable) {
|
||||
list(, $revisions_id,) = entity_extract_ids($entity_type, $entity);
|
||||
$revisions_ids[$id] = $revisions_id;
|
||||
}
|
||||
}
|
||||
|
||||
$results = db_select('entity_translation', 'et')
|
||||
$table = $revisionable ? 'entity_translation_revision' : 'entity_translation';
|
||||
$query = db_select($table, 'et')
|
||||
->fields('et')
|
||||
->condition('entity_type', $entity_type)
|
||||
->condition('entity_id', array_keys($entities), 'IN')
|
||||
->orderBy('entity_id')
|
||||
->orderBy('created')
|
||||
->execute();
|
||||
->condition('entity_type', $entity_type);
|
||||
|
||||
if (!$revisionable) {
|
||||
$query->condition('entity_id', array_keys($entities), 'IN');
|
||||
}
|
||||
else {
|
||||
$query->condition('revision_id', $revisions_ids, 'IN');
|
||||
}
|
||||
|
||||
$results = $query->execute();
|
||||
|
||||
foreach ($results as $row) {
|
||||
$id = $row->entity_id;
|
||||
@@ -394,6 +514,46 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::setFactory()
|
||||
*/
|
||||
public function setFactory(EntityTranslationHandlerFactory $factory) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::addChild()
|
||||
*/
|
||||
public function addChild($entity_type, $entity) {
|
||||
if (!empty($this->factory)) {
|
||||
$handler = $this->factory->getHandler($entity_type, $entity);
|
||||
$handler->setActiveLanguage($this->getActiveLanguage());
|
||||
$handler->setSourceLanguage($this->getSourceLanguage());
|
||||
// Avoid registering more than one child handler for each entity.
|
||||
$hid = $this->factory->getHandlerId($entity_type, $entity);
|
||||
$this->children[$hid] = $handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::removeChild()
|
||||
*/
|
||||
public function removeChild($entity_type, $entity) {
|
||||
if (!empty($this->factory)) {
|
||||
$hid = $this->factory->getHandlerId($entity_type, $entity);
|
||||
unset($this->children[$hid]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxies the specified method invocation to a child translation handler.
|
||||
*/
|
||||
protected function notifyChildren($method, $args) {
|
||||
foreach ($this->children as $handler) {
|
||||
call_user_func_array(array($handler, $method), $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::loadTranslations()
|
||||
*/
|
||||
@@ -410,43 +570,14 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::saveTranslations()
|
||||
*/
|
||||
public function saveTranslations() {
|
||||
// Delete and insert, rather than update, in case a value was added.
|
||||
db_delete('entity_translation')
|
||||
->condition('entity_type', $this->entityType)
|
||||
->condition('entity_id', $this->entityId)
|
||||
->execute();
|
||||
|
||||
$translations = $this->getTranslations();
|
||||
|
||||
if (count($translations->data)) {
|
||||
global $user;
|
||||
// Save current values.
|
||||
$this->doSaveTranslations($translations, 'entity_translation');
|
||||
|
||||
$columns = array('entity_type', 'entity_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed');
|
||||
$query = db_insert('entity_translation')->fields($columns);
|
||||
|
||||
// These values should overridde the translation ones as they are not
|
||||
// supposed to change.
|
||||
$overrides = array(
|
||||
'entity_id' => $this->entityId,
|
||||
'entity_type' => $this->entityType,
|
||||
);
|
||||
|
||||
// These instead are just defaults.
|
||||
$defaults = array(
|
||||
'source' => '',
|
||||
'uid' => $user->uid,
|
||||
'translate' => 0,
|
||||
'status' => 0,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
);
|
||||
|
||||
foreach ($translations->data as $langcode => $translation) {
|
||||
$translation = $overrides + $translation + $defaults;
|
||||
$query->values($translation);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
// Save revision values.
|
||||
if ($this->isRevisionable()) {
|
||||
$this->doSaveTranslations($translations, 'entity_translation_revision', TRUE);
|
||||
}
|
||||
|
||||
// The translation handler interface decouples operations on translations at
|
||||
@@ -476,6 +607,54 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves entity translation records to the storage.
|
||||
*/
|
||||
protected function doSaveTranslations($translations, $table, $revision = FALSE) {
|
||||
// Delete and insert, rather than update, in case a value was added.
|
||||
$query = db_delete($table)
|
||||
->condition('entity_type', $this->entityType)
|
||||
->condition('entity_id', $this->entityId);
|
||||
// If we are storing translations for the current revision or we are
|
||||
// deleting the entity we should remove all translation data.
|
||||
$langcode = $translations->original;
|
||||
$hook = isset($translations->hook) ? $translations->hook : array();
|
||||
if ($revision && $this->isRevisionable() && (empty($hook[$langcode]['hook']) || $hook[$langcode]['hook'] != 'delete')) {
|
||||
$query->condition('revision_id', $this->revisionId);
|
||||
}
|
||||
$query->execute();
|
||||
|
||||
if (count($translations->data)) {
|
||||
$columns = array('entity_type', 'entity_id', 'revision_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed');
|
||||
$query = db_insert($table)->fields($columns);
|
||||
|
||||
// These values should override the translation ones as they are not
|
||||
// supposed to change.
|
||||
$overrides = array(
|
||||
'entity_type' => $this->entityType,
|
||||
'entity_id' => $this->entityId,
|
||||
'revision_id' => $this->isRevisionable() ? $this->revisionId : $this->entityId,
|
||||
);
|
||||
|
||||
// These instead are just defaults.
|
||||
$defaults = array(
|
||||
'source' => '',
|
||||
'uid' => $GLOBALS['user']->uid,
|
||||
'translate' => 0,
|
||||
'status' => 0,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
);
|
||||
|
||||
foreach ($translations->data as $translation) {
|
||||
$translation = $overrides + $translation + $defaults;
|
||||
$query->values($translation);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::getTranslations()
|
||||
*/
|
||||
@@ -495,6 +674,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::setTranslation()
|
||||
*/
|
||||
public function setTranslation($translation, $values = NULL) {
|
||||
$args = func_get_args();
|
||||
if (isset($translation['source']) && $translation['language'] == $translation['source']) {
|
||||
throw new Exception('Invalid translation language');
|
||||
}
|
||||
@@ -525,11 +705,13 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
foreach (field_info_instances($this->entityType, $this->bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
if ($field['translatable'] && isset($values[$field_name])) {
|
||||
if ($field['translatable'] && isset($values[$field_name][$langcode])) {
|
||||
$this->entity->{$field_name}[$langcode] = $values[$field_name][$langcode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->notifyChildren(__FUNCTION__, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -600,7 +782,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
if (empty($this->getTranslations()->data)) {
|
||||
$this->initTranslations();
|
||||
}
|
||||
elseif (!empty($langcode) && !$this->isTranslating()) {
|
||||
elseif (!empty($langcode)) {
|
||||
$this->setOriginalLanguage($langcode);
|
||||
}
|
||||
}
|
||||
@@ -612,6 +794,17 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
$this->removeTranslation(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::removeRevisionTranslations()
|
||||
*/
|
||||
public function removeRevisionTranslations() {
|
||||
$translations_key = $this->getTranslationsKey();
|
||||
$keys = array_keys($this->entity->{$translations_key}->data);
|
||||
$values = array_fill(0, count($keys), array('hook' => 'delete_revision'));
|
||||
$this->removeTranslation(NULL);
|
||||
$this->entity->{$translations_key}->hook = array_combine($keys, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::initOriginalTranslation()
|
||||
*/
|
||||
@@ -656,6 +849,55 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setActiveLanguage($langcode) {
|
||||
// @todo To fully preserve BC, we proxy the call to the deprecated
|
||||
// ::setFormLanguage method. This will keep things working even when it
|
||||
// has been overridden. Inline its implementation here upon removal.
|
||||
$this->setFormLanguage($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getActiveLanguage() {
|
||||
// @todo To fully preserve BC, we proxy the call to the deprecated
|
||||
// ::getFormLanguage method. This will keep things working even when it
|
||||
// has been overridden. Inline its implementation here upon removal.
|
||||
return $this->getFormLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFormLanguage($langcode) {
|
||||
$args = func_get_args();
|
||||
$this->activeLanguage = $langcode;
|
||||
$this->notifyChildren(__FUNCTION__, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormLanguage() {
|
||||
if (!empty($this->activeLanguage)) {
|
||||
return $this->activeLanguage;
|
||||
}
|
||||
// For new entities the active language should match the default language.
|
||||
// The language stored with the entity itself (for example, $node->language)
|
||||
// may not be reliable since the function creating the entity object will
|
||||
// not know which language "Entity Translation" is configured to create the
|
||||
// entity in.
|
||||
elseif ($this->isNewEntity()) {
|
||||
return $this->getDefaultLanguage();
|
||||
}
|
||||
else {
|
||||
return $this->getLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::getLanguageKey()
|
||||
*/
|
||||
@@ -700,15 +942,19 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::setOriginalLanguage()
|
||||
*/
|
||||
public function setOriginalLanguage($langcode) {
|
||||
$args = func_get_args();
|
||||
$translations = $this->getTranslations();
|
||||
|
||||
if (isset($translations->original) && $translations->original != $langcode) {
|
||||
$translations->data[$langcode] = $translations->data[$translations->original];
|
||||
$translations->data[$langcode]['language'] = $langcode;
|
||||
unset($translations->data[$translations->original]);
|
||||
}
|
||||
if (!isset($translations->original) || $translations->original != $langcode) {
|
||||
if (isset($translations->original)) {
|
||||
$translations->data[$langcode] = $translations->data[$translations->original];
|
||||
$translations->data[$langcode]['language'] = $langcode;
|
||||
unset($translations->data[$translations->original]);
|
||||
}
|
||||
|
||||
$translations->original = $langcode;
|
||||
$translations->original = $langcode;
|
||||
$this->notifyChildren(__FUNCTION__, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,6 +978,25 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::isRevisionable()
|
||||
*/
|
||||
public function isRevisionable() {
|
||||
$result = FALSE;
|
||||
if (!isset($this->revisionable)) {
|
||||
$result = self::isEntityTypeRevisionable($this->entityType);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the entity type is revisionable.
|
||||
*/
|
||||
public static function isEntityTypeRevisionable($entity_type) {
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
return variable_get('entity_translation_revision_enabled', FALSE) && !empty($entity_info['entity keys']['revision']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::setEntity()
|
||||
*/
|
||||
@@ -744,8 +1009,27 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
$this->entity->{$translations_key} = self::emptyTranslations();
|
||||
}
|
||||
|
||||
// Update bundle and entity id properties.
|
||||
list($this->entityId, , $this->bundle) = entity_extract_ids($this->entityType, $this->entity);
|
||||
// Update entity properties.
|
||||
list($this->entityId, $this->revisionId, $this->bundle) = entity_extract_ids($this->entityType, $this->entity);
|
||||
|
||||
// Initialize the handler id if needed.
|
||||
if (!empty($this->factory)) {
|
||||
$this->factory->getHandlerId($this->entityType, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::getEntity()
|
||||
*/
|
||||
public function getEntity() {
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::getEntityType()
|
||||
*/
|
||||
public function getEntityType() {
|
||||
return $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -760,13 +1044,15 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::setOutdated()
|
||||
*/
|
||||
public function setOutdated($outdated) {
|
||||
$args = func_get_args();
|
||||
if ($outdated) {
|
||||
$translations = $this->getTranslations();
|
||||
foreach ($translations->data as $langcode => &$translation) {
|
||||
if ($langcode != $this->getFormLanguage()) {
|
||||
if ($langcode != $this->getActiveLanguage()) {
|
||||
$translation['translate'] = 1;
|
||||
}
|
||||
}
|
||||
$this->notifyChildren(__FUNCTION__, $args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,7 +1192,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
*/
|
||||
public function getSharedFieldsAccess() {
|
||||
$settings = entity_translation_settings($this->entityType, $this->bundle);
|
||||
return ($settings['shared_fields_original_only'] == FALSE || $this->getLanguage() == $this->getFormLanguage()) &&
|
||||
return ($settings['shared_fields_original_only'] == FALSE || $this->getLanguage() == $this->getActiveLanguage()) &&
|
||||
(!entity_translation_workflow_enabled() || user_access('edit translation shared fields') || user_access("edit {$this->entityType} translation shared fields"));
|
||||
}
|
||||
|
||||
@@ -917,25 +1203,13 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
return !empty($this->entityInfo['translation']['entity_translation']['alias']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::setFormLanguage()
|
||||
*/
|
||||
public function setFormLanguage($langcode) {
|
||||
$this->formLanguage = $langcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::getFormLanguage()
|
||||
*/
|
||||
public function getFormLanguage() {
|
||||
return !empty($this->formLanguage) ? $this->formLanguage : $this->getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::setSourceLanguage()
|
||||
*/
|
||||
public function setSourceLanguage($langcode) {
|
||||
$args = func_get_args();
|
||||
$this->sourceLanguage = $langcode;
|
||||
$this->notifyChildren(__FUNCTION__, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +1240,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
public function entityForm(&$form, &$form_state) {
|
||||
$this->entityForm = TRUE;
|
||||
$translations = $this->getTranslations();
|
||||
$form_langcode = $this->getFormLanguage();
|
||||
$form_langcode = $this->getActiveLanguage();
|
||||
$langcode = $this->getLanguage();
|
||||
$is_translation = $this->isTranslationForm();
|
||||
$new_translation = !isset($translations->data[$form_langcode]);
|
||||
@@ -974,12 +1248,16 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
$languages = language_list();
|
||||
$access = user_access('translate any entity') || user_access("translate $this->entityType entities");
|
||||
|
||||
// Store contextual information in the form state.
|
||||
$form_state['entity_translation']['form_langcode'] = $form_langcode;
|
||||
$form_state['entity_translation']['source_langcode'] = $this->getSourceLanguage();
|
||||
// The only way to determine whether we are editing the original values is
|
||||
// comparing form language and entity language. Since a language change
|
||||
// might render impossible to make this check after form submission, we
|
||||
// store the related information here.
|
||||
$form_state['entity_translation']['is_translation'] = $is_translation;
|
||||
|
||||
|
||||
// Adjust page title to specify the current language being edited, if we
|
||||
// have at least one translation.
|
||||
if ($form_langcode != LANGUAGE_NONE && (!$no_translations || $new_translation)) {
|
||||
@@ -1019,7 +1297,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
// Add the entity language switcher.
|
||||
$this->entityFormLanguageWidget($form, $form_state);
|
||||
|
||||
if ($is_translation) {
|
||||
if ($is_translation && isset($form['actions']['delete'])) {
|
||||
// Replace the delete button with the delete translation one.
|
||||
if (!$new_translation) {
|
||||
$weight = 100;
|
||||
@@ -1082,7 +1360,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
'#disabled' => !$enabled,
|
||||
);
|
||||
|
||||
$translate = !$new_translation && $translations->data[$form_langcode]['translate'];
|
||||
$translate = !$new_translation && !empty($translations->data[$form_langcode]['translate']);
|
||||
if (!$translate) {
|
||||
$form['translation']['retranslate'] = array(
|
||||
'#type' => 'checkbox',
|
||||
@@ -1101,7 +1379,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
);
|
||||
}
|
||||
|
||||
$name = $new_translation ? $GLOBALS['user']->name : user_load($translations->data[$form_langcode]['uid'])->name;
|
||||
$translation_author = $new_translation ? $GLOBALS['user'] : user_load($translations->data[$form_langcode]['uid']);
|
||||
$name = isset($translation_author->name) ? $translation_author->name : '';
|
||||
$form['translation']['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Authored by'),
|
||||
@@ -1142,8 +1421,9 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* Either remove access or add a translatability clue depending on the current
|
||||
* user's "edit translation shared fields" permissions.
|
||||
*/
|
||||
public function entityFormSharedElements(&$element) {
|
||||
static $ignored_types, $shared_labels, $access;
|
||||
public function entityFormSharedElements(&$element, $access = NULL) {
|
||||
static $ignored_types, $shared_labels;
|
||||
|
||||
if (!isset($ignored_types)) {
|
||||
$ignored_types = array_flip(array('actions', 'value', 'hidden', 'vertical_tabs', 'token'));
|
||||
}
|
||||
@@ -1156,7 +1436,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
|
||||
foreach (element_children($element) as $key) {
|
||||
if (!isset($element[$key]['#type'])) {
|
||||
$this->entityFormSharedElements($element[$key]);
|
||||
$this->entityFormSharedElements($element[$key], $access);
|
||||
}
|
||||
else {
|
||||
// Ignore non-widget form elements.
|
||||
@@ -1183,7 +1463,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
public function entityFormLanguageWidget(&$form, &$form_state) {
|
||||
if (entity_translation_enabled($this->entityType, $this->bundle)) {
|
||||
$is_new = $this->isNewEntity();
|
||||
$is_translation = !$is_new && !empty($form_state['entity_translation']['is_translation']);
|
||||
$is_translation = !empty($form_state['entity_translation']['is_translation']);
|
||||
$translations = $this->getTranslations();
|
||||
$settings = entity_translation_settings($this->entityType, $this->bundle);
|
||||
$languages = entity_translation_languages($this->entityType, $this->entity);
|
||||
@@ -1191,8 +1471,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
|
||||
foreach ($languages as $langcode => $language) {
|
||||
// Disable languages for existing translations, so it is not possible to
|
||||
// switch this entity to some language which is already in the translation
|
||||
// set.
|
||||
// switch this entity to some language which is already in the
|
||||
// translation set.
|
||||
if (!isset($translations->data[$langcode]) || empty($translations->data[$langcode]['source'])) {
|
||||
$options[$langcode] = t($language->name);
|
||||
}
|
||||
@@ -1217,17 +1497,14 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($form['actions']['submit']['#submit'])) {
|
||||
$submit = &$form['actions']['submit']['#submit'];
|
||||
}
|
||||
else {
|
||||
if (!isset($form['#submit'])) {
|
||||
$form['#submit'] = array();
|
||||
}
|
||||
$submit = &$form['#submit'];
|
||||
}
|
||||
|
||||
array_unshift($submit, 'entity_translation_language_widget_submit');
|
||||
// Prepend an empty form element to the form array so that we can update the
|
||||
// form language before any other form handler has been called.
|
||||
$form = array(
|
||||
'entity_translation_entity_form_language_update' => array(
|
||||
'#element_validate' => array('entity_translation_entity_form_language_update'),
|
||||
'#entity_type' => $this->entityType,
|
||||
),
|
||||
) + $form;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1241,6 +1518,15 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::entityFormValidate()
|
||||
*/
|
||||
public function entityFormValidate($form, &$form_state) {
|
||||
// The locale module is creating the node object anew based on the form's
|
||||
// submitted values, thus losing the relation with the currently assigned
|
||||
// translation handler. By adding the 'entity_translation_handler_id' property to
|
||||
// the $form_state['values'] array, we can persist that relation and utilize
|
||||
// a correctly instantiated translation handler for the new node entity.
|
||||
if (empty($form_state['values']['entity_translation_handler_id']) && field_has_translation_handler($this->getEntityType(), 'locale')) {
|
||||
$form_state['values']['entity_translation_handler_id'] = $form_state[$this->getEntityType()]->entity_translation_handler_id;
|
||||
}
|
||||
|
||||
if (!empty($form_state['values']['translation'])) {
|
||||
$values = $form_state['values']['translation'];
|
||||
// Validate the "authored by" field.
|
||||
@@ -1260,28 +1546,36 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
protected function updateFormLanguage($form_state) {
|
||||
// Update the form language as it might have changed. We exploit the
|
||||
// validation phase to be sure to act as early as possible.
|
||||
if (isset($form_state['values']['language']) && !$this->isTranslationForm()) {
|
||||
$this->setFormLanguage($form_state['values'][$this->getLanguageKey()]);
|
||||
$language_key = $this->getLanguageKey();
|
||||
if (isset($form_state['values'][$language_key]) && !$this->isTranslationForm()) {
|
||||
$langcode = $form_state['values'][$language_key];
|
||||
$this->setActiveLanguage($langcode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationHandlerInterface::entityFormLanguageWidgetSubmit()
|
||||
*/
|
||||
function entityFormLanguageWidgetSubmit($form, &$form_state) {
|
||||
public function entityFormLanguageWidgetSubmit($form, &$form_state) {
|
||||
if (!entity_translation_enabled($this->entityType, $this->bundle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->updateFormLanguage($form_state);
|
||||
$form_langcode = $this->getFormLanguage();
|
||||
$form_langcode = $this->getActiveLanguage();
|
||||
|
||||
foreach (field_info_instances($this->entityType, $this->bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
$previous_langcode = $form[$field_name]['#language'];
|
||||
if (isset($form[$field_name]['#language'])) {
|
||||
$field = field_info_field($field_name);
|
||||
$previous_langcode = $form[$field_name]['#language'];
|
||||
|
||||
// Handle a possible language change: new language values are inserted,
|
||||
// previous ones are deleted.
|
||||
if ($field['translatable'] && $previous_langcode != $form_langcode) {
|
||||
$form_state['values'][$field_name][$form_langcode] = $form_state['values'][$field_name][$previous_langcode];
|
||||
$form_state['values'][$field_name][$previous_langcode] = array();
|
||||
// Handle a possible language change: new language values are inserted,
|
||||
// previous ones are deleted.
|
||||
if ($field['translatable'] && $previous_langcode != $form_langcode && isset($form_state['values'][$field_name][$previous_langcode])) {
|
||||
$form_state['values'][$field_name][$form_langcode] = $form_state['values'][$field_name][$previous_langcode];
|
||||
$form_state['values'][$field_name][$previous_langcode] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1290,7 +1584,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
* @see EntityTranslationHandlerInterface::entityFormSubmit()
|
||||
*/
|
||||
public function entityFormSubmit($form, &$form_state) {
|
||||
$form_langcode = $this->getFormLanguage();
|
||||
$form_langcode = $this->getActiveLanguage();
|
||||
$translations = $this->getTranslations();
|
||||
$is_translation = !empty($form_state['entity_translation']['is_translation']);
|
||||
$new_translation = !isset($translations->data[$form_langcode]);
|
||||
@@ -1345,7 +1639,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
|
||||
if (count($translations->data) > 0) {
|
||||
$languages = language_list();
|
||||
$form_langcode = $this->getFormLanguage();
|
||||
$form_langcode = $this->getActiveLanguage();
|
||||
$language_tabs = array();
|
||||
|
||||
if ($this->getSourceLanguage()) {
|
||||
@@ -1406,16 +1700,19 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
|
||||
/**
|
||||
* Returns the title to be used for the entity form page.
|
||||
*
|
||||
* This may contain HTML markup and thus needs to be sanitized, since it will
|
||||
* be used as page title as-is.
|
||||
*/
|
||||
protected function entityFormTitle() {
|
||||
return $this->getLabel();
|
||||
return check_plain($this->getLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if an entity translation is being edited.
|
||||
*/
|
||||
protected function isTranslationForm() {
|
||||
return !$this->isNewEntity() && $this->getFormLanguage() != $this->getLanguage();
|
||||
return !$this->isNewEntity() && $this->getActiveLanguage() != $this->getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1444,7 +1741,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function initPathVariables() {
|
||||
protected function initPathVariables() {
|
||||
if (empty($this->pathScheme) || !isset($this->entityInfo['translation']['entity_translation']['path schemes'][$this->pathScheme])) {
|
||||
throw new Exception("Cannot initialize entity translation path variables (invalid path scheme).");
|
||||
}
|
||||
@@ -1473,7 +1770,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
|
||||
if ($segment == $this->pathWildcard) {
|
||||
$path_segments[$index] = $this->getEntityId();
|
||||
}
|
||||
elseif ($segment{0} == '%' && isset($this->routerMap[$index])) {
|
||||
elseif ($segment[0] == '%' && isset($this->routerMap[$index])) {
|
||||
$path_segments[$index] = $this->routerMap[$index];
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -92,7 +92,9 @@ class EntityTranslationNodeHandler extends EntityTranslationDefaultHandler {
|
||||
|
||||
if (!$this->isTranslationForm()) {
|
||||
// Inherit entity authoring information for the original values.
|
||||
$values['name'] = $form_state['values']['name'];
|
||||
if (isset($form_state['values']['name'])) {
|
||||
$values['name'] = $form_state['values']['name'];
|
||||
}
|
||||
if (!empty($form_state['values']['date'])) {
|
||||
$values['created'] = $form_state['values']['date'];
|
||||
}
|
||||
|
||||
+19
@@ -15,6 +15,24 @@ class EntityTranslationTaxonomyTermHandler extends EntityTranslationDefaultHandl
|
||||
parent::__construct('taxonomy_term', $entity_info, $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationDefaultHandler::getLanguage()
|
||||
*/
|
||||
public function getLanguage() {
|
||||
if (isset($this->entity->vid) && module_exists('i18n_taxonomy')) {
|
||||
$mode = i18n_taxonomy_vocabulary_mode($this->entity->vid);
|
||||
// We support also terms having no translation enabled, since they can
|
||||
// just be language-aware.
|
||||
if ($mode == I18N_MODE_NONE || $mode == I18N_MODE_ENTITY_TRANSLATION) {
|
||||
$translations = $this->getTranslations();
|
||||
if (!empty($translations->original)) {
|
||||
return $translations->original;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parent::getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EntityTranslationDefaultHandler::entityForm()
|
||||
*/
|
||||
@@ -32,4 +50,5 @@ class EntityTranslationTaxonomyTermHandler extends EntityTranslationDefaultHandl
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Translation handler factory for the Entity Translation module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class implementing the entity translation handler factory.
|
||||
*/
|
||||
class EntityTranslationHandlerFactory {
|
||||
|
||||
/**
|
||||
* A singleton instance of the factory.
|
||||
*
|
||||
* @var EntityTranslationHandlerFactory
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Counter used to generate handler ids for new entities.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $newId = 1;
|
||||
|
||||
/**
|
||||
* Handlers cache.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $handlers = array();
|
||||
|
||||
/**
|
||||
* The last translation handler retrieved.
|
||||
*
|
||||
* @var EntityTranslationHandlerInterface
|
||||
*/
|
||||
protected $last;
|
||||
|
||||
/**
|
||||
* An array of translation handler retrieved by type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $lastByType = array();
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of the translation handler factory.
|
||||
*
|
||||
* @return EntityTranslationHandlerFactory
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (!isset(self::$instance)) {
|
||||
self::$instance = new EntityTranslationHandlerFactory();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the factory from being publicly instantiated.
|
||||
*/
|
||||
protected function __construct() {}
|
||||
|
||||
/**
|
||||
* Translation handler factory.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity; e.g. 'node' or 'user'.
|
||||
* @param $entity
|
||||
* The entity to be translated. A bundle name may be passed to instantiate
|
||||
* an empty entity.
|
||||
*
|
||||
* @return EntityTranslationHandlerInterface
|
||||
* A class implementing EntityTranslationHandlerInterface.
|
||||
*/
|
||||
public function getHandler($entity_type, $entity) {
|
||||
if (is_numeric($entity)) {
|
||||
$entities = entity_load($entity_type, array($entity));
|
||||
$entity = reset($entities);
|
||||
}
|
||||
elseif (is_string($entity)) {
|
||||
$entity = entity_create_stub_entity($entity_type, array(NULL, NULL, $entity));
|
||||
}
|
||||
|
||||
$id = $this->getHandlerId($entity_type, $entity);
|
||||
if (!isset($this->handlers[$entity_type][$id])) {
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$class = $entity_info['translation']['entity_translation']['class'];
|
||||
$handler = new $class($entity_type, $entity_info, $entity);
|
||||
$handler->setFactory($this);
|
||||
$this->handlers[$entity_type][$id] = $handler;
|
||||
}
|
||||
|
||||
$this->last = $this->handlers[$entity_type][$id];
|
||||
$this->lastByType[$entity_type] = $this->last;
|
||||
$this->last->setEntity($entity);
|
||||
|
||||
return $this->last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the translation handler identifier for the given entity.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of the entity the translation handler will wrap.
|
||||
* @param $entity
|
||||
* The entity the translation handler will wrap.
|
||||
*/
|
||||
public function getHandlerId($entity_type, $entity) {
|
||||
if (!isset($entity->entity_translation_handler_id)) {
|
||||
list($id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$revision_id = isset($revision_id) ? $revision_id : 0;
|
||||
$bundle = isset($bundle) ? $bundle : $entity_type;
|
||||
$entity->entity_translation_handler_id = $entity_type . '-' . $bundle . '-' . (!empty($id) ? 'eid-' . $id . '-' . $revision_id : 'new-' . self::$newId++);
|
||||
}
|
||||
return $entity->entity_translation_handler_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last translation handler retrieved.
|
||||
*
|
||||
* @param $entity_type
|
||||
* (optional) The entity type of the translation handler. Defaults to the
|
||||
* last one.
|
||||
*
|
||||
* @return EntityTranslationHandlerInterface
|
||||
* A class implementing EntityTranslationHandlerInterface.
|
||||
*/
|
||||
public function getLastHandler($entity_type = NULL) {
|
||||
if (isset($entity_type)) {
|
||||
return isset($this->lastByType[$entity_type]) ? $this->lastByType[$entity_type] : NULL;
|
||||
}
|
||||
return $this->last;
|
||||
}
|
||||
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* MigrateTranslationEntityHandler class for entity_translation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Destination handler implementing translatable fields.
|
||||
*/
|
||||
class MigrateTranslationEntityHandler extends MigrateDestinationHandler {
|
||||
/**
|
||||
* Registers all entites as handled by this class.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('entity'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Entity Translations.
|
||||
*
|
||||
* @param stdClass $entity
|
||||
* @param stdClass $sourceRow
|
||||
*/
|
||||
public function prepare($entity, stdClass $row) {
|
||||
$migration = Migration::currentMigration();
|
||||
$entity_type = $migration->getDestination()->getEntityType();
|
||||
|
||||
// Only continue if the entity type + bundle combination is enabled for
|
||||
// entity_translation.
|
||||
if (entity_translation_enabled($entity_type, $entity)) {
|
||||
// Get the entity_translation handler and load any existing translations.
|
||||
$handler = entity_translation_get_handler($entity_type, $entity);
|
||||
$handler->loadTranslations();
|
||||
$original_language = $handler->getLanguage();
|
||||
|
||||
// Get the bundle of the entity.
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
// We need to get all of the possible translations to create. So we look
|
||||
// for any translatable fields.
|
||||
$translatable_fields = array();
|
||||
foreach (field_info_instances($entity_type, $bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
|
||||
if ($field['translatable']) {
|
||||
$translatable_fields[] = $field_name;
|
||||
}
|
||||
}
|
||||
|
||||
// In those fields we look for translated values to build out an array
|
||||
// of languages we need to set translations for.
|
||||
$translate_languages = array();
|
||||
foreach ($translatable_fields as $translatable_field) {
|
||||
if (!empty($entity->{$translatable_field})) {
|
||||
$field_languages = array_keys($entity->{$translatable_field});
|
||||
foreach ($field_languages as $field_language) {
|
||||
$translate_languages[$field_language] = $field_language;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the LANGUAGE_NONE results.
|
||||
unset($translate_languages[LANGUAGE_NONE]);
|
||||
|
||||
// Remove the original language.
|
||||
unset($translate_languages[$original_language]);
|
||||
|
||||
// Anything we're left with is a translation that should be set.
|
||||
foreach ($translate_languages as $translate_language) {
|
||||
if (!isset($entity->translations->data[$translate_language])) {
|
||||
// Add the new translation and store it.
|
||||
$handler->setTranslation(array(
|
||||
'translate' => 0,
|
||||
'status' => 1,
|
||||
'language' => $translate_language,
|
||||
'source' => $original_language,
|
||||
'uid' => (empty($entity->uid)) ? 0 : $entity->uid,
|
||||
'changed' => (empty($entity->changed)) ? REQUEST_TIME : $entity->changed,
|
||||
'created' => (empty($entity->created)) ? REQUEST_TIME : $entity->created,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Plugin to provide access control on entities based on translations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CTools Plugin definition.
|
||||
*/
|
||||
$plugin = array(
|
||||
'title' => t("Entity translation: translation exists"),
|
||||
'description' => t('Control access by checking if a translation exists.'),
|
||||
'callback' => 'entity_translation_translation_exists_ctools_access_check',
|
||||
'default' => array('language' => array()),
|
||||
'settings form' => 'entity_translation_translation_exists_ctools_access_settings',
|
||||
'settings form submit' => 'entity_translation_translation_exists_ctools_access_settings_submit',
|
||||
'summary' => 'entity_translation_translation_exists_ctools_access_summary',
|
||||
'get child' => 'entity_translation_translation_exists_ctools_access_get_child',
|
||||
'get children' => 'entity_translation_translation_exists_ctools_access_get_children',
|
||||
);
|
||||
|
||||
/**
|
||||
* Get a particular instance of this plugin.
|
||||
*/
|
||||
function entity_translation_translation_exists_ctools_access_get_child($plugin, $parent, $child) {
|
||||
|
||||
$plugins = &drupal_static(__FUNCTION__, array());
|
||||
if (empty($plugins[$parent . ':' . $child])) {
|
||||
$plugins[$parent . ':' . $child] = _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $child);
|
||||
}
|
||||
return $plugins[$parent . ':' . $child];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all children of this plugin.
|
||||
*/
|
||||
function entity_translation_translation_exists_ctools_access_get_children($plugin, $parent) {
|
||||
$plugins = &drupal_static(__FUNCTION__, array());
|
||||
if (!empty($plugins)) {
|
||||
return $plugins;
|
||||
}
|
||||
$entities = entity_get_info();
|
||||
foreach ($entities as $entity_type => $entity) {
|
||||
$plugin = _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $entity_type);
|
||||
$plugins[$parent . ':' . $entity_type] = $plugin;
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin definition for one particular plugin child.
|
||||
*/
|
||||
function _entity_translation_translation_exists_ctools_access_definition($plugin, $parent, $entity_type) {
|
||||
$entity = entity_get_info($entity_type);
|
||||
|
||||
$plugin['title'] = t('@entity: entity translation exists', array('@entity' => $entity['label']));
|
||||
$plugin['keyword'] = $entity_type;
|
||||
$plugin['description'] = t('Control access by @entity language', array('@entity' => $entity['label']));
|
||||
$plugin['name'] = $parent . ':' . $entity_type;
|
||||
$plugin['required context'] = new ctools_context_required(t(ucfirst($entity_type)), $entity_type);
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings form for the 'by node_language' access plugin
|
||||
*/
|
||||
function entity_translation_translation_exists_ctools_access_settings($form, &$form_state, $conf) {
|
||||
$options = array(
|
||||
ENTITY_TRANSLATION_LANGUAGE_CURRENT => t('Current content language'),
|
||||
ENTITY_TRANSLATION_LANGUAGE_DEFAULT => t('Default site language'),
|
||||
LANGUAGE_NONE => t('Language neutral'),
|
||||
);
|
||||
$options = array_merge($options, locale_language_list());
|
||||
$form['settings']['language'] = array(
|
||||
'#title' => t('Language'),
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $options,
|
||||
'#description' => t('This rule will pass if any of these languages are present.'),
|
||||
'#default_value' => $conf['language'],
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant access based on the which translations are available.
|
||||
*/
|
||||
function entity_translation_translation_exists_ctools_access_check($conf, $context) {
|
||||
// Check that the context exists.
|
||||
if (empty($context) || empty($context->data)) {
|
||||
return FALSE;
|
||||
}
|
||||
$entity = $context->data;
|
||||
$handler = entity_translation_get_handler($context->keyword, $entity);
|
||||
if (!empty($handler)) {
|
||||
$translations = $handler->getTranslations();
|
||||
global $language_content;
|
||||
foreach ($conf['language'] as $lang) {
|
||||
if ($lang) {
|
||||
switch ($lang) {
|
||||
case ENTITY_TRANSLATION_LANGUAGE_CURRENT:
|
||||
$lang = $language_content->language;
|
||||
break;
|
||||
case ENTITY_TRANSLATION_LANGUAGE_DEFAULT:
|
||||
$lang = language_default('language');
|
||||
break;
|
||||
}
|
||||
if (isset($translations->data[$lang]) && $translations->data[$lang]['status']) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a summary description based upon the checked node_languages.
|
||||
*/
|
||||
function entity_translation_translation_exists_ctools_access_summary($conf, $context) {
|
||||
$languages = array(
|
||||
ENTITY_TRANSLATION_LANGUAGE_CURRENT => t('Current site content language'),
|
||||
ENTITY_TRANSLATION_LANGUAGE_DEFAULT => t('Default site language'),
|
||||
LANGUAGE_NONE => t('Language neutral'),
|
||||
);
|
||||
$languages = array_merge($languages, locale_language_list());
|
||||
|
||||
if (!isset($conf['language'])) {
|
||||
$conf['language'] = array();
|
||||
}
|
||||
|
||||
$names = array();
|
||||
foreach (array_filter($conf['language']) as $language) {
|
||||
$names[] = $languages[$language];
|
||||
}
|
||||
|
||||
if (empty($names)) {
|
||||
return t('@identifier is in any language', array('@identifier' => $context->identifier));
|
||||
}
|
||||
|
||||
return format_plural(count($names), '@languages translation exists for @identifier', '@languages translations exists for identifier', array('@languages' => implode(', ', $names), '@identifier' => $context->identifier));
|
||||
}
|
||||
+957
-8
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -4,11 +4,11 @@ core = 7.x
|
||||
package = Testing
|
||||
hidden = TRUE
|
||||
dependencies[] = entity_translation
|
||||
|
||||
files[] = entity_translation_test.module
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-04-26
|
||||
version = "7.x-1.0-beta2+33-dev"
|
||||
; Information added by Drupal.org packaging script on 2019-11-24
|
||||
version = "7.x-1.1"
|
||||
core = "7.x"
|
||||
project = "entity_translation"
|
||||
datestamp = "1366981073"
|
||||
|
||||
datestamp = "1574630892"
|
||||
|
||||
+31
@@ -5,3 +5,34 @@
|
||||
* Installation functionality for Entity Translation testing module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function entity_translation_test_install() {
|
||||
// Create a simple text field, attached to taxonomy_terms.
|
||||
field_info_cache_clear();
|
||||
|
||||
$field = array(
|
||||
'field_name' => 'field_simple_text',
|
||||
'type' => 'text',
|
||||
'cardinality' => 1,
|
||||
);
|
||||
field_create_field($field);
|
||||
|
||||
$instance = array(
|
||||
'field_name' => $field['field_name'],
|
||||
'label' => ucfirst(str_replace('_', ' ', $field['field_name'])),
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => 'tags',
|
||||
'widget' => array(
|
||||
'type' => 'text_textfield',
|
||||
),
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'type' => 'text_default',
|
||||
),
|
||||
),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
|
||||
}
|
||||
|
||||
+12
@@ -5,6 +5,18 @@
|
||||
* Testing functionality for Entity Translation module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function entity_translation_test_form_alter(&$form, &$form_state) {
|
||||
if ($info = entity_translation_edit_form_info($form, $form_state)) {
|
||||
$handler = entity_translation_get_handler($info['entity type'], $info['entity']);
|
||||
if (entity_translation_enabled($info['entity type'], $info['entity'])) {
|
||||
$form_state['no_cache'] = $handler->isNewEntity() && variable_get('entity_translation_test_disable_form_cache', FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_translation_insert().
|
||||
*/
|
||||
|
||||
+21
-1
@@ -182,7 +182,7 @@ function entity_translation_views_data() {
|
||||
);
|
||||
$data['entity_translation']['translation_exists'] = array(
|
||||
'title' => t('Translation exists'),
|
||||
'help' => t('Link to translation overview page.'),
|
||||
'help' => t('Determines if a translation exists for a particular translation.'),
|
||||
'filter' => array(
|
||||
'handler' => 'entity_translation_handler_filter_translation_exists',
|
||||
),
|
||||
@@ -224,4 +224,24 @@ function entity_translation_views_data_alter(&$data) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Expose all translatable fields, using a handler based off Views' default
|
||||
// field handler, that allows users to select the language to display the
|
||||
// field in.
|
||||
foreach (field_info_fields() as $field) {
|
||||
if ($field['translatable'] && $field['storage']['type'] == 'field_sql_storage') {
|
||||
// Set defaults and just change the handler, title, group, and help.
|
||||
$defaults = field_views_field_default_views_data($field);
|
||||
foreach ($defaults as $table_name => $table_data) {
|
||||
if (isset($data[$table_name][$field['field_name']]) && isset($table_data[$field['field_name']])) {
|
||||
$field_title = $table_data[$field['field_name']]['title'];
|
||||
$table_data[$field['field_name']]['title'] = t('!title: translated', array('!title' => $field_title));
|
||||
$table_data[$field['field_name']]['group'] = t('Entity translation');
|
||||
$table_data[$field['field_name']]['help'] .= ' ' . t('Show the field !title translated into a specified language', array('!title' => $field_title));
|
||||
$table_data[$field['field_name']]['field']['handler'] = 'entity_translation_handler_field_field';
|
||||
$data[$table_name][$field['field_name'] . '_et'] = $table_data[$field['field_name']];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file contains a field handler for entity translation that shows field
|
||||
* content translated into a specified language.
|
||||
*/
|
||||
|
||||
class entity_translation_handler_field_field extends views_handler_field_field {
|
||||
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
|
||||
$options['language'] = array(
|
||||
'default' => '***CURRENT_LANGUAGE***',
|
||||
);
|
||||
return $options;
|
||||
}
|
||||
|
||||
function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
|
||||
$languages = array(
|
||||
'***CURRENT_LANGUAGE***' => t("Current user's language"),
|
||||
'***DEFAULT_LANGUAGE***' => t("Default site language"),
|
||||
LANGUAGE_NONE => t('Language neutral'),
|
||||
);
|
||||
|
||||
$languages = array_merge($languages, locale_language_list());
|
||||
|
||||
$form['language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Language'),
|
||||
'#options' => $languages,
|
||||
'#default_value' => $this->options['language'],
|
||||
'#description' => t('Select the language to display this field in')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides parent::field_language, retrieving the language from the handler
|
||||
* options.
|
||||
*/
|
||||
function field_language($entity_type, $entity) {
|
||||
global $language_content;
|
||||
|
||||
if (field_is_translatable($entity_type, $this->field_info)) {
|
||||
$default_language = language_default('language');
|
||||
$language = str_replace(array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
|
||||
array($language_content->language, $default_language),
|
||||
$this->options['language']);
|
||||
|
||||
// Give the Field Language API a chance to fallback to a different language
|
||||
// (or LANGUAGE_NONE), in case the field has no data for the selected language.
|
||||
// field_view_field() does this as well, but since the returned language code
|
||||
// is used before calling it, the fallback needs to happen explicitly.
|
||||
$language = field_language($entity_type, $entity, $this->field_info['field_name'], $language);
|
||||
|
||||
return $language;
|
||||
}
|
||||
else {
|
||||
return LANGUAGE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the relationship plugin for relating entities to translation metadata.
|
||||
* Contains a views plugin for relating entities to translation metadata.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -27,7 +28,10 @@ class entity_translation_handler_relationship extends views_handler_relationship
|
||||
$alias = $def['table'] . '_' . $this->table;
|
||||
// We need to add a condition on entity type to the join to avoid getting
|
||||
// relationships to entities with other types.
|
||||
$join->extra = "$alias.entity_type = '{$def['entity type']}'";
|
||||
$join->extra = array(
|
||||
array('field' => 'entity_type', 'value' => $def['entity type']),
|
||||
);
|
||||
$this->alias = $this->query->add_relationship($alias, $join, 'entity_translation', $this->relationship);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ For support, please create a support request for this module's project:
|
||||
|
||||
Support questions by email to the module maintainer will be simply ignored. Use the issue tracker.
|
||||
|
||||
Now if you want professional (paid) support the module maintainer may be available occassionally.
|
||||
Now if you want professional (paid) support the module maintainer may be available occasionally.
|
||||
Drop me a message to check availability and hourly rates, http://reyero.net/en/contact
|
||||
|
||||
====================================================================
|
||||
|
||||
@@ -4,15 +4,12 @@ dependencies[] = locale
|
||||
dependencies[] = variable
|
||||
package = Multilingual - Internationalization
|
||||
core = 7.x
|
||||
|
||||
files[] = i18n.install
|
||||
files[] = i18n_object.inc
|
||||
files[] = i18n.test
|
||||
configure = admin/config/regional/i18n
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -22,12 +22,21 @@ function i18n_uninstall() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields to schema if they don't exist
|
||||
* Add fields to the schema if they don't exist.
|
||||
*
|
||||
* @param string $table
|
||||
* The name of the database table.
|
||||
* @param array $fields
|
||||
* The list of database fields to create.
|
||||
* @param boolean $force_rebuild_schema
|
||||
* Whether to force drupal_get_schema() to rebuild the schema. This may be
|
||||
* necessary when additional implementations of hook_schema_alter() have
|
||||
* become available since the schema was originally built.
|
||||
*/
|
||||
function i18n_install_create_fields($table, $fields) {
|
||||
function i18n_install_create_fields($table, $fields, $force_rebuild_schema = FALSE) {
|
||||
static $schema;
|
||||
// Do not force schema refresh more than once per request.
|
||||
$schema = drupal_get_schema($table, !isset($schema));
|
||||
$rebuild_schema = !isset($schema) || $force_rebuild_schema;
|
||||
$schema = drupal_get_schema($table, $rebuild_schema);
|
||||
foreach ($fields as $field) {
|
||||
if (!empty($schema['fields'][$field])) {
|
||||
if (!db_field_exists($table, $field)) {
|
||||
|
||||
@@ -198,7 +198,8 @@ function i18n_language_field_extra() {
|
||||
*/
|
||||
function i18n_language_list($field = 'name', $mode = NULL) {
|
||||
$mode = isset($mode) ? $mode : variable_get('i18n_language_list', I18N_LANGUAGE_ENABLED);
|
||||
return locale_language_list($field, I18N_LANGUAGE_EXTENDED & $mode);
|
||||
$all = I18N_LANGUAGE_EXTENDED & $mode;
|
||||
return locale_language_list($field, $all);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,9 +404,17 @@ function i18n_get_object($type, $key, $object = NULL) {
|
||||
$index = is_array($key) ? implode(':', $key) : $key;
|
||||
if (!isset($cache[$type][$index])) {
|
||||
$class = i18n_object_info($type, 'class', 'i18n_object_wrapper');
|
||||
$cache[$type][$index] = new $class($type, $key, $object);
|
||||
$object_wrapper = new $class($type, $key, $object);
|
||||
// Do not cache object with empty index.
|
||||
if (!empty($index)) {
|
||||
$cache[$type][$index] = $object_wrapper;
|
||||
}
|
||||
}
|
||||
return $cache[$type][$index];
|
||||
else {
|
||||
$object_wrapper = $cache[$type][$index];
|
||||
}
|
||||
|
||||
return $object_wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -498,9 +507,12 @@ function i18n_object_translate_access($type, $object) {
|
||||
*
|
||||
* @param $path
|
||||
* Path to get translations for or '<front>' for front page.
|
||||
* @param $check_access
|
||||
* Whether to check access to paths, defaults to TRUE
|
||||
*/
|
||||
function i18n_get_path_translations($path) {
|
||||
function i18n_get_path_translations($path, $check_access = TRUE) {
|
||||
$translations = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($translations[$path])) {
|
||||
$translations[$path] = array();
|
||||
foreach (module_implements('i18n_translate_path') as $module) {
|
||||
@@ -510,10 +522,31 @@ function i18n_get_path_translations($path) {
|
||||
$translations[$path] += $translated;
|
||||
}
|
||||
}
|
||||
// Add access information if not there.
|
||||
foreach ($translations[$path] as $langcode => &$info) {
|
||||
if (!isset($info['access'])) {
|
||||
$item = menu_get_item($info['href']);
|
||||
// If no menu item, it may be an external URL, we allow access.
|
||||
$info['access'] = $item ? !empty($item['access']) : TRUE;
|
||||
}
|
||||
}
|
||||
// Chance for altering the results.
|
||||
drupal_alter('i18n_translate_path', $translations[$path], $path);
|
||||
}
|
||||
return $translations[$path];
|
||||
|
||||
if ($check_access) {
|
||||
return array_filter($translations[$path], '_i18n_get_path_translations_access');
|
||||
}
|
||||
else {
|
||||
return $translations[$path];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check access to path translation.
|
||||
*/
|
||||
function _i18n_get_path_translations_access($path) {
|
||||
return $path['access'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
|
||||
function setUpLanguages($admin_permissions = array()) {
|
||||
// Setup admin user.
|
||||
$this->admin_user = $this->drupalCreateUser(array_merge(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages', 'translate interface'), $admin_permissions));
|
||||
$this->admin_user = $this->drupalCreateUser(array_merge(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer fields', 'administer blocks', 'access administration pages', 'translate interface'), $admin_permissions));
|
||||
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
@@ -144,12 +144,14 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "Basic page" in the specified language.
|
||||
*
|
||||
* Create a node of the specified type in the specified language.
|
||||
|
||||
* @param $type
|
||||
* The node type.
|
||||
* @param $title
|
||||
* Title of basic page in specified language.
|
||||
* Title of node in specified language.
|
||||
* @param $body
|
||||
* Body of basic page in specified language.
|
||||
* Body of node in specified language.
|
||||
* @param $langcode
|
||||
* Language code.
|
||||
*/
|
||||
@@ -159,7 +161,9 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
$edit["body[$lang][0][value]"] = $body;
|
||||
$edit['language'] = $langcode;
|
||||
$this->drupalPost('node/add/' . $type, $edit, t('Save'));
|
||||
$this->assertRaw(t('Basic @type %title has been created.', array('@type' => $type, '%title' => $title)), t('Basic page created.'));
|
||||
$info = node_type_load($type);
|
||||
$message = t('@name %title has been created.', array('@name' => $info->name, '%title' => $title));
|
||||
$this->assertRaw($message);
|
||||
|
||||
// Check to make sure the node was created.
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
@@ -169,15 +173,14 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a translation for the specified basic page in the specified
|
||||
* language.
|
||||
* Create a translation for the specified node in the specified language.
|
||||
*
|
||||
* @param $node
|
||||
* The basic page to create translation for.
|
||||
* @param $title
|
||||
* Title of basic page in specified language.
|
||||
* Title of node in specified language.
|
||||
* @param $body
|
||||
* Body of basic page in specified language.
|
||||
* Body of node in specified language.
|
||||
* @param $language
|
||||
* Language code.
|
||||
*/
|
||||
@@ -196,8 +199,9 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
$edit["title"] = $title;
|
||||
$edit[$body_key] = $body;
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Translation created.'));
|
||||
|
||||
$info = node_type_load($node->type);
|
||||
$message = t('@name %title has been created.', array('@name' => $info->name, '%title' => $title));
|
||||
$this->assertRaw($message);
|
||||
// Check to make sure that translation was successful.
|
||||
$translation = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertTrue($translation, t('Node found in database.'));
|
||||
@@ -358,6 +362,7 @@ class Drupali18nTestCase extends DrupalWebTestCase {
|
||||
function resetCaches() {
|
||||
drupal_static_reset('locale_url_outbound_alter');
|
||||
drupal_static_reset('language_list');
|
||||
drupal_language_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Troubleshooting
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Block languages module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, allows the user to configure
|
||||
for which languages each block is visible.
|
||||
|
||||
* For a full description of the module,
|
||||
visit https://www.drupal.org/node/1279698.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
The settings for visibility per language are provided under Visibility
|
||||
Settings via the Languages tab when configuring a block.
|
||||
|
||||
The Languages tab also provides a setting for whether the block is translatable.
|
||||
For custom blocks, the block title and the block content will be translatable.
|
||||
For blocks defined by modules, only the block title will be translatable. If
|
||||
"Make this block translatable" is selected, a Translate tab will appear for that
|
||||
block. This tab provides a UI for adding translations of the block in each
|
||||
available language.
|
||||
|
||||
|
||||
TROUBLESHOOTING
|
||||
---------------
|
||||
|
||||
Conflicts with Context
|
||||
|
||||
The Block languages module conflicts with the Context module, which alters how
|
||||
blocks are rendered. This issue can be tracked in the Internationalization
|
||||
issue queue: http://drupal.org/node/1343044
|
||||
|
||||
String Errors
|
||||
|
||||
The user must allow your used string format to be translated on
|
||||
admin/config/regional/i18n/strings or you are going to have an error message
|
||||
like "The string blocks:block:1:body for textgroup blocks is not allowed for
|
||||
translation because of its text format."
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -8,9 +8,8 @@ files[] = i18n_block.inc
|
||||
files[] = i18n_block.test
|
||||
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -37,6 +37,17 @@ function i18n_block_menu() {
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function i18n_block_permission() {
|
||||
return array(
|
||||
'translate blocks' => array(
|
||||
'title' => t('Translate Blocks'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_menu_alter().
|
||||
*
|
||||
@@ -59,7 +70,7 @@ function i18n_block_menu_alter(&$items) {
|
||||
*/
|
||||
function i18n_block_translate_tab_access($module, $delta) {
|
||||
$block = block_load($module, $delta);
|
||||
return user_access('translate interface') && isset($block) && ($block->i18n_mode == I18N_MODE_LOCALIZE);
|
||||
return (user_access('translate interface') || user_access('translate blocks')) && $block && isset($block->i18n_mode) && ($block->i18n_mode == I18N_MODE_LOCALIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +237,7 @@ function i18n_block_form_block_admin_configure_alter(&$form, &$form_state, $form
|
||||
'#options' => i18n_language_list(),
|
||||
'#description' => t('If no language is selected, block will show regardless of language.'),
|
||||
);
|
||||
if (user_access('translate interface')) {
|
||||
if (user_access('translate interface') || user_access('translate blocks')) {
|
||||
$form['actions']['translate'] = array(
|
||||
'#type' => 'submit',
|
||||
'#name' => 'save_translate',
|
||||
|
||||
@@ -72,15 +72,16 @@ class i18nBlocksTestCase extends Drupali18nTestCase {
|
||||
|
||||
$this->clickLink(t('translate'));
|
||||
|
||||
// Title is a textarea, body is a text_format.
|
||||
$this->assertFieldByName('strings[blocks:block:' . $box2['delta'] . ':title]', $translations['title']['es']);
|
||||
$this->assertFieldByName('strings[blocks:block:' . $box2['delta'] . ':body]', $translations['body']['es']);
|
||||
$this->assertFieldByName('strings[blocks:block:' . $box2['delta'] . ':body][value]', $translations['body']['es']);
|
||||
|
||||
// Update the translation.
|
||||
$translations['title']['es'] = $this->randomName(10);
|
||||
$translations['body']['es'] = $this->randomName(20);
|
||||
$edit = array(
|
||||
'strings[blocks:block:' . $box2['delta'] . ':title]' => $translations['title']['es'],
|
||||
'strings[blocks:block:' . $box2['delta'] . ':body]' => $translations['body']['es'],
|
||||
'strings[blocks:block:' . $box2['delta'] . ':body][value]' => $translations['body']['es'],
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save translation'));
|
||||
$this->i18nAssertTranslations($translations['title'], '', 'Updated block title translation displayed.');
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Contact translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, helps with the multilingual
|
||||
configuration of the site contact forms. This module makes contact categories
|
||||
and replies available for translation.
|
||||
|
||||
* For a full description of the module, visit
|
||||
https://www.drupal.org/node/1396984.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes,
|
||||
visit https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
1. Enable the Contact translation module included with the Internationalization
|
||||
module.
|
||||
2. Go to Administration > Structure > Contact form.
|
||||
3. Select edit for the form to configure.
|
||||
4. Select the Translate tab.
|
||||
5. Select the translate link for a language.
|
||||
6. Translate the Category and Auto-reply text.
|
||||
7. Select Save translation.
|
||||
8. Repeat steps 5 to 7 for each language.
|
||||
9. Repeat steps 2 to 8 for all forms.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -5,9 +5,8 @@ dependencies[] = i18n_string
|
||||
package = Multilingual - Internationalization
|
||||
core = 7.x
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Field translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, allows for translation of text
|
||||
associated with a field's settings including the label, help text, default
|
||||
value, and list options.
|
||||
|
||||
* For a full description of the module, visit this page
|
||||
https://www.drupal.org/node/1279346.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
1. Enable the Field translation module included with Internationalization.
|
||||
2. Go to Administration > Configuration > Regional and language > Translate
|
||||
interface.
|
||||
3. Select the Filter Translatable Strings field set and limit search to fields.
|
||||
4. Edit the desired field and Save.
|
||||
|
||||
For the translation to be displayed, the user needs to use some of the Field
|
||||
Formatters provided by this module whose name usually ends up in 'translated'.
|
||||
For most core fields it is Default translated.
|
||||
|
||||
Note: The Field Translation module does not provide content translation for
|
||||
fields. This functionality is provided by the Entity Translation (ET) module.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API documentation file for Field translation module.
|
||||
*
|
||||
* This module takes care of translating common field elements like title and
|
||||
* description for all fields, plus some field specific values (default, options)
|
||||
* for field types defined by Drupal core.
|
||||
*
|
||||
* Before implementing any of these hooks, consider whether you would be better
|
||||
* off implementing Drupal core's hook_field_widget_form_alter().
|
||||
*
|
||||
* @see i18n_field_field_widget_form_alter()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide information about callbacks for translating specific field types.
|
||||
*
|
||||
* This information can be retrieved using i18n_field_type_info().
|
||||
* @return
|
||||
* Array of values indexed by field type. Valid keys are:
|
||||
* - 'translate_default', Callback for translating the default value for this field type.
|
||||
* - 'translate_options', Callback for translating options for this field type.
|
||||
*
|
||||
* @see i18n_field_type_info()
|
||||
* @see i18n_field_i18n_field_info()
|
||||
*
|
||||
* For examples of both callback types:
|
||||
*
|
||||
* @see i18n_field_translate_allowed_values()
|
||||
* @see i18n_field_translate_default()
|
||||
*
|
||||
*/
|
||||
function hook_i18n_field_info() {
|
||||
$info['text'] = $info['text_long'] = $info['text_with_summary'] = array(
|
||||
'translate_default' => 'i18n_field_translate_default',
|
||||
);
|
||||
$info['list_text'] = $info['list_boolean'] = $info['list_integer'] = array(
|
||||
'translate_options' => 'i18n_field_translate_allowed_values',
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter information provided by hook_i18n_field_info().
|
||||
*
|
||||
* @see i18n_field_type_info()
|
||||
*/
|
||||
function hook_i18n_field_info_alter(&$info) {
|
||||
// Unset the default callback for text fields.
|
||||
unset($info['text']['translate_default']);
|
||||
}
|
||||
@@ -72,6 +72,7 @@ function i18n_field_i18n_string_info() {
|
||||
'description' => t('Configurable fields descriptions, defaults, options, etc.'),
|
||||
'format' => FALSE, // This group doesn't have formatted strings
|
||||
'list' => TRUE, // This group can list all strings
|
||||
'class' => variable_get('i18n_string_textgroup_class_field', 'i18n_string_textgroup_cached'),
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@ package = Multilingual - Internationalization
|
||||
core = 7.x
|
||||
files[] = i18n_field.inc
|
||||
files[] = i18n_field.test
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -163,7 +163,6 @@ function i18n_field_field_formatter_view($entity_type, $entity, $field, $instanc
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_form_alter().
|
||||
*
|
||||
@@ -176,8 +175,8 @@ function i18n_field_field_formatter_view($entity_type, $entity, $field, $instanc
|
||||
function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
|
||||
global $language;
|
||||
|
||||
// Skip the node type edit fields by checking for existing entity
|
||||
if (empty($element['#entity'])) {
|
||||
// Don't translate if the widget is being shown on the field edit form.
|
||||
if ($form_state['build_info']['form_id'] == 'field_ui_field_edit_form') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,8 +188,45 @@ function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
|
||||
$instance = $context['instance'];
|
||||
$langcode = $context['langcode'];
|
||||
|
||||
// Get the element to alter. Account for inconsistencies in how the element
|
||||
// is built for different field types.
|
||||
if (isset($element[0]) && count($element) == 1) {
|
||||
// Single-value file fields and image fields.
|
||||
$alter_element = &$element[0];
|
||||
}
|
||||
elseif ($field['type'] == 'url' && $field['module'] == 'url' && $field['cardinality'] == 1) {
|
||||
$alter_element = &$element;
|
||||
}
|
||||
elseif (isset($element['value'])) {
|
||||
// Number fields. Single-value text fields.
|
||||
$alter_element = &$element['value'];
|
||||
}
|
||||
elseif ($field['type'] == 'entityreference' && isset($element['target_id'])) {
|
||||
// Entityreference fields using the entityreference_autocomplete widget.
|
||||
$alter_element = &$element['target_id'];
|
||||
}
|
||||
elseif ($field['type'] == 'node_reference' && isset($element['nid'])) {
|
||||
// The node_reference fields using the entityreference_autocomplete widget.
|
||||
$alter_element = &$element['nid'];
|
||||
}
|
||||
else {
|
||||
// All other fields.
|
||||
$alter_element = &$element;
|
||||
}
|
||||
|
||||
// If a subelement has the same title as the parent, translate it instead.
|
||||
// Allows fields such as email and commerce_price to be translated.
|
||||
foreach (element_get_visible_children($element) as $key) {
|
||||
$single_value = ($field['cardinality'] == 1);
|
||||
$has_title = (isset($element['#title']) && isset($element[$key]['#title']));
|
||||
if ($single_value && $has_title && $element[$key]['#title'] == $element['#title']) {
|
||||
$alter_element = &$element[$key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The field language may affect some variables (default) but not others (description will be in current page language)
|
||||
$i18n_langcode = empty($element['#language']) || $element['#language'] == LANGUAGE_NONE ? $language->language : $element['#language'];
|
||||
$i18n_langcode = empty($alter_element['#language']) || $alter_element['#language'] == LANGUAGE_NONE ? $language->language : $alter_element['#language'];
|
||||
|
||||
// Translate instance to current page language and set to form_state
|
||||
// so it will be used for validation messages later.
|
||||
@@ -200,37 +236,60 @@ function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
|
||||
}
|
||||
|
||||
// Translate field title if set and it is the default one.
|
||||
// When cardinality is 1, $element['value'] is used instead.
|
||||
if (!empty($instance_current['label']) && $instance_current['label'] != $instance['label']) {
|
||||
if (!empty($element['#title']) && $element['#title'] == $instance['label']) {
|
||||
$element['#title'] = $instance_current['label'];
|
||||
}
|
||||
if (isset($element['value']) && !empty($element['value']['#title']) && $element['value']['#title'] == $instance['label']) {
|
||||
$element['value']['#title'] = $instance_current['label'];
|
||||
if (!empty($alter_element['#title']) && $alter_element['#title'] == check_plain($instance['label'])) {
|
||||
$alter_element['#title'] = check_plain($instance_current['label']);
|
||||
}
|
||||
}
|
||||
|
||||
// Translate field description if set and it is the default one.
|
||||
// When cardinality is 1, $element['value'] is used instead.
|
||||
if (!empty($instance_current['description']) && $instance_current['description'] != $instance['description']) {
|
||||
if (!empty($element['#description']) && $element['#description'] == $instance['description']) {
|
||||
$element['#description'] = $instance_current['description'];
|
||||
}
|
||||
if (isset($element['value']) && !empty($element['value']['#description']) && $element['value']['#description'] == $instance['description']) {
|
||||
$element['value']['#description'] = $instance_current['description'];
|
||||
if (!empty($alter_element['#description'])) {
|
||||
// Allow single-value file fields and image fields to have their
|
||||
// descriptions translated. file_field_widget_form() passes the
|
||||
// description through theme('file_upload_help'), so i18n_field
|
||||
// must do the same.
|
||||
$filefield = in_array($field['type'], array('file', 'image'));
|
||||
$single_value = ($field['cardinality'] == 1);
|
||||
$no_default = empty($alter_element['#default_value']['fid']);
|
||||
if ($filefield && $single_value && $no_default) {
|
||||
$help_variables = array(
|
||||
'description' => field_filter_xss($instance['description']),
|
||||
'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
|
||||
);
|
||||
$original_description = theme('file_upload_help', $help_variables);
|
||||
if ($alter_element['#description'] == $original_description) {
|
||||
$help_variables = array(
|
||||
'description' => field_filter_xss($instance_current['description']),
|
||||
'upload_validators' => isset($alter_element['#upload_validators']) ? $alter_element['#upload_validators'] : array(),
|
||||
);
|
||||
$alter_element['#description'] = theme('file_upload_help', $help_variables);
|
||||
}
|
||||
}
|
||||
elseif ($alter_element['#description'] == field_filter_xss($instance['description'])) {
|
||||
$alter_element['#description'] = field_filter_xss($instance_current['description']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translate list options
|
||||
if (!empty($element['#options']) && ($translate = i18n_field_type_info($field['type'], 'translate_options')) && !empty($field['settings']['allowed_values'])) {
|
||||
$element['#options'] = $translate($field, $i18n_langcode);
|
||||
if (isset($element['#properties']) && !empty($element['#properties']['empty_option'])) {
|
||||
$label = theme('options_none', array('instance' => $instance, 'option' => $element['#properties']['empty_option']));
|
||||
$element['#options'] = array('_none' => $label) + $element['#options'];
|
||||
// For some elements, change title to new translated option
|
||||
if (!empty($element['#title']) && $field['type'] == 'list_boolean' && !empty($element['#on_value'])) {
|
||||
$on_value = $element['#on_value'];
|
||||
$element['#title'] = $element['#options'][$on_value];
|
||||
// Translate list options.
|
||||
$has_options = (!empty($alter_element['#options']) || $field['type'] == 'list_boolean');
|
||||
$has_allowed_values = !empty($field['settings']['allowed_values']);
|
||||
$translate = i18n_field_type_info($field['type'], 'translate_options');
|
||||
if ($has_options && $has_allowed_values && $translate) {
|
||||
$alter_element['#options'] = $translate($field, $i18n_langcode);
|
||||
if (isset($alter_element['#properties']) && !empty($alter_element['#properties']['empty_option'])) {
|
||||
$label = theme('options_none', array('instance' => $instance, 'option' => $alter_element['#properties']['empty_option']));
|
||||
$alter_element['#options'] = array('_none' => $label) + $alter_element['#options'];
|
||||
}
|
||||
// Translate list_boolean fields using the checkboxes widget.
|
||||
if (!empty($alter_element['#title']) && $field['type'] == 'list_boolean' && !empty($alter_element['#on_value'])) {
|
||||
$on_value = $alter_element['#on_value'];
|
||||
$alter_element['#options'];
|
||||
$alter_element['#title'] = $alter_element['#options'][$on_value];
|
||||
// For using label instead of "On value".
|
||||
if ($instance['widget']['settings']['display_label']) {
|
||||
$alter_element['#title'] = $instance_current['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,11 +301,15 @@ function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
|
||||
$delta = $context['delta'];
|
||||
$items = $context['items'];
|
||||
|
||||
// Translate default value if exists and the current value is the default
|
||||
if (isset($element['value']['#default_value']) && ($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']) {
|
||||
$element['value']['#default_value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
|
||||
// Translate default value.
|
||||
$has_default_value = (isset($alter_element['#default_value']) && !empty($instance['default_value'][$delta]['value']));
|
||||
$storage_has_value = !empty($items[$delta]['value']);
|
||||
$translate = i18n_field_type_info($field['type'], 'translate_default');
|
||||
if ($has_default_value && $storage_has_value && $translate) {
|
||||
// Compare the default value with the value currently in storage.
|
||||
if ($instance['default_value'][$delta]['value'] === $items[$delta]['value']) {
|
||||
$alter_element['#default_value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,10 +407,12 @@ function i18n_field_translate_allowed_values($field, $langcode = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate field default
|
||||
* Translate field default.
|
||||
*/
|
||||
function i18n_field_translate_default($instance, $value, $langcode = NULL) {
|
||||
return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode));
|
||||
// The default value does not need sanitizing in a text_textfield widget.
|
||||
$sanitize = !($instance['widget']['type'] == 'text_textfield' && $instance['widget']['module'] == 'text');
|
||||
return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode, 'sanitize' => $sanitize));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,7 +425,17 @@ function i18n_field_translate_property($instance, $property, $langcode = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get i18n information for fields
|
||||
* Get i18n information for translating fields.
|
||||
*
|
||||
* @param $type
|
||||
* Optional field type.
|
||||
* @param $property
|
||||
* Optional property to get from field type.
|
||||
*
|
||||
* @return
|
||||
* - The property for the field if $type and $property set.
|
||||
* - Array of properties for the field type if only $type is set.
|
||||
* - Array of translation information for all field types.
|
||||
*/
|
||||
function i18n_field_type_info($type = NULL, $property = NULL) {
|
||||
$info = &drupal_static(__FUNCTION__);
|
||||
@@ -378,3 +453,120 @@ function i18n_field_type_info($type = NULL, $property = NULL) {
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_info_alter().
|
||||
*/
|
||||
function i18n_field_field_info_alter(&$field_info) {
|
||||
foreach(array_keys($field_info) as $type) {
|
||||
$field_info[$type]['property_callbacks'][] = 'i18n_field_entity_property_callback';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime the cache to avoid single db queries for entity fields / properties.
|
||||
*
|
||||
* This is mainly uses when large operations are occuring like a flush of the
|
||||
* entity_property_infos().
|
||||
*/
|
||||
function i18n_field_prime_caches() {
|
||||
global $language;
|
||||
static $cache_primed;
|
||||
|
||||
// Fill the cache. This should avoid single db queries when filling the
|
||||
// properties.
|
||||
if (empty($cache_primed)) {
|
||||
$cache_primed = TRUE;
|
||||
$text_group = i18n_string_textgroup('field');
|
||||
// Load all strings at once to avoid callbacks for each individual string.
|
||||
$text_group->load_strings();
|
||||
$text_group->multiple_translation_search(array('type' => '*', 'objectid' => '*', 'property' => '*'), $language->language);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to translate entity property info for a fields.
|
||||
*
|
||||
* @see entity_metadata_field_entity_property_info()
|
||||
* @see entity_metadata_field_default_property_callback()
|
||||
* @see i18n_field_i18n_object_info_alter()
|
||||
* @see hook_module_implements_alter()
|
||||
*/
|
||||
function i18n_field_entity_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
|
||||
global $language;
|
||||
|
||||
// This could create a endless recursion if it's called during rebuilding the
|
||||
// cache for i18n_object_info(). So if the cache of i18n_object_info isn't
|
||||
// available yet we assume the worst case, leave the info alone but trigger a
|
||||
// rebuild of the property when hook_i18n_object_info_alter is invoked. At
|
||||
// that point the info is available and we can rely on it.
|
||||
if (!$info = &drupal_static('i18n_object_info')) {
|
||||
$i18n_field_entity_property_callback_fallback = &drupal_static(__FUNCTION__);
|
||||
$i18n_field_entity_property_callback_fallback = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
i18n_field_prime_caches();
|
||||
$name = $field['field_name'];
|
||||
$property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
|
||||
$property['label'] = i18n_field_translate_property($instance, 'label', $language->language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_i18n_object_info_alter().
|
||||
*/
|
||||
function i18n_field_i18n_object_info_alter(&$info) {
|
||||
if (drupal_static('i18n_field_entity_property_callback')) {
|
||||
if ($info = drupal_static('i18n_object_info')) {
|
||||
// Clean static and permanent cache of the data and then re-run the property
|
||||
// building.
|
||||
// Use a lock to avoid stampeding.
|
||||
$lock_name = 'i18n_field_entity_property_callback_fallback:' . $GLOBALS['language']->language;
|
||||
// See if another request is already doing this. If so we bail out here as
|
||||
// we won't help with anything at the moment.
|
||||
if (!lock_may_be_available($lock_name)) {
|
||||
return;
|
||||
}
|
||||
if (lock_acquire($lock_name)) {
|
||||
i18n_field_prime_caches();
|
||||
// Inject translated properties.
|
||||
$entity_property_info = entity_get_property_info();
|
||||
foreach ($entity_property_info as $entity_type => $properties) {
|
||||
if (isset($properties['bundles'])) {
|
||||
foreach ($properties['bundles'] as $bundle => $bundle_properties) {
|
||||
if ($bundle_properties['properties']) {
|
||||
foreach ($bundle_properties['properties'] as $bundle_property => $bundle_property_info) {
|
||||
if ($instance = field_info_instance($entity_type, $bundle_property, $bundle)) {
|
||||
$property = &$entity_property_info[$entity_type]['bundles'][$instance['bundle']]['properties'][$bundle_property];
|
||||
$property['label'] = i18n_field_translate_property($instance, 'label', $GLOBALS['language']->language);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Inject into static cache.
|
||||
$entity_get_property_info = &drupal_static('entity_get_property_info', array());
|
||||
$entity_get_property_info = $entity_property_info;
|
||||
// Write permanent cache.
|
||||
cache_set('entity_property_info:' . $GLOBALS['language']->language, $entity_property_info);
|
||||
lock_release($lock_name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
watchdog('i18n_field', 'Unable to run fall-back handling for entity property translation due missing "i18n_object_info" cache', array(), WATCHDOG_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_module_implements_alter().
|
||||
*/
|
||||
function i18n_field_module_implements_alter(&$implementations, $hook) {
|
||||
if ($hook == 'i18n_object_info_alter') {
|
||||
// Move our hook implementation to the bottom.
|
||||
$group = $implementations['i18n_field'];
|
||||
unset($implementations['i18n_field']);
|
||||
$implementations['i18n_field'] = $group;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Multilingual forum module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, helps with the multilingual
|
||||
configuration of the site’s forums.
|
||||
|
||||
* For a full description of the module visit
|
||||
https://www.drupal.org/node/1396988.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
To configure forum vocabulary
|
||||
|
||||
1. Enable the Multilingual forum module included with Internationalization.
|
||||
2. Go to Administration > Structure > Taxonomy.
|
||||
3. Select the "edit vocabulary" for the Forums vocabulary.
|
||||
4. Choose the translation mode (Localize or Translate).
|
||||
5. Select the "Save and translate" button.
|
||||
6. Select the "translate" link for a language.
|
||||
7. Translate the "Name" and "Description" for the forum.
|
||||
8. Select the "Save translation" button.
|
||||
9. Repeat steps 6 to 8 for each language.
|
||||
|
||||
To configure forum terms
|
||||
|
||||
1. Go to Administration > Structure > Forums.
|
||||
2. Select the "edit" link for a forum or container.
|
||||
3. Select the "Translate" tab.
|
||||
4. Select the "translate" link for a language.
|
||||
5. Translate the "Name" and "Description" for the term.
|
||||
6. Select the "Save translation" button.
|
||||
7. Repeat steps 4 to 6 for each language.
|
||||
8. Repeat all steps for all terms.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -7,9 +7,8 @@ package = Multilingual - Internationalization
|
||||
core = 7.x
|
||||
files[] = i18n_forum.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -35,7 +35,7 @@ function i18n_forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
|
||||
if ($vid && ($vocabulary = taxonomy_vocabulary_load($vid)) && ($field = field_info_field('taxonomy_' . $vocabulary->machine_name))) {
|
||||
foreach ($field['bundles']['node'] as $type) {
|
||||
if (isset($data['actions']['output'][$type])) {
|
||||
$data['actions']['output'][$type]['#link']['title'] = t('Add new @node_type', array('@node_type' => i18n_node_type_name($type, node_type_get_name($type))));
|
||||
$data['actions']['output'][$type]['#link']['title'] = t('Add new @node_type', array('@node_type' => i18n_node_type_name($type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Troubleshooting
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Menu translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, allows users to select a
|
||||
translation mode for each menu.
|
||||
|
||||
* For a full description of the module, visit this page:
|
||||
https://www.drupal.org/node/1113982.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes:
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
To link menu item menus to nodes, it is useful to have the following modules:
|
||||
|
||||
* Entity translation i18n menu module, a submodule of Entity translation -
|
||||
https://www.drupal.org/project/entity_translation
|
||||
* Menu translation node module - https://www.drupal.org/project/i18n_menu_node
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
Language-specific menus
|
||||
1. To create or edit a menu, navigate to Structure > Menus > (menu to edit) >
|
||||
Edit.
|
||||
2. In the Translation mode section, choose Fixed Language and a Language field
|
||||
will appear.
|
||||
3. Select a language, select Save, and add or update the menu items as needed.
|
||||
The menu block will only appear when viewing content in the same language.
|
||||
|
||||
There are three modes available: Translate and Localize, Fixed Language, and No
|
||||
Multilingual Options.
|
||||
|
||||
Translate and Localize:
|
||||
The user can create one menu for all languages, and translate or localize each
|
||||
menu item. There are two ways that menu items will be translated.
|
||||
1. The user can set a language when creating a custom menu item so that the menu
|
||||
item will only show up for that language. Menu items that link to nodes in a
|
||||
particular language will be treated this way.
|
||||
2. The user can localize other custom menu items without a language
|
||||
(for example, menu items linking to Views pages). Use the Translate tab to
|
||||
translate the menu item title and description. Translators can also use the
|
||||
"Translate interface" pages to translate these menu items.
|
||||
|
||||
Fixed Language:
|
||||
If the user chooses Fixed Language, they'll have to set up a separate menu in
|
||||
each language. This could become tedious if have a lot of languages enabled on
|
||||
the site, but is useful if the content or menu structure is different for each
|
||||
language.
|
||||
|
||||
No Multilingual Options:
|
||||
Only the menu will be translatable.
|
||||
|
||||
TROUBLESHOOTING
|
||||
---------------
|
||||
|
||||
A menu item linked to a node will be displayed only when the node language
|
||||
matches the page language. This is due to how the menu system works and the
|
||||
"Language selection" feature in i18n. Therefore, to get translated menus items
|
||||
that link to nodes, you first need translated content. For more information
|
||||
visit https://www.drupal.org/docs/7/multilingual/translating-content.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -10,7 +10,7 @@
|
||||
* Produces a menu translation form.
|
||||
*/
|
||||
function i18n_menu_translation_form($form, $form_state, $translation_set = NULL, $item = NULL) {
|
||||
$translation_set = $translation_set ? $translation_set : i18n_translation_set_create('menu_link');
|
||||
$translation_set = $translation_set ? $translation_set : i18n_translation_set_build('menu_link');
|
||||
$form['translation_set'] = array('#type' => 'value', '#value' => $translation_set);
|
||||
$translations = $translation_set->get_translations();
|
||||
// What to do with title? Let's make it a hidden field for now, some tests relay on it
|
||||
@@ -55,7 +55,7 @@ function i18n_menu_translation_form($form, $form_state, $translation_set = NULL,
|
||||
}
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['update'] = array('#type' => 'submit', '#value' => t('Save'));
|
||||
if ($translation_set) {
|
||||
if (!empty($translation_set->tsid)) {
|
||||
$form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
|
||||
}
|
||||
return $form;
|
||||
@@ -244,12 +244,16 @@ function i18n_menu_translation_item_overview($item, $translation_set = NULL) {
|
||||
$title = t('n/a');
|
||||
$options[] = l(t('add translation'), 'admin/structure/menu/manage/' . $item['menu_name'] . '/add', array('query' => array('translation' => $item['mlid'], 'target' => $langcode) + drupal_get_destination()));
|
||||
}
|
||||
$rows[] = array($language_name, $title, implode(" | ", $options));
|
||||
$rows[$langcode] = array(
|
||||
'language' => $language_name,
|
||||
'title' => $title,
|
||||
'operations' => implode(" | ", $options)
|
||||
);
|
||||
}
|
||||
|
||||
drupal_set_title(t('Translations of menu item %title', array('%title' => $item['link_title'])), PASS_THROUGH);
|
||||
|
||||
$build['translation_item_overview'] = array(
|
||||
$build['translation_overview'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
|
||||
@@ -123,8 +123,8 @@ function i18n_menu_item_translation_page($type, $item) {
|
||||
module_load_include('admin.inc', 'i18n_menu');
|
||||
// If the item has a language code, we can only support translation sets.
|
||||
$translation_set = !empty($item['i18n_tsid']) ? i18n_translation_set_load($item['i18n_tsid']) : NULL;
|
||||
$build['overview'] = i18n_menu_translation_item_overview($item, $translation_set);
|
||||
$build['translation_form'] = drupal_get_form('i18n_menu_translation_form', $translation_set, $item);
|
||||
return $build;
|
||||
$overview = i18n_menu_translation_item_overview($item, $translation_set);
|
||||
$translation_form = drupal_get_form('i18n_menu_translation_form', $translation_set, $item);
|
||||
return $overview + $translation_form;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ class i18n_menu_link extends i18n_string_object_wrapper {
|
||||
return I18N_MODE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to object translation. This should check object properties and permissions
|
||||
*/
|
||||
|
||||
@@ -10,9 +10,8 @@ core = 7.x
|
||||
files[] = i18n_menu.inc
|
||||
files[] = i18n_menu.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -53,6 +53,24 @@ function i18n_menu_menu_alter(&$items) {
|
||||
$items['admin/structure/menu/item/%menu_link'] = $items['admin/structure/menu/item/%menu_link/edit'];
|
||||
$items['admin/structure/menu/item/%menu_link']['type'] = MENU_CALLBACK;
|
||||
$items['admin/structure/menu/item/%menu_link/edit']['type'] = MENU_DEFAULT_LOCAL_TASK;
|
||||
$items['admin/structure/menu/manage/%menu']['title callback'] = 'i18n_menu_menu_overview_title';
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess theme_menu_admin_overview to translate menu name and description
|
||||
*
|
||||
* @param $variables
|
||||
*/
|
||||
function i18n_menu_preprocess_menu_admin_overview(&$variables) {
|
||||
$variables['title'] = i18n_string(array('menu', 'menu', $variables['name'], 'title'), $variables['title'], array('sanitize' => FALSE));
|
||||
$variables['description'] = i18n_string(array('menu', 'menu', $variables['name'], 'description'), $variables['description'], array('sanitize' => FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for the menu overview page and links.
|
||||
*/
|
||||
function i18n_menu_menu_overview_title($menu) {
|
||||
return i18n_string(array('menu', 'menu', $menu['menu_name'], 'title'), $menu['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,6 +361,8 @@ function i18n_menu_variable_info_alter(&$variables, $options) {
|
||||
// Make menu variables translatable
|
||||
$variables['menu_main_links_source']['localize'] = TRUE;
|
||||
$variables['menu_secondary_links_source']['localize'] = TRUE;
|
||||
$variables['menu_parent_[node_type]']['localize'] = TRUE;
|
||||
$variables['menu_options_[node_type]']['localize'] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +397,7 @@ function i18n_menu_localize_tree($tree, $langcode = NULL) {
|
||||
if (_i18n_menu_link_process($item['link'])) {
|
||||
if (!_i18n_menu_link_is_visible($item['link'], $langcode)) {
|
||||
// Remove links for other languages than current.
|
||||
// Links with language wont be localized.
|
||||
// Links with language won't be localized.
|
||||
unset($tree[$index]);
|
||||
// @todo Research whether the above has any advantage over:
|
||||
// $item['hidden'] = TRUE;
|
||||
@@ -468,7 +488,8 @@ function i18n_menu_navigation_links($menu_name, $level = 0) {
|
||||
* Get localized menu title
|
||||
*/
|
||||
function _i18n_menu_link_title($link, $langcode = NULL) {
|
||||
return i18n_string_translate(array('menu', 'item', $link['mlid'], 'title'), $link['link_title'], array('langcode' => $langcode, 'sanitize' => FALSE));
|
||||
$key = i18n_object_info('menu_link', 'key');
|
||||
return i18n_string_translate(array('menu', 'item', $link[$key], 'title'), $link['link_title'], array('langcode' => $langcode, 'sanitize' => FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,7 +521,8 @@ function _i18n_menu_link_localize(&$link, $langcode = NULL) {
|
||||
*/
|
||||
function _i18n_menu_link_description($link, $langcode = NULL) {
|
||||
if (!empty($link['options']['attributes']['title'])) {
|
||||
return i18n_string_translate(array('menu', 'item', $link['mlid'], 'description'), $link['options']['attributes']['title'], array('langcode' => $langcode));
|
||||
$key = i18n_object_info('menu_link', 'key');
|
||||
return i18n_string_translate(array('menu', 'item', $link[$key], 'description'), $link['options']['attributes']['title'], array('langcode' => $langcode, 'sanitize' => FALSE));
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
@@ -511,9 +533,11 @@ function _i18n_menu_link_description($link, $langcode = NULL) {
|
||||
* Check whether this link is to be processed by i18n_menu and start processing.
|
||||
*/
|
||||
function _i18n_menu_link_process(&$link) {
|
||||
// Only visible links that have a language property and haven't been processed
|
||||
// before. We also check that they belong to a menu with language options.
|
||||
if (empty($link['i18n_menu']) && !empty($link['language']) && !empty($link['access']) && empty($link['hidden']) && i18n_menu_mode($link['menu_name'])) {
|
||||
// Only links that have a language property and haven't been processed before.
|
||||
// We also translate links marked as hidden because core breadcrumbs ignore
|
||||
// that flag and excluding them would basically interfere with core behaviour.
|
||||
// We also check that they belong to a menu with language options.
|
||||
if (empty($link['i18n_menu']) && !empty($link['language']) && !empty($link['access']) && i18n_menu_mode($link['menu_name'])) {
|
||||
// Mark so it won't be processed twice.
|
||||
$link['i18n_menu'] = TRUE;
|
||||
// Skip if administering this menu or this menu item.
|
||||
@@ -525,6 +549,10 @@ function _i18n_menu_link_process(&$link) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
// Skip if administering this menu item through the node edit form.
|
||||
elseif (arg(0) == 'node' && arg(2) == 'edit' && $link['link_path'] == arg(0) . '/' . arg(1)) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
@@ -571,7 +599,7 @@ function _i18n_menu_link_is_visible($link, $langcode = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localizable properties for menu link checking agains the router item.
|
||||
* Get localizable properties for menu link checking against the router item.
|
||||
*/
|
||||
function _i18n_menu_link_localizable_properties($link) {
|
||||
$props = array();
|
||||
@@ -580,7 +608,7 @@ function _i18n_menu_link_localizable_properties($link) {
|
||||
// If the title callback is 't' and the link title matches the router title
|
||||
// it will be localized by core, not by i18n_menu.
|
||||
if (!$router ||
|
||||
(empty($router['title_callback']) || $router['title_callback'] != 't') ||
|
||||
(empty($router['title_callback']) || ($router['title_callback'] != 't' || !empty($link['customized']))) ||
|
||||
(empty($router['title']) || $router['title'] != $link['link_title'])
|
||||
) {
|
||||
$props[] = 'title';
|
||||
@@ -589,7 +617,7 @@ function _i18n_menu_link_localizable_properties($link) {
|
||||
if (!empty($link['options']['attributes']['title'])) {
|
||||
// If the description matches the router description, it will be localized
|
||||
// by core.
|
||||
if (!$router || empty($router['description']) || $router['description'] != $link['options']['attributes']['title']) {
|
||||
if (!$router || empty($router['description']) || ($router['description'] != $link['options']['attributes']['title']) || !empty($link['customized'])) {
|
||||
$props[] = 'description';
|
||||
}
|
||||
}
|
||||
@@ -709,6 +737,30 @@ function i18n_menu_form_menu_edit_item_alter(&$form, &$form_state) {
|
||||
array_unshift($form['#validate'], 'i18n_menu_menu_item_prepare_normal_path');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
* FORM_ID = menu-overview-form.
|
||||
* Add a "translate" link in operations column for each menu item.
|
||||
*/
|
||||
function i18n_menu_form_menu_overview_form_alter(&$form, &$form_state) {
|
||||
if (i18n_menu_mode($form['#menu']['menu_name'], I18N_MODE_MULTIPLE)) {
|
||||
foreach (element_children($form) as $element) {
|
||||
if (substr($element, 0, 5) == 'mlid:') {
|
||||
$item = $form[$element]["#item"];
|
||||
$mlid = $form[$element]['#item']['mlid'];
|
||||
if (i18n_get_object('menu', $mlid)->get_translate_access()) {
|
||||
$form[$element]['operations']['translate'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => t('translate'),
|
||||
'#href' => "admin/structure/menu/item/{$mlid}/translate",
|
||||
);
|
||||
$form[$element]['title']['#markup'] = l(_i18n_menu_link_title($item), $item['href'], $item['localized_options']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal path should be checked with menu item's language to avoid
|
||||
* troubles when a node and it's translation has the same url alias.
|
||||
@@ -727,6 +779,9 @@ function i18n_menu_item_get_language($item) {
|
||||
}
|
||||
else {
|
||||
$menu = menu_load($item['menu_name']);
|
||||
if (!isset($menu['i18n_mode'])) {
|
||||
return LANGUAGE_NONE;
|
||||
}
|
||||
switch ($menu['i18n_mode']) {
|
||||
case I18N_MODE_LANGUAGE:
|
||||
return $menu['language'];
|
||||
@@ -859,93 +914,33 @@ function i18n_menu_link_load($path, $langcode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_init().
|
||||
* Implements hook_query_TAG_alter() for features_menu_links.
|
||||
* Add needed fields to properly serialize localization information.
|
||||
*/
|
||||
function i18n_menu_init() {
|
||||
function i18n_menu_query_features_menu_link_alter($query) {
|
||||
$query->fields('menu_links', array('language', 'customized'));
|
||||
}
|
||||
|
||||
// The only way to override the default preferred menu link for a path is to
|
||||
// inject it into the static cache of the function menu_link_get_preferred().
|
||||
/**
|
||||
* Implements hook_query_TAG_alter()
|
||||
*
|
||||
* Using tag 'preferred_menu_links' added in menu_link_get_preferred().
|
||||
* See http://drupal.org/node/1854134
|
||||
*/
|
||||
function i18n_menu_query_preferred_menu_links_alter(QueryAlterableInterface $query) {
|
||||
global $language;
|
||||
// Get queried tables.
|
||||
$tables = $query->getTables();
|
||||
|
||||
// The problem with the default implementation is that it does not take the
|
||||
// language of a menu link into account. Whe having different menu trees for
|
||||
// different menus, this means that the active trail will not work for all but
|
||||
// one language.
|
||||
|
||||
// The code below is identical to the mentioned function except the added
|
||||
// language condition on the query.
|
||||
|
||||
// TODO: Adding an alter tag to the query would allow to do this with a simple
|
||||
// hook_query_alter() implementation.
|
||||
|
||||
$preferred_links = &drupal_static('menu_link_get_preferred');
|
||||
|
||||
$path = $_GET['q'];
|
||||
|
||||
// Look for the correct menu link by building a list of candidate paths,
|
||||
// which are ordered by priority (translated hrefs are preferred over
|
||||
// untranslated paths). Afterwards, the most relevant path is picked from
|
||||
// the menus, ordered by menu preference.
|
||||
$item = menu_get_item($path);
|
||||
$path_candidates = array();
|
||||
// 1. The current item href.
|
||||
$path_candidates[$item['href']] = $item['href'];
|
||||
// 2. The tab root href of the current item (if any).
|
||||
if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) {
|
||||
$path_candidates[$tab_root['href']] = $tab_root['href'];
|
||||
}
|
||||
// 3. The current item path (with wildcards).
|
||||
$path_candidates[$item['path']] = $item['path'];
|
||||
// 4. The tab root path of the current item (if any).
|
||||
if (!empty($tab_root)) {
|
||||
$path_candidates[$tab_root['path']] = $tab_root['path'];
|
||||
}
|
||||
// Retrieve a list of menu names, ordered by preference.
|
||||
$menu_names = menu_get_active_menu_names();
|
||||
// Use an illegal menu name as the key for the preferred menu link.
|
||||
$selected_menu = MENU_PREFERRED_LINK;
|
||||
// Put the selected menu at the front of the list.
|
||||
array_unshift($menu_names, $selected_menu);
|
||||
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query->fields('ml');
|
||||
// Weight must be taken from {menu_links}, not {menu_router}.
|
||||
$query->addField('ml', 'weight', 'link_weight');
|
||||
$query->fields('m');
|
||||
$query->condition('ml.link_path', $path_candidates, 'IN');
|
||||
|
||||
// Only look menu links with none or the current language.
|
||||
$query->condition('ml.language', array(LANGUAGE_NONE, i18n_language_interface()->language), 'IN');
|
||||
|
||||
// Sort candidates by link path and menu name.
|
||||
$candidates = array();
|
||||
foreach ($query->execute() as $candidate) {
|
||||
$candidate['weight'] = $candidate['link_weight'];
|
||||
$candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
|
||||
// Add any menus not already in the menu name search list.
|
||||
if (!in_array($candidate['menu_name'], $menu_names)) {
|
||||
$menu_names[] = $candidate['menu_name'];
|
||||
}
|
||||
}
|
||||
|
||||
// Store the most specific link for each menu. Also save the most specific
|
||||
// link of the most preferred menu in $preferred_link.
|
||||
foreach ($path_candidates as $link_path) {
|
||||
if (isset($candidates[$link_path])) {
|
||||
foreach ($menu_names as $menu_name) {
|
||||
if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) {
|
||||
$candidate_item = $candidates[$link_path][$menu_name];
|
||||
$map = explode('/', $path);
|
||||
_menu_translate($candidate_item, $map);
|
||||
if ($candidate_item['access']) {
|
||||
$preferred_links[$path][$menu_name] = $candidate_item;
|
||||
if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) {
|
||||
// Store the most specific link.
|
||||
$preferred_links[$path][MENU_PREFERRED_LINK] = $candidate_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($tables as $alias => $table) {
|
||||
if ($table['table'] == 'menu_links') {
|
||||
// Add language filter, ensuring that we don't have any collision when
|
||||
// determining the active menu trail when there are multiple menu items
|
||||
// with same link path but different languages.
|
||||
if ($language) {
|
||||
$query->condition('language', array($language->language, LANGUAGE_NONE), 'IN');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ class i18nMenuTestCase extends Drupali18nTestCase {
|
||||
$this->drupalGet('admin/structure/menu/item/' . $node->menu['mlid'] . '/edit');
|
||||
$this->assertText(t('This menu item belongs to a node, so it will have the same language as the node and cannot be localized.'));
|
||||
$this->assertNoField('language', 'We cannot edit language for menu items that belong to nodes.');
|
||||
// Edit the node and submit it to see if the menu link stays enabled.
|
||||
$this->drupalPost('node/' . $node->nid . '/edit', array(), t('Save'));
|
||||
}
|
||||
// Check menu items show up for the right language.
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Multilingual content module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, provides extended multilingual
|
||||
options for nodes. These options help accommodate a variety of translation
|
||||
workflows by controlling how the language for nodes is set.
|
||||
|
||||
Note that the Multilingual content module lives in the i18n_node directory
|
||||
in the Internationalization package. Don't confuse this module with the core
|
||||
Content translation module.
|
||||
|
||||
* For a full description of the module visit:
|
||||
https://www.drupal.org/node/1279644.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
For each content type, the following Extended language options are available
|
||||
under the Multilingual Settings tab.
|
||||
1. "Set current language as default for new content" can be useful for content
|
||||
that is community-generated.
|
||||
2. "Require language" prevents users from creating 'Language neutral' nodes.
|
||||
3. "Lock language" prevents users from changing the language of a node after
|
||||
it's created.
|
||||
|
||||
Site-wide Settings for Node Translation
|
||||
1. There are also site-wide settings provided to help streamline how
|
||||
multilingual content is created. Navigate to Config > Regional and language >
|
||||
Multilingual settings > Node Options.
|
||||
2. "Switch interface for translating" switches the language of the user
|
||||
interface to the chosen language when a user translates a node. This is
|
||||
useful if users speak the language in which the translation is written. It
|
||||
means that after the node translation is saved, the language of the UI will
|
||||
match the language of the node.
|
||||
3. "Hide content translation links" will prevent the language switcher links
|
||||
from appearing in nodes and teasers. This is useful if a language switcher
|
||||
block is enabled on the site.
|
||||
4. The user can also select the default language for new nodes if the
|
||||
corresponding content type doesn't have language support. By default, it is
|
||||
set to be in the default language of the site, but this can be changed to be
|
||||
language neutral. This is a useful option when thinking about forward
|
||||
compatibility for adding multilingual support to these content types in the
|
||||
future.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -9,9 +9,8 @@ configure = admin/config/regional/i18n/node
|
||||
files[]=i18n_node.test
|
||||
files[]=i18n_node.variable.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -54,7 +54,7 @@ function i18n_node_block_view_system_help_alter(&$block) {
|
||||
$arg = drupal_help_arg(arg(NULL));
|
||||
if ($arg[0] == 'node' && $arg[1] == 'add' && $arg[2]) {
|
||||
if (($type = node_type_get_type(str_replace('-', '_', $arg[2]))) && !empty($type->help)) {
|
||||
$help = i18n_node_translate_type($type, 'help');
|
||||
$help = i18n_node_translate_type($type, 'help', NULL, array('sanitize' => FALSE));
|
||||
if ($help !== $type->help) {
|
||||
$block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
|
||||
}
|
||||
@@ -64,7 +64,7 @@ function i18n_node_block_view_system_help_alter(&$block) {
|
||||
$node = menu_get_object();
|
||||
if ($node && isset($node->type)) {
|
||||
$type = node_type_get_type($node->type);
|
||||
$help = i18n_node_translate_type($type, 'help');
|
||||
$help = i18n_node_translate_type($type, 'help', NULL, array('sanitize' => FALSE));
|
||||
if ($help !== $type->help) {
|
||||
$block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
|
||||
}
|
||||
@@ -121,6 +121,8 @@ function i18n_node_i18n_translate_path($path) {
|
||||
'href' => 'node/' . $node_translation->nid . $matches[2],
|
||||
'title' => $node_translation->title,
|
||||
'object' => $node_translation,
|
||||
// Performance: for node view add access information right away.
|
||||
'access' => !$matches[2] ? $node_translation->status : NULL,
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
@@ -220,8 +222,16 @@ function i18n_node_language_mode($type) {
|
||||
function i18n_node_node_prepare($node) {
|
||||
$options = variable_get('i18n_node_options_' . $node->type, array());
|
||||
if (i18n_node_type_enabled($node) && empty($node->nid) && !i18n_object_langcode($node) && in_array('current', $options)) {
|
||||
$default = variable_get('i18n_node_default_language_for_' . $node->type, '-- current --');
|
||||
|
||||
// Set current language for new nodes if option enabled
|
||||
$node->language = i18n_language_content()->language;
|
||||
if ($default === '-- current --') {
|
||||
$node->language = i18n_language_content()->language;
|
||||
}
|
||||
// If a custom language was specified, apply it.
|
||||
else {
|
||||
$node->language = $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,13 +257,16 @@ function i18n_node_permission() {
|
||||
/**
|
||||
* Implements hook_node_view()
|
||||
*/
|
||||
function i18n_node_node_view($node) {
|
||||
function i18n_node_node_view($node, $view_mode, $langcode) {
|
||||
if (i18n_node_type_enabled($node)) {
|
||||
$node->content['language'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Language'),
|
||||
'#markup' => i18n_language_name($node->language),
|
||||
);
|
||||
$extra_fields_display_settings = field_extra_fields_get_display('node', $node->type, $view_mode);
|
||||
if ($extra_fields_display_settings['language']['visible']) {
|
||||
$node->content['language'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Language'),
|
||||
'#markup' => i18n_language_name($node->language),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +276,9 @@ function i18n_node_node_view($node) {
|
||||
* Handles links for extended languages. Sets current interface language.
|
||||
*/
|
||||
function i18n_node_node_view_alter(&$build) {
|
||||
$node = $build['#node'];
|
||||
if (isset($build['#node'])) {
|
||||
$node = $build['#node'];
|
||||
}
|
||||
// Hide node translation links.
|
||||
if (variable_get('i18n_hide_translation_links', 0)) {
|
||||
if (isset($build['links']['translation'])) {
|
||||
@@ -347,7 +362,7 @@ function i18n_node_textfield_process($element) {
|
||||
static $sent = FALSE;
|
||||
|
||||
// Ensure we send the Javascript only once.
|
||||
if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) {
|
||||
if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_DEFAULT) != LANGUAGE_NEGOTIATION_DEFAULT) {
|
||||
// Add a JS file for node forms.
|
||||
// Determine if we are either editing or translating an existing node.
|
||||
// We can't act on regular node creation because we don't have a specified
|
||||
@@ -415,7 +430,16 @@ function i18n_node_form_node_type_form_alter(&$form, &$form_state) {
|
||||
// Some settings about node languages. Add variables for node type from variable definition
|
||||
if ($form['#node_type']->type) {
|
||||
variable_type_include('node_type');
|
||||
$form['i18n'] += node_variable_type_subform($form['#node_type']->type, array('i18n_node_options', 'i18n_node_extended'));
|
||||
$form['i18n'] += node_variable_type_subform($form['#node_type']->type, array('i18n_node_options', 'i18n_node_default_language_for', 'i18n_node_extended'));
|
||||
// Only show custom default language field if "current" is checked.
|
||||
$form['i18n']['i18n_node_default_language_for']['#states'] = array(
|
||||
'visible' => array(
|
||||
':input[name="i18n_node_options[current]"]' => array('checked' => TRUE),
|
||||
),
|
||||
'required' => array(
|
||||
':input[name="i18n_node_options[current]"]' => array('checked' => TRUE),
|
||||
),
|
||||
);
|
||||
}
|
||||
// Add disabled message
|
||||
if ($disabled) {
|
||||
@@ -442,7 +466,7 @@ function i18n_node_form_node_form_alter(&$form, $form_state) {
|
||||
*
|
||||
* @see http://drupal.org/node/765860
|
||||
*/
|
||||
|
||||
|
||||
// Replace core's node submit callback with our own,
|
||||
// in order to translate the node type name.
|
||||
$key = array_search('node_form_submit', $form['actions']['submit']['#submit']);
|
||||
@@ -464,8 +488,7 @@ function i18n_node_form_submit($form, &$form_state) {
|
||||
$insert = empty($node->nid);
|
||||
node_save($node);
|
||||
$node_link = l(t('view'), 'node/' . $node->nid);
|
||||
$type = node_type_get_type($node->type);
|
||||
$type_name = i18n_node_translate_type($type);
|
||||
$type_name = i18n_node_type_name($node->type);
|
||||
|
||||
$watchdog_args = array('@type' => $node->type, '%title' => $node->title);
|
||||
$t_args = array('@type' => $type_name, '%title' => $node->title);
|
||||
@@ -481,7 +504,7 @@ function i18n_node_form_submit($form, &$form_state) {
|
||||
if ($node->nid) {
|
||||
$form_state['values']['nid'] = $node->nid;
|
||||
$form_state['nid'] = $node->nid;
|
||||
$form_state['redirect'] = 'node/' . $node->nid;
|
||||
$form_state['redirect'] = node_access('view', $node) ? 'node/' . $node->nid : '<front>';
|
||||
}
|
||||
else {
|
||||
// In the unlikely case something went wrong on save, the node will be
|
||||
@@ -505,8 +528,11 @@ function _i18n_node_form_node_form_alter($form, &$form_state) {
|
||||
}
|
||||
}
|
||||
elseif (variable_get('i18n_node_default_language_none', 0) && !isset($form['#node']->nid)) {
|
||||
// Override locale module setting default language to nodes. It is already in form_state.
|
||||
$form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
|
||||
// Only do this if the language is really disabled
|
||||
if (variable_get('language_content_type_' . $node->type, 0) == 0) {
|
||||
// Override locale module setting default language to nodes. It is already in form_state.
|
||||
$form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
|
||||
}
|
||||
}
|
||||
// Translate field names for title and body for the node edit form.
|
||||
if (!empty($form['title']['#title'])) {
|
||||
@@ -516,12 +542,11 @@ function _i18n_node_form_node_form_alter($form, &$form_state) {
|
||||
$form['body_field']['body']['#title'] = i18n_node_translate_type($node->type, 'body', $form['body_field']['body']['#title']);
|
||||
}
|
||||
// Translate page title for node/add/% and node/%/edit pages.
|
||||
$types = node_type_get_types();
|
||||
if (empty($node->nid) && strpos($_GET['q'], 'node/add/' . str_replace('_', '-', $node->type)) === 0) {
|
||||
drupal_set_title(t('Create @name', array('@name' => i18n_node_translate_type($types[$node->type], 'name'))), PASS_THROUGH);
|
||||
drupal_set_title(t('Create @name', array('@name' => i18n_node_type_name($node->type))), PASS_THROUGH);
|
||||
}
|
||||
elseif (!empty($node->nid) && $_GET['q'] == 'node/' . $node->nid . '/edit') {
|
||||
drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => i18n_node_translate_type($types[$node->type], 'name'), '@title' => $node->title)), PASS_THROUGH);
|
||||
drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => i18n_node_type_name($node->type), '@title' => $node->title)), PASS_THROUGH);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
@@ -553,10 +578,16 @@ function i18n_node_translate_type($type, $property = 'name', $source = NULL, $op
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate node type name (unfiltered)
|
||||
* Get translated node type name (unfiltered)
|
||||
*
|
||||
* @param string $type
|
||||
* Node type.
|
||||
* @param string $name
|
||||
* Optional node type name.
|
||||
*/
|
||||
function i18n_node_type_name($type, $name) {
|
||||
return i18n_string_translate(array('node', 'type', $type, 'name'), $name);
|
||||
function i18n_node_type_name($type, $name = NULL) {
|
||||
$name = isset($name) ? $name : node_type_get_name($type);
|
||||
return i18n_string_translate(array('node', 'type', $type, 'name'), $name, array('sanitize' => FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ function i18n_node_add_page() {
|
||||
if ($type) {
|
||||
// We just need to translate the description, the title is translated by the menu system
|
||||
// The string will be filtered (xss_admin) on the theme layer
|
||||
$item['description'] = i18n_node_translate_type($type, 'description', $item['description']);
|
||||
$item['description'] = i18n_node_translate_type($type, 'description', $item['description'], array('sanitize' => FALSE));
|
||||
}
|
||||
}
|
||||
return theme('node_add_list', array('content' => $content));
|
||||
@@ -213,6 +213,7 @@ function i18n_node_select_translation_submit($form, &$form_state) {
|
||||
))
|
||||
->condition('nid', $add)
|
||||
->execute();
|
||||
entity_get_controller('node')->resetCache($add);
|
||||
if (count($new)) {
|
||||
drupal_set_message(format_plural(count($new), 'Added a node to the translation set.', 'Added @count nodes to the translation set.'));
|
||||
}
|
||||
@@ -224,6 +225,7 @@ function i18n_node_select_translation_submit($form, &$form_state) {
|
||||
))
|
||||
->condition('nid', $remove)
|
||||
->execute();
|
||||
entity_get_controller('node')->resetCache($remove);
|
||||
drupal_set_message(format_plural(count($remove), 'Removed a node from the translation set.', 'Removed @count nodes from the translation set.'));
|
||||
}
|
||||
}
|
||||
@@ -246,7 +248,7 @@ function i18n_node_autocomplete($type, $language, $string = '') {
|
||||
*/
|
||||
function i18n_node_nid2autocomplete($nid) {
|
||||
if ($node = node_load($nid)) {
|
||||
return check_plain($node->title) . ' [nid:' . $nid . ']';
|
||||
return $node->title . ' [nid:' . $nid . ']';
|
||||
}
|
||||
else {
|
||||
return t('Not found');
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Builds placeholder replacement tokens for content types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function i18n_node_token_info() {
|
||||
$content_type['i18n-name'] = array(
|
||||
'name' => t("Name (localized)"),
|
||||
'description' => t("The name of the content type."),
|
||||
);
|
||||
$content_type['i18n-description'] = array(
|
||||
'name' => t("Description (localized)"),
|
||||
'description' => t("The optional description of the content type."),
|
||||
);
|
||||
|
||||
return array(
|
||||
'tokens' => array(
|
||||
'content-type' => $content_type,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function i18n_node_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$replacements = array();
|
||||
$sanitize = !empty($options['sanitize']) ? TRUE : FALSE;
|
||||
$langcode = isset($options['language']) ? $options['language']->language : i18n_langcode();
|
||||
|
||||
if ($type == 'content-type' && !empty($data['node_type'])) {
|
||||
|
||||
$node_type = $data['node_type'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
|
||||
case 'i18n-name':
|
||||
$name = array('node', 'type', $node_type->type, 'name');
|
||||
$options = array('sanitize' => $sanitize, 'langcode' => $langcode);
|
||||
$name = i18n_string_translate($name, $node_type->name, $options);
|
||||
$replacements[$original] = $name;
|
||||
break;
|
||||
|
||||
case 'i18n-description':
|
||||
$description = array('node', 'type', $node_type->type, 'description');
|
||||
$options = array('sanitize' => $sanitize, 'langcode' => $langcode);
|
||||
$description = i18n_string_translate($description, $node_type->description, $options);
|
||||
$replacements[$original] = $description;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
@@ -45,13 +45,30 @@ function i18n_node_variable_info($options = array()) {
|
||||
'repeat' => array(
|
||||
'type' => 'options',
|
||||
'options' => array(
|
||||
'current' => t('Set current language as default for new content.', array(), $options),
|
||||
// Note: this was previously used only to mark new, translatable nodes
|
||||
// with the current language of the user. Now, this setting is extended
|
||||
// to allow a specific language to be chosen (defaulting to the current
|
||||
// language). This was done for backwards compatibility reasons.
|
||||
'current' => t('Set custom language as default for new content.', array(), $options),
|
||||
'required' => t('Require language (Do not allow Language Neutral).', array(), $options),
|
||||
'lock' => t('Lock language (Cannot be changed).', array(), $options),
|
||||
),
|
||||
),
|
||||
'group' => 'i18n',
|
||||
);
|
||||
// This field will only be displayed if "current" is checked above.
|
||||
$variables['i18n_node_default_language_for_[node_type]'] = array(
|
||||
'type' => 'multiple',
|
||||
'title' => t('Custom default language', array(), $options),
|
||||
'repeat' => array(
|
||||
'type' => 'select',
|
||||
'options' => array_merge(array(
|
||||
'-- current --' => t('Current language')
|
||||
), locale_language_list('name')),
|
||||
'default' => '-- current --',
|
||||
),
|
||||
'group' => 'i18n',
|
||||
);
|
||||
$variables['i18n_node_extended_[node_type]'] = array(
|
||||
'type' => 'multiple',
|
||||
'title' => t('Extended language support'),
|
||||
|
||||
@@ -6,9 +6,8 @@ core = 7.x
|
||||
|
||||
files[] = i18n_path.inc
|
||||
files[] = i18n_path.test
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Redirect translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, improves search engine
|
||||
optimization (SEO) for multilingual websites.
|
||||
|
||||
It redirects anonymous users (including web crawlers) to the translation of the
|
||||
page in the requested language, if it exists, using a 301 redirect code.
|
||||
|
||||
* For a full description of the module, visit this page:
|
||||
https://www.drupal.org/node/1280468.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes:
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
The Translation redirect module requires the implementation of
|
||||
hook_i18n_translate_path by another module for the redirect page to be
|
||||
determined. Currently, the Multilingual content, Path translation, and Taxonomy
|
||||
translation modules implement this hook.
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
No configuration is necessary.
|
||||
|
||||
For node translation, enable the Multilingual content and the Translation
|
||||
redirect modules. For non-node pages, the redirection hook must be implemented
|
||||
by the relevant module.
|
||||
For example, for taxonomy pages, you should enable the Taxonomy translation
|
||||
module because the module provides the necessary hook code. If you are using the
|
||||
Path translation module to create translation sets for non-node pages, then it
|
||||
implements the hook code for determining the redirection page.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -4,9 +4,8 @@ dependencies[] = i18n
|
||||
package = Multilingual - Internationalization
|
||||
core = 7.x
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Multilingual select module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, allows the user to define whether
|
||||
content is filtered by language on pages provided by Drupal core.
|
||||
|
||||
* For a full description of the module visit:
|
||||
https://www.drupal.org/node/1279512.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
* Variable - https://www.drupal.org/project/variable
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
The module allows the user to configure whether or not to filter pages by the
|
||||
current language. If the Taxonomy translation submodule is also enabled, an
|
||||
option will be available for how taxonomy term pages are filtered. It also
|
||||
allows the exclusion of the module’s language selection handling for some
|
||||
elements and certain paths.
|
||||
|
||||
1. Enable the Multilingual select module included with Internationalization.
|
||||
2. Navigate to Configuration > Regional and language > Multilingual settings >
|
||||
Selection.
|
||||
3. Select nodes and taxonomy can be filtered by language in the Content to
|
||||
Filter by Language field set.
|
||||
4. The "Content Selection Mode" allows content with specific tags to be skipped
|
||||
by entering a list of tags.
|
||||
5. The "Enable for Specific Pages" field set allows specific pages to be
|
||||
included by path.
|
||||
6. After making choices, Save configuration.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -6,9 +6,8 @@ core = 7.x
|
||||
configure = admin/config/regional/i18n/select
|
||||
files[] = i18n_select.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -82,11 +82,11 @@ function i18n_select_mode($type = NULL) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current path to enable selection
|
||||
* Check current path to enable selection.
|
||||
*
|
||||
* This works pretty much like block visibility
|
||||
* This works pretty much like block visibility.
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* TRUE if content selection should be enabled for this page.
|
||||
*/
|
||||
function i18n_select_page() {
|
||||
@@ -100,8 +100,11 @@ function i18n_select_page() {
|
||||
// with different case. Ex: /Page, /page, /PAGE.
|
||||
$pages = drupal_strtolower($pages);
|
||||
if ($visibility < I18N_SELECT_PAGE_PHP) {
|
||||
// Convert the Drupal path to lowercase
|
||||
$path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
|
||||
// @see views_ajax()
|
||||
// @see I18NSelectAdminViewsAjax::testViewsAjaxWithoutSkippingTags()
|
||||
$path = isset($_REQUEST['view_path']) ? $_REQUEST['view_path'] : $_GET['q'];
|
||||
// Convert the Drupal path to lowercase.
|
||||
$path = drupal_strtolower(drupal_get_path_alias($path));
|
||||
// Compare the lowercase internal and lowercase path alias (if any).
|
||||
$page_match = drupal_match_path($path, $pages);
|
||||
if ($path != $_GET['q']) {
|
||||
@@ -109,8 +112,8 @@ function i18n_select_page() {
|
||||
}
|
||||
// When $visibility has a value of 0 (I18N_SELECT_PAGE_NOTLISTED),
|
||||
// the block is displayed on all pages except those listed in $pages.
|
||||
// When set to 1 (I18N_SELECT_PAGE_LISTED), it is displayed only on those
|
||||
// pages listed in $pages.
|
||||
// When set to 1 (I18N_SELECT_PAGE_LISTED), it is displayed only on
|
||||
// those pages listed in $pages.
|
||||
$mode = !($visibility xor $page_match);
|
||||
}
|
||||
elseif (module_exists('php')) {
|
||||
@@ -121,7 +124,7 @@ function i18n_select_page() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No pages defined, still respect the setting (unlike blocks)
|
||||
// No pages defined, still respect the setting (unlike blocks).
|
||||
$mode = $visibility == I18N_SELECT_PAGE_NOTLISTED;
|
||||
}
|
||||
}
|
||||
@@ -223,7 +226,7 @@ function _i18n_select_table_alias($table) {
|
||||
function i18n_select_check_conditions($query, $table_alias = NULL) {
|
||||
$conditions =& $query->conditions();
|
||||
foreach ($conditions as $index => $condition) {
|
||||
if (is_array($condition) && isset($condition['field'])) {
|
||||
if (is_array($condition) && !empty($condition['field'])) {
|
||||
if (strpos($condition['field'], '.') === FALSE) {
|
||||
if ($table_alias) {
|
||||
// Change the condition to include a table alias.
|
||||
|
||||
@@ -84,3 +84,87 @@ class i18nSelectTestCase extends Drupali18nTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case for AJAX queries on "views/ajax" when view on admin page.
|
||||
*/
|
||||
class I18NSelectAdminViewsAjax extends Drupali18nTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('I18N select Admin Views (AJAX)'),
|
||||
'group' => 'Internationalization',
|
||||
'description' => t('Test AJAX requests to the "views/ajax" when view located on "admin/*" and list of skipping tags is empty.'),
|
||||
// Skip this test when "admin_views" module does not exists.
|
||||
'dependencies' => array('admin_views'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp('translation', 'i18n_variable', 'i18n_select', 'admin_views');
|
||||
parent::setUpLanguages(array('access content overview'));
|
||||
parent::setUpContentTranslation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test AJAX of a view without skipping tags for selection.
|
||||
*
|
||||
* @see i18n_select_page()
|
||||
*/
|
||||
public function testViewsAjaxWithoutSkippingTags() {
|
||||
// If this variable will have the "views" value then this test will not
|
||||
// have sense. For instance, we want apply language selection filter
|
||||
// for views and remove "views" from "i18n_select_skip_tags" variable.
|
||||
// In this case all AJAX for views, on administration part of the site,
|
||||
// will be broken because the "i18n_select_page()" function will work
|
||||
// with "views/ajax" path instead of, for example, "admin/content".
|
||||
variable_set('i18n_select_skip_tags', '');
|
||||
|
||||
// Create one hundred of nodes.
|
||||
for ($i = 1; $i <= 100; $i++) {
|
||||
// Create every second node on Spanish language and
|
||||
// every first - on English.
|
||||
$node = $this->createNode('page', "Node $i", '', $i % 2 ? $this->default_language : $this->secondary_language);
|
||||
|
||||
// Update "changed" in order to sort the content by updating date. In
|
||||
// other case all nodes will be with the same date and not arranged in
|
||||
// order.
|
||||
db_update('node')
|
||||
->fields(array('changed' => strtotime("+ $i minute")))
|
||||
->condition('nid', $node->nid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/content');
|
||||
|
||||
// Check that latest node exists at the top.
|
||||
$this->assertText('Node 100');
|
||||
// Check that our page contains fifty nodes (the latest must be 51).
|
||||
$this->assertNoText('Node 50');
|
||||
|
||||
// Test $_REQUEST['view_path']. There's no form to submit to, so
|
||||
// drupalPost() won't work here. This just tests a direct $_POST
|
||||
// request instead.
|
||||
$this->curlExec(array(
|
||||
CURLOPT_URL => $this->getAbsoluteUrl('views/ajax'),
|
||||
CURLOPT_POST => TRUE,
|
||||
CURLOPT_POSTFIELDS => http_build_query(array(
|
||||
'page' => 1,
|
||||
'view_path' => 'admin/content',
|
||||
'view_name' => 'admin_views_node',
|
||||
'view_display_id' => 'system_1',
|
||||
)),
|
||||
));
|
||||
|
||||
// Check that we are successfully switched to a new page of content.
|
||||
$this->assertText('Node 50');
|
||||
$this->assertNoText('Node 100');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The String translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, provides support for other
|
||||
modules to translate user-defined strings. This is an API module that must be
|
||||
enabled only when required by other modules in the i18n package.
|
||||
|
||||
|
||||
* For a full description of the module, visit this page:
|
||||
https://www.drupal.org/node/1279668
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes:
|
||||
https://www.drupal.org/project/issues/i18n
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
Strings will be translated from the source languages. By default the source
|
||||
language is the site's default language, so changing the default language could
|
||||
break these translations.
|
||||
|
||||
1. The user can set which language is used as the source language via
|
||||
Administration > Configuration > Regional and language > Multilingual
|
||||
settings > Strings. By default, only plain strings are enabled, so regular
|
||||
blocks are not fully translatable.
|
||||
2. To allow Filtered HTML, Full HTML or Plain text select the appropriate radio
|
||||
box(es) and Save configuration.
|
||||
3. To select the strings to be translated navigate to Administration >
|
||||
Configuration > Regional and language > Translate interface and select on
|
||||
Stings vertical tab. From here the user can select which text groups to
|
||||
translate and select the Refresh strings tab.
|
||||
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
The String translation module allows you to configure which text formats are
|
||||
translatable. Formats like PHP Filter and Full HTML are translated before they
|
||||
are processed, so allowing a translator to edit these can be a security risk.
|
||||
This is particularly problematic when importing translations in bulk from a CSV
|
||||
file, since the translator's access to the import formats isn't verified by
|
||||
Drupal. After updating this setting, be sure to refresh the strings via
|
||||
Administration > Configuration > Regional and language > Translate interface >
|
||||
Strings so that strings in forbidden formats are deleted.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -28,7 +28,7 @@ class i18n_string_object {
|
||||
// Properties from metadata
|
||||
public $title;
|
||||
// Array of translations to multiple languages
|
||||
public $translations;
|
||||
public $translations = array();
|
||||
// Textgroup object
|
||||
protected $_textgroup;
|
||||
|
||||
@@ -39,7 +39,49 @@ class i18n_string_object {
|
||||
if ($data) {
|
||||
$this->set_properties($data);
|
||||
}
|
||||
// Attempt to re-build the data from the persistent cache.
|
||||
$this->rebuild_from_cache($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the object data based on the persistent cache.
|
||||
*
|
||||
* Since the textgroup defines if a string is cacheable or not the caching
|
||||
* of the string objects happens in the textgroup handler itself.
|
||||
*
|
||||
* @see i18n_string_textgroup_cached::__destruct()
|
||||
*/
|
||||
protected function rebuild_from_cache($data = NULL) {
|
||||
// Check if we've the required information to repopulate the cache and do so
|
||||
// if possible.
|
||||
$meta_data_exist = isset($this->textgroup) && isset($this->type) && isset($this->objectid) && isset($this->property);
|
||||
if ($meta_data_exist && ($cache = cache_get($this->get_cid())) && !empty($cache->data)) {
|
||||
// Re-spawn the cached data.
|
||||
// @TODO do we need a array_diff to ensure we don't overwrite the data
|
||||
// provided by the $data parameter?
|
||||
$this->set_properties($cache->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset cache, needed for tests.
|
||||
*/
|
||||
public function cache_reset() {
|
||||
$this->translations = array();
|
||||
// Ensure a possible persistent cache of this object is cleared too.
|
||||
cache_clear_all($this->get_cid(), 'cache', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caching id for this object.
|
||||
*
|
||||
* @return string
|
||||
* The caching id.
|
||||
*/
|
||||
public function get_cid() {
|
||||
return 'i18n:string:obj:' . $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message parameters from context and string.
|
||||
*/
|
||||
@@ -63,6 +105,10 @@ class i18n_string_object {
|
||||
$this->objectkey = (int)$this->objectid;
|
||||
// Remaining elements glued again with ':'
|
||||
$this->property = $parts ? implode(':', $parts) : '';
|
||||
|
||||
// Attempt to re-build the other data from the persistent cache.
|
||||
$this->rebuild_from_cache();
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
@@ -100,13 +146,14 @@ class i18n_string_object {
|
||||
if (isset($string['format'])) {
|
||||
$this->format = $string['format'];
|
||||
}
|
||||
if (isset($string['title'])) {
|
||||
$this->title = $string['title'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->string = $string;
|
||||
}
|
||||
if (isset($string['title'])) {
|
||||
$this->title = $string['title'];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
@@ -279,9 +326,9 @@ class i18n_string_textgroup_default {
|
||||
// Debug flag, set to true to print out more information.
|
||||
public $debug;
|
||||
// Cached or preloaded string objects
|
||||
public $strings;
|
||||
public $strings = array();
|
||||
// Multiple translations search map
|
||||
protected $cache_multiple;
|
||||
protected $cache_multiple = array();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
@@ -400,8 +447,13 @@ class i18n_string_textgroup_default {
|
||||
/**
|
||||
* Filter array of strings
|
||||
*
|
||||
* @param $filter
|
||||
* @param array $string_list
|
||||
* Array of strings to be filtered.
|
||||
* @param array $filter
|
||||
* Array of name value conditions.
|
||||
*
|
||||
* @return array
|
||||
* Strings from $string_list that match the filter conditions.
|
||||
*/
|
||||
protected static function string_filter($string_list, $filter) {
|
||||
// Remove 'language' and '*' conditions.
|
||||
@@ -566,7 +618,11 @@ class i18n_string_textgroup_default {
|
||||
public function cache_reset() {
|
||||
$this->strings = array();
|
||||
$this->string_format = array();
|
||||
$this->translations = array();
|
||||
|
||||
// Reset the persistent caches.
|
||||
cache_clear_all('i18n:string:tgroup:' . $this->textgroup , 'cache', TRUE);
|
||||
// Reset the complete string object cache too.
|
||||
cache_clear_all('i18n:string:obj:', 'cache', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -613,7 +669,7 @@ class i18n_string_textgroup_default {
|
||||
public static function load_translation($i18nstring, $langcode) {
|
||||
// Search the database using lid if we've got it or textgroup, context otherwise
|
||||
if (!empty($i18nstring->lid)) {
|
||||
// We've alreay got lid, we just need translation data
|
||||
// We've already got lid, we just need translation data
|
||||
$query = db_select('locales_target', 't');
|
||||
$query->condition('t.lid', $i18nstring->lid);
|
||||
}
|
||||
@@ -647,9 +703,19 @@ class i18n_string_textgroup_default {
|
||||
// Create source string so we get an lid
|
||||
$this->save_source($string);
|
||||
}
|
||||
|
||||
// Convert objectid to objectkey if it's numeric.
|
||||
if (!isset($string->objectkey)) {
|
||||
$string->objectkey = (int)$string->objectid;
|
||||
if (is_numeric($string->objectid)) {
|
||||
$string->objectkey = (int)$string->objectid;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure objectkey is numeric.
|
||||
if (!is_numeric($string->objectkey)) {
|
||||
$string->objectkey = 0;
|
||||
}
|
||||
|
||||
if (!isset($string->format)) {
|
||||
$string->format = '';
|
||||
}
|
||||
@@ -1110,10 +1176,17 @@ class i18n_string_object_wrapper extends i18n_object_wrapper {
|
||||
$info = is_array($info) ? $info : array('title' => $info);
|
||||
$field_name = isset($info['field']) ? $info['field'] : $field;
|
||||
$value = $this->get_field($field_name);
|
||||
if (is_array($value) && isset($value['value'])) {
|
||||
$format = isset($value['format']) ? $value['format'] : NULL;
|
||||
$value = $value['value'];
|
||||
}
|
||||
else {
|
||||
$format = isset($info['format']) ? $this->get_field($info['format']) : NULL;
|
||||
}
|
||||
$strings[$this->get_textgroup()][$string_type][$object_id][$field] = array(
|
||||
'string' => is_array($value) || isset($info['empty']) && $value === $info['empty'] ? NULL : $value,
|
||||
'title' => $info['title'],
|
||||
'format' => isset($info['format']) ? $this->get_field($info['format']) : NULL,
|
||||
'format' => $format,
|
||||
'name' => array_merge($object_keys, array($field)),
|
||||
);
|
||||
}
|
||||
@@ -1188,6 +1261,10 @@ class i18n_string_object_wrapper extends i18n_object_wrapper {
|
||||
* Translate access (localize strings)
|
||||
*/
|
||||
protected function localize_access() {
|
||||
// We could check also whether the object has strings to translate:
|
||||
// && $this->get_strings(array('empty' => TRUE))
|
||||
// However it may be better to display the 'No available strings' message
|
||||
// for the user to have a clue of what's going on. See i18n_string_translate_page_object()
|
||||
return user_access('translate interface') && user_access('translate user-defined strings');
|
||||
}
|
||||
|
||||
@@ -1280,3 +1357,168 @@ class i18n_string_object_wrapper extends i18n_object_wrapper {
|
||||
return $this->textgroup()->load_strings(array('type' => $type, 'objectid' => $id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Textgroup handler for i18n_string API which integrated persistent caching.
|
||||
*/
|
||||
class i18n_string_textgroup_cached extends i18n_string_textgroup_default {
|
||||
|
||||
/**
|
||||
* Defines the timeout for the persistent caching.
|
||||
* @var int
|
||||
*/
|
||||
public $caching_time = CACHE_TEMPORARY;
|
||||
|
||||
/**
|
||||
* Extends the existing constructor with a cache handling.
|
||||
*
|
||||
* @param string $textgroup
|
||||
* The name of this textgroup.
|
||||
*/
|
||||
public function __construct($textgroup) {
|
||||
parent::__construct($textgroup);
|
||||
// Fetch persistent caches, the persistent caches contain only metadata.
|
||||
// Those metadata are processed by the related cache_get() methods.
|
||||
foreach (array('cache_multiple', 'strings') as $caches_type) {
|
||||
if (($cache = cache_get('i18n:string:tgroup:' . $this->textgroup . ':' . $caches_type)) && !empty($cache->data)) {
|
||||
$this->{$caches_type} = $cache->data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class destructor.
|
||||
*
|
||||
* Updates the persistent caches for the next usage.
|
||||
* This function not only stores the data of the textgroup objects but also
|
||||
* of the string objects. That way we ensure that only cacheable string object
|
||||
* go into the persistent cache.
|
||||
*/
|
||||
public function __destruct() {
|
||||
// Reduce size to cache by removing NULL values.
|
||||
$this->strings = array_filter($this->strings);
|
||||
|
||||
|
||||
$strings_to_cache = array();
|
||||
// Store the persistent caches. We just store the metadata the translations
|
||||
// are stored by the string object itself. However storing the metadata
|
||||
// reduces the number of DB queries executed during runtime.
|
||||
$cache_data = array();
|
||||
foreach ($this->strings as $context => $i18n_string_object) {
|
||||
$cache_data[$context] = $context;
|
||||
$strings_to_cache[$context] = $i18n_string_object;
|
||||
}
|
||||
cache_set('i18n:string:tgroup:' . $this->textgroup . ':strings', $cache_data, 'cache', $this->caching_time);
|
||||
|
||||
$cache_data = array();
|
||||
foreach ($this->cache_multiple as $pattern => $strings) {
|
||||
foreach ($strings as $context => $i18n_string_object) {
|
||||
$cache_data[$pattern][$context] = $context;
|
||||
$strings_to_cache[$context] = $i18n_string_object;
|
||||
}
|
||||
}
|
||||
cache_set('i18n:string:tgroup:' . $this->textgroup . ':cache_multiple', $cache_data, 'cache', $this->caching_time);
|
||||
|
||||
// Cache the string objects related to this textgroup.
|
||||
// Store only the public visible data into the persistent cache.
|
||||
foreach ($strings_to_cache as $i18n_string_object) {
|
||||
// If this isn't an object it's an unprocessed cache item and doesn't need
|
||||
// to be stored again.
|
||||
if (is_object($i18n_string_object)) {
|
||||
cache_set($i18n_string_object->get_cid(), get_object_vars($i18n_string_object), 'cache', $this->caching_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset cache, needed for tests.
|
||||
*
|
||||
* Takes care of the persistent caches.
|
||||
*/
|
||||
public function cache_reset() {
|
||||
// Reset the persistent caches.
|
||||
cache_clear_all('i18n:string:tgroup:' . $this->textgroup , 'cache', TRUE);
|
||||
// Reset the complete string object cache too. This will affect string
|
||||
// objects of other textgroups as well.
|
||||
cache_clear_all('i18n:string:obj:', 'cache', TRUE);
|
||||
|
||||
return parent::cache_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translation from cache.
|
||||
*
|
||||
* Extends the original handler with persistent caching.
|
||||
*
|
||||
* @param string $context
|
||||
* The context to look out for.
|
||||
* @return i18n_string_object|NULL
|
||||
* The string object if available or NULL otherwise.
|
||||
*/
|
||||
protected function cache_get($context) {
|
||||
if (isset($this->strings[$context])) {
|
||||
// If the cache contains a string re-build i18n_string_object.
|
||||
if (is_string($this->strings[$context])) {
|
||||
$i8n_string_object = new i18n_string_object(array('textgroup' => $this->textgroup));
|
||||
$i8n_string_object->set_context($context);
|
||||
$this->strings[$context] = $i8n_string_object;
|
||||
}
|
||||
// Now run the original handling.
|
||||
return parent::cache_get($context);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get strings from multiple cache.
|
||||
*
|
||||
* @param $context array
|
||||
* String context as array with language property at the end.
|
||||
*
|
||||
* @return mixed
|
||||
* Array of strings (may be empty) if we've got a cache hit.
|
||||
* Null otherwise.
|
||||
*/
|
||||
protected function multiple_cache_get($context) {
|
||||
// Ensure the values from the persistent cache are properly re-build.
|
||||
$cache_key = implode(':', $context);
|
||||
if (isset($this->cache_multiple[$cache_key])) {
|
||||
foreach ($this->cache_multiple[$cache_key] as $cached_context) {
|
||||
if (is_string($cached_context)) {
|
||||
$i8n_string_object = new i18n_string_object(array('textgroup' => $this->textgroup));
|
||||
$i8n_string_object->set_context($cached_context);
|
||||
$this->cache_multiple[$cache_key][$cached_context] = $i8n_string_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Now we try more generic keys. For instance, if we are searching 'term:1:*'
|
||||
// we may try too 'term:*:*' and filter out the results.
|
||||
foreach ($context as $key => $value) {
|
||||
if ($value != '*') {
|
||||
$try = array_merge($context, array($key => '*'));
|
||||
$cached_results = $this->multiple_cache_get($try);
|
||||
// Now filter the ones that actually match.
|
||||
if (!empty($cached_results)) {
|
||||
$cached_results = $this->string_filter($cached_results, $context);
|
||||
}
|
||||
return $cached_results;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parent::multiple_cache_get($context);
|
||||
}
|
||||
|
||||
public function string_update($i18nstring, $options = array()) {
|
||||
// Flush persistent cache.
|
||||
cache_clear_all($i18nstring->get_cid(), 'cache', TRUE);
|
||||
return parent::string_update($i18nstring, $options);
|
||||
}
|
||||
|
||||
public function string_remove($i18nstring, $options = array()) {
|
||||
// Flush persistent cache.
|
||||
cache_clear_all($i18nstring->get_cid(), 'cache', TRUE);
|
||||
return parent::string_remove($i18nstring, $options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ files[] = i18n_string.inc
|
||||
files[] = i18n_string.test
|
||||
configure = admin/config/regional/i18n/strings
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -28,6 +28,10 @@ function i18n_string_install() {
|
||||
i18n_string_update_7000();
|
||||
i18n_string_update_7001();
|
||||
}
|
||||
// Create new index in {locales_source}, performance improvement in sites with i18n.
|
||||
if (!db_index_exists('locales_source', 'textgroup_context')) {
|
||||
db_add_index('locales_source', 'textgroup_context', array('textgroup', array('context', 50)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,6 +40,8 @@ function i18n_string_install() {
|
||||
function i18n_string_uninstall() {
|
||||
// Drop custom field.
|
||||
db_drop_field('locales_target', 'i18n_status');
|
||||
// Drop custom index in locales_source table
|
||||
db_drop_index('locales_source', 'textgroup_context');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +94,7 @@ function i18n_string_schema() {
|
||||
),
|
||||
'objectindex' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Integer value of Object ID.',
|
||||
@@ -102,7 +109,7 @@ function i18n_string_schema() {
|
||||
),
|
||||
'primary key' => array('lid'),
|
||||
'indexes' => array(
|
||||
'group_context' => array('textgroup', 'context'),
|
||||
'group_context' => array('textgroup', array('context', 50)),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
@@ -230,6 +237,34 @@ function i18n_string_update_7001() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new index in {locales_source}, performance improvement in sites with i18n.
|
||||
*/
|
||||
function i18n_string_update_7002() {
|
||||
if (!db_index_exists('locales_source', 'textgroup_context')) {
|
||||
db_add_index('locales_source', 'textgroup_context', array('textgroup', array('context', 50)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed due to buggy upgrade for #2200647.
|
||||
*/
|
||||
function i18n_string_update_7003() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Change objectindex from int to bigint.
|
||||
*/
|
||||
function i18n_string_update_7004() {
|
||||
db_change_field('i18n_string', 'objectindex', 'objectindex', array(
|
||||
'type' => 'int',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Integer value of Object ID.',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes for update script
|
||||
*/
|
||||
@@ -257,4 +292,4 @@ function i18n_string_update_7001() {
|
||||
* Node type
|
||||
* nodetype:type:[type]:[property] -> node:type:[type]:[property]
|
||||
* Property names: title -> title_label
|
||||
*/
|
||||
*/
|
||||
|
||||
@@ -118,13 +118,14 @@ function i18n_string_menu() {
|
||||
'file' => 'i18n_string.admin.inc',
|
||||
'access arguments' => array('translate interface'),
|
||||
);
|
||||
|
||||
$items['admin/config/regional/i18n/strings'] = array(
|
||||
'title' => 'Strings',
|
||||
'description' => 'Options for user defined strings.',
|
||||
'weight' => 20,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('variable_edit_form', array('i18n_string_allowed_formats', 'i18n_string_source_language')),
|
||||
'page arguments' => array('variable_edit_form', array('i18n_string_allowed_formats', 'i18n_string_source_language', 'i18n_string_textgroup_class_[textgroup]')),
|
||||
'access arguments' => array('administer site configuration'),
|
||||
);
|
||||
// AJAX callback path for strings.
|
||||
@@ -258,6 +259,34 @@ function i18n_string_locale_translate_import_form_submit($form, &$form_state) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_element_info_alter().
|
||||
*
|
||||
* We need to do this on the element info level as wysiwyg also does so and form
|
||||
* API (incorrectly) does not merge in the defaults for values that are arrays.
|
||||
*/
|
||||
function i18n_string_element_info_alter(&$types) {
|
||||
$types['text_format']['#pre_render'][] = 'i18n_string_pre_render_text_format';
|
||||
}
|
||||
|
||||
/**
|
||||
* The '#pre_render' function to alter the text format element in a translation.
|
||||
* The text format for a translation is taken form the original, so the text
|
||||
* format drop down should be disabled.
|
||||
*
|
||||
* @param array $element
|
||||
* The text_format element which will be rendered.
|
||||
*
|
||||
* @return array
|
||||
* The altered text_format element with a disabled "Text format" select.
|
||||
*/
|
||||
function i18n_string_pre_render_text_format($element) {
|
||||
if (!empty($element['#i18n_string_is_translation'])) {
|
||||
$element['format']['format']['#attributes']['disabled'] = TRUE;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if translation is required for this language code.
|
||||
*
|
||||
@@ -334,7 +363,10 @@ function i18n_string_update_context($oldname, $newname) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get textgroup handler
|
||||
* Get textgroup handler.
|
||||
*
|
||||
* @return i18n_string_textgroup_default
|
||||
*
|
||||
*/
|
||||
function i18n_string_textgroup($textgroup) {
|
||||
$groups = &drupal_static(__FUNCTION__);
|
||||
@@ -488,6 +520,19 @@ function i18n_string_group_info($group = NULL, $property = NULL, $default = NULL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_i18n_string_info_alter().
|
||||
*
|
||||
* Set determined classes to use for the text group.
|
||||
*/
|
||||
function i18n_string_i18n_string_info_alter(&$info) {
|
||||
foreach (array_keys($info) as $name) {
|
||||
// If class is not defined. Classes from other modules, fixed classes and etc.
|
||||
if (!isset($info[$name]['class'])) {
|
||||
$info[$name]['class'] = variable_get('i18n_string_textgroup_class_' . $name, 'i18n_string_textgroup_default');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate / update multiple strings
|
||||
@@ -519,11 +564,12 @@ function i18n_string_multiple($operation, $name, $strings, $options = array()) {
|
||||
*
|
||||
* This function is intended to return translations for plain strings that have NO text format
|
||||
*
|
||||
* @param $name
|
||||
* @param array|string name
|
||||
* Array or string concatenated with ':' that contains textgroup and string context
|
||||
* @param $string
|
||||
* String in default language or array of strings to be translated
|
||||
* @param $options
|
||||
* @param array|string $string
|
||||
* A string in the default language, a string wth format (array with keys
|
||||
* value and format),or an array of strings (without format) to be translated.
|
||||
* @param array $options
|
||||
* An associative array of additional options, with the following keys:
|
||||
* - 'langcode' (defaults to the current language) The language code to translate to a language other than what is used to display the page.
|
||||
* - 'filter' Filtering callback to apply to the translated string only
|
||||
@@ -531,8 +577,13 @@ function i18n_string_multiple($operation, $name, $strings, $options = array()) {
|
||||
* - 'callback' Callback to apply to the result (both to translated or untranslated string
|
||||
* - 'sanitize' Whether to filter the translation applying the text format if any, default is TRUE
|
||||
* - 'sanitize default' Whether to filter the default value if no translation found, default is FALSE
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function i18n_string_translate($name, $string, $options = array()) {
|
||||
if (is_array($string) && isset($string['value'])) {
|
||||
$string = $string['value'];
|
||||
}
|
||||
if (is_array($string)) {
|
||||
return i18n_string_translate_list($name, $string, $options);
|
||||
}
|
||||
@@ -583,10 +634,17 @@ function i18n_string_translate_access($string_format, $account = NULL) {
|
||||
* Message if the user cannot translate that string.
|
||||
*/
|
||||
function i18n_string_translate_check_string($i18nstring, $account = NULL) {
|
||||
if (!user_access('translate interface', $account) || !user_access('translate user-defined strings', $account)) {
|
||||
// Check block translation permissions.
|
||||
if ($i18nstring->textgroup == 'blocks') {
|
||||
if (!user_access('translate interface', $account) && !user_access('translate blocks', $account)) {
|
||||
return t('This is a user-defined string within a block. You are not allowed to translate blocks.');
|
||||
}
|
||||
}
|
||||
elseif (!user_access('translate interface', $account) || !user_access('translate user-defined strings', $account)) {
|
||||
return t('This is a user-defined string. You are not allowed to translate these strings.');
|
||||
}
|
||||
elseif (!empty($i18nstring->format)) {
|
||||
|
||||
if (!empty($i18nstring->format)) {
|
||||
if (!i18n_string_allowed_format($i18nstring->format)) {
|
||||
$format = filter_format_load($i18nstring->format);
|
||||
return t('This string uses the %name text format. Strings with this format are not allowed for translation.', array('%name' => $format->name));
|
||||
|
||||
@@ -12,7 +12,14 @@ include_once DRUPAL_ROOT . '/includes/locale.inc';
|
||||
include_once drupal_get_path('module', 'locale') . '/locale.admin.inc';
|
||||
|
||||
/**
|
||||
* Generate translate page from object
|
||||
* Generate translate page from object.
|
||||
*
|
||||
* @param string $object_type
|
||||
* Obejct type as declared in hook_i18n_object_info().
|
||||
* @param object $object_value
|
||||
* Drupal object to translate.
|
||||
* @param object $language
|
||||
* Optional language object.
|
||||
*/
|
||||
function i18n_string_translate_page_object($object_type, $object_value, $language = NULL) {
|
||||
// For backwards compatibility, ensure parameter is a language object
|
||||
@@ -22,6 +29,13 @@ function i18n_string_translate_page_object($object_type, $object_value, $languag
|
||||
$object = i18n_object($object_type, $object_value);
|
||||
$strings = $object->get_strings(array('empty' => TRUE));
|
||||
|
||||
// If no localizable strings, print message and fail gracefully.
|
||||
// Possibly this object comes from some other contrib module.
|
||||
// See http://drupal.org/node/1889878
|
||||
if (!$strings) {
|
||||
return t('This object has no strings available for translation.');
|
||||
}
|
||||
|
||||
if (empty($langcode)) {
|
||||
drupal_set_title(t('Translate !name', array('!name' => i18n_object_info($object_type, 'title'))));
|
||||
return i18n_string_translate_page_overview($object, $strings);
|
||||
@@ -44,7 +58,6 @@ function i18n_string_translate_page_overview($object, $strings) {
|
||||
* Provide a core translation module like overview page for this object.
|
||||
*/
|
||||
function i18n_string_translate_page_overview_form($form, &$form_state, $object, $strings) {
|
||||
//include_once DRUPAL_ROOT . '/includes/language.inc';
|
||||
// Set the default item key, assume it's the first.
|
||||
$item_title = reset($strings);
|
||||
$header = array(
|
||||
@@ -157,14 +170,16 @@ function i18n_string_translate_page_form_base($form, $langcode, $redirect = NULL
|
||||
|
||||
/**
|
||||
* Create field elements for strings
|
||||
*
|
||||
* @param i18n_string_object[] $strings
|
||||
* @param string $langcode
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function i18n_string_translate_page_form_strings($strings, $langcode) {
|
||||
$formats = filter_formats();
|
||||
global $user;
|
||||
$form = array();
|
||||
foreach ($strings as $item) {
|
||||
// We may have a source or not. Load it, our string may get the format from it.
|
||||
$source = $item->get_source();
|
||||
$format_id = $source ? $source->format : $item->format;
|
||||
$description = '';
|
||||
// Check permissions to translate this string, depends on format, etc..
|
||||
if ($message = $item->check_translate_access()) {
|
||||
// We'll display a disabled element with the reason it cannot be translated.
|
||||
@@ -175,27 +190,31 @@ function i18n_string_translate_page_form_strings($strings, $langcode) {
|
||||
$disabled = FALSE;
|
||||
$description = '';
|
||||
// If we don't have a source and it can be translated, we create it.
|
||||
if (!$source) {
|
||||
if (!$item->get_source()) {
|
||||
// Enable messages just as a reminder these strings are not being updated properly.
|
||||
$status = $item->update(array('messages' => TRUE));
|
||||
if ($status === FALSE || $status === SAVED_DELETED) {
|
||||
// We don't have a source string so nothing to translate here
|
||||
$disabled = TRUE;
|
||||
}
|
||||
else {
|
||||
$source = $item->get_source();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$default_value = $item->format_translation($langcode, array('langcode' => $langcode, 'sanitize' => FALSE, 'debug' => FALSE));
|
||||
$available_formats = array_keys(filter_formats($user));
|
||||
if (!in_array($item->format, $available_formats)) {
|
||||
$item->format = NULL;
|
||||
}
|
||||
$form[$item->get_name()] = array(
|
||||
'#title' => $item->get_title(),
|
||||
'#type' => 'textarea',
|
||||
'#type' => $item->format ? 'text_format' : 'textarea',
|
||||
'#default_value' => $default_value,
|
||||
'#format' => $item->format,
|
||||
// This will trigger i18n_string_pre_render_text_format() to actually
|
||||
// alter the element.
|
||||
'#i18n_string_is_translation' => TRUE,
|
||||
'#disabled' => $disabled,
|
||||
'#description' => $description . _i18n_string_translate_format_help($format_id),
|
||||
//'#i18n_string_format' => $source ? $source->format : 0,
|
||||
'#description' => $description,
|
||||
// If disabled, provide smaller textarea (that can be expanded anyway).
|
||||
'#rows' => $disabled ? 1 : min(ceil(str_word_count($default_value) / 12), 10),
|
||||
// Change the parent for disabled strings so we don't get empty values later
|
||||
@@ -213,6 +232,16 @@ function i18n_string_translate_page_form_submit($form, &$form_state) {
|
||||
foreach ($form_state['values']['strings'] as $name => $value) {
|
||||
$count++;
|
||||
list($textgroup, $context) = i18n_string_context(explode(':', $name));
|
||||
if (is_array($value)) {
|
||||
if (isset($value['value'])) {
|
||||
$value = $value['value'];
|
||||
$form_state['values']['strings'][$name] = $value;
|
||||
}
|
||||
else {
|
||||
form_set_error("strings][$name", t('Unable to get the translated string value.'));
|
||||
watchdog('locale', 'Unable to get the translated string value, string array is: %string', array('%string' => var_dump($value)), WATCHDOG_WARNING);
|
||||
}
|
||||
}
|
||||
$result = i18n_string_textgroup($textgroup)->update_translation($context, $form_state['values']['langcode'], $value);
|
||||
$success += ($result ? 1 : 0);
|
||||
}
|
||||
@@ -385,6 +414,9 @@ function i18n_string_locale_translate_edit_form_submit($form, &$form_state) {
|
||||
// Invoke locale submission.
|
||||
locale_translate_edit_form_submit($form, $form_state);
|
||||
$lid = $form_state['values']['lid'];
|
||||
if ($i18n_string_object = i18n_string_get_by_lid($lid)) {
|
||||
$i18n_string_object->cache_reset();
|
||||
}
|
||||
foreach ($form_state['values']['translations'] as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
// An update has been made, so we assume the translation is now current.
|
||||
|
||||
@@ -24,7 +24,7 @@ class i18nStringTestCase extends Drupali18nTestCase {
|
||||
|
||||
function setUp() {
|
||||
// We can use any of the modules that define a text group, to use it for testing
|
||||
parent::setUp('i18n_string', 'i18n_menu');
|
||||
parent::setUp('i18n_string', 'i18n_menu', 'i18n_test');
|
||||
parent::setUpLanguages();
|
||||
$this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
|
||||
}
|
||||
@@ -53,8 +53,130 @@ class i18nStringTestCase extends Drupali18nTestCase {
|
||||
$this->assertEqual($translation, $translations[$key][$language->language], "The right $language->name ($language->language) translation has been retrieved for $name, $translation");
|
||||
}
|
||||
}
|
||||
|
||||
// Test that regular strings can be translated. Use 'Built-in interface' as
|
||||
// filter, and translate first one.
|
||||
$search = array(
|
||||
'language' => 'all',
|
||||
'translation' => 'all',
|
||||
'group' => 'default',
|
||||
'string' => '',
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
|
||||
$this->clickLink(t('edit'));
|
||||
// Just add a random translation.
|
||||
$translation = $this->randomName();
|
||||
$edit = array();
|
||||
foreach ($this->getOtherLanguages() as $language) {
|
||||
$langcode = $language->language;
|
||||
$edit["translations[$langcode]"] = $translation;
|
||||
}
|
||||
$this->drupalPost(NULL, $edit, t('Save translations'));
|
||||
$this->assertText(t('The string has been saved.'), t('The string has been saved.'));
|
||||
$this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test base i18n_string caching.
|
||||
*/
|
||||
function testCaching() {
|
||||
// Create a bunch of strings for all languages.
|
||||
$textgroup = 'test_cached';
|
||||
$strings = $this->stringCreateArray(2);
|
||||
$translations = array();
|
||||
$textgroup_object = i18n_string_textgroup($textgroup);
|
||||
// Save source strings and store translations.
|
||||
foreach ($strings as $key => $string) {
|
||||
$name = "$textgroup:item:$key:title";
|
||||
i18n_string_update($name, $string);
|
||||
$translations[$key] = $this->createStringTranslation($textgroup, $string);
|
||||
}
|
||||
|
||||
// Now fetch the strings to fill the cache.
|
||||
foreach ($textgroup_object->strings as $context => $string_object) {
|
||||
$this->drupalGet('tests/i18n/i18n_string_build/' . $textgroup . ':' . $context);
|
||||
}
|
||||
foreach ($strings as $key => $string) {
|
||||
$this->drupalGet('tests/i18n/i18n_string_translation_search/' . $textgroup . ':item:' . $key . ':*/es');
|
||||
}
|
||||
|
||||
// Check the persistent cache for contents.
|
||||
$cache = cache_get('i18n:string:tgroup:' . $textgroup . ':strings');
|
||||
if ($this->assertNotEqual($cache, FALSE, 'Textgroup strings cache found')) {
|
||||
foreach ($textgroup_object->strings as $context => $string_object) {
|
||||
if ($this->assertTrue(isset($cache->data[$context]), format_string('Cached string %context found', array('%context' => $context)))) {
|
||||
$this->assertEqual($cache->data[$context], $context, 'Cached string is a string and not an object');
|
||||
}
|
||||
// Check if the string object cache is also available.
|
||||
$string_cache = cache_get($string_object->get_cid());
|
||||
if ($this->assertNotEqual($string_cache, FALSE, format_string('Cached string object %cid found', array('%cid' => $string_object->get_cid())))) {
|
||||
$this->assertTrue(is_array($string_cache->data), 'Cached string object is an array.');
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache = cache_get('i18n:string:tgroup:' . $textgroup . ':cache_multiple');
|
||||
if ($this->assertNotEqual($cache, FALSE, 'Textgroup cache_multiple cache found')) {
|
||||
foreach ($strings as $key => $string) {
|
||||
$pattern = 'item:' . $key . ':*:es';
|
||||
if ($this->assertTrue(isset($cache->data[$pattern]), format_string('Cached multiple cache for pattern %pattern found', array('%pattern_key' => $pattern)))) {
|
||||
$property_pattern = 'item:' . $key . ':title';
|
||||
if ($this->assertTrue(isset($cache->data[$pattern][$property_pattern]), format_string('Cached multiple property title found', array('%pattern_key' => $pattern)))) {
|
||||
$this->assertEqual($cache->data[$pattern][$property_pattern], $property_pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache injection.
|
||||
foreach ($textgroup_object->strings as $context => $string_object) {
|
||||
// Check if the string object cache is also available.
|
||||
$string_cache = cache_get($string_object->get_cid());
|
||||
if (isset($string_cache->data)) {
|
||||
// Modify cache.
|
||||
$string_cache->data['string'] = "Injected value.";
|
||||
cache_set($string_object->get_cid(), $string_cache->data, 'cache', CACHE_TEMPORARY);
|
||||
|
||||
// Check if modification is reflected on the next page call.
|
||||
$this->drupalGet('tests/i18n/i18n_string_build/' . $textgroup . ':' . $context);
|
||||
$this->assertText($string_cache->data['string']);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that un-translated strings are cached correctly.
|
||||
$textgroup = 'test_cached';
|
||||
$key = 3;
|
||||
$string = self::randomName(100);
|
||||
$name = "$textgroup:item:$key:title";
|
||||
i18n_string_update($name, $string);
|
||||
|
||||
// Generate the cache entry.
|
||||
$string_object = i18n_string_build($name, $string);
|
||||
$langcode = i18n_langcode();
|
||||
$string_object->get_translation($langcode);
|
||||
|
||||
// Destroy the textgroup object to write the cache entry.
|
||||
$textgroup_object = i18n_string_textgroup($textgroup);
|
||||
$textgroup_object->__destruct();
|
||||
$this->assertTrue(cache_get($string_object->get_cid()) !== FALSE, "Cache entry created.");
|
||||
drupal_static_reset('i18n_string_textgroup');
|
||||
|
||||
// Reset the loaded translation variable.
|
||||
variable_del('i18n_loaded_translations');
|
||||
$loaded_translations = variable_get('i18n_loaded_translations', array());
|
||||
$this->verbose(var_export($loaded_translations, TRUE));
|
||||
|
||||
// Rebuild the string.
|
||||
$string_object = i18n_string_build($name, $string);
|
||||
$string_object->get_translation($langcode);
|
||||
|
||||
// Check that the string hasn't been loaded.
|
||||
$loaded_translations = variable_get('i18n_loaded_translations', array());
|
||||
$this->verbose(var_export($loaded_translations, TRUE));
|
||||
$this->assertFalse(isset($loaded_translations['test_cached:item:3:title']), "The untranslated string was correctly cached.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create strings for all languages
|
||||
*/
|
||||
@@ -76,7 +198,7 @@ class i18nStringTestCase extends Drupali18nTestCase {
|
||||
/**
|
||||
* Create and store one translation into the db
|
||||
*/
|
||||
public static function stringCreateTranslation($name, $lang, $length = 20) {
|
||||
public function stringCreateTranslation($name, $lang, $length = 20) {
|
||||
$translation = $this->randomName($length);
|
||||
if (self::stringSaveTranslation($name, $lang, $translation)) {
|
||||
return $translation;
|
||||
|
||||
@@ -34,9 +34,32 @@ function i18n_string_variable_info($options = array()) {
|
||||
'default' => 0,
|
||||
'group' => 'debug',
|
||||
);
|
||||
$variables['i18n_string_textgroup_class_[textgroup]'] = array(
|
||||
'title' => t('Class to use for the text group'),
|
||||
'description' => t('Determines which the class will be use for string translation in the text group.', array(), $options),
|
||||
'repeat' => array(
|
||||
'type' => 'select',
|
||||
'default' => 'i18n_string_textgroup_default',
|
||||
'options callback' => 'i18n_string_variable_textgroup_class_list',
|
||||
),
|
||||
'submit callback' => 'i18n_string_variable_textgroup_class_submit_callback',
|
||||
'group' => 'i18n',
|
||||
);
|
||||
return $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_variable_type_info().
|
||||
*/
|
||||
function i18n_string_variable_type_info() {
|
||||
$type['textgroup'] = array(
|
||||
'title' => t('Text group'),
|
||||
'type' => 'select',
|
||||
'options callback' => 'i18n_string_variable_textgroup_list',
|
||||
);
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options callback, format list
|
||||
*/
|
||||
@@ -54,4 +77,33 @@ function i18n_string_variable_format_list() {
|
||||
*/
|
||||
function i18n_string_variable_format_default() {
|
||||
return array(filter_fallback_format());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options callback, text groups list.
|
||||
*/
|
||||
function i18n_string_variable_textgroup_list() {
|
||||
$groups = array();
|
||||
foreach (i18n_string_group_info() as $name => $info) {
|
||||
$groups[$name] = $info['title'];
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options callback, text group classes list.
|
||||
*/
|
||||
function i18n_string_variable_textgroup_class_list($variable, $options = array()) {
|
||||
return array(
|
||||
'i18n_string_textgroup_default' => t('Text group handler default.', array(), $options),
|
||||
'i18n_string_textgroup_cached' => t('Text group handler which include persistent caching.', array(), $options),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback. Execute Reset the persistent caches after save the text group class variables.
|
||||
*/
|
||||
function i18n_string_variable_textgroup_class_submit_callback($variable, $options, $form, $form_state) {
|
||||
// Reset the persistent caches.
|
||||
cache_clear_all('i18n:string:' , 'cache', TRUE);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ README.txt
|
||||
==========
|
||||
Drupal module: i18n_sync (Synchronization)
|
||||
|
||||
This module will handle content synchronization accross translations.
|
||||
This module will handle content synchronization across translations.
|
||||
|
||||
The available list of fields to synchronize will include standard node fields and cck fields.
|
||||
To have aditional fields, add the list in a variable in the settings.php file, like this:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name = Synchronize translations
|
||||
description = Synchronizes taxonomy and fields accross translations of the same content.
|
||||
description = Synchronizes taxonomy and fields across translations of the same content.
|
||||
dependencies[] = i18n
|
||||
dependencies[] = translation
|
||||
package = Multilingual - Internationalization
|
||||
@@ -10,9 +10,8 @@ files[] = i18n_sync.install
|
||||
files[] = i18n_sync.module.inc
|
||||
files[] = i18n_sync.node.inc
|
||||
files[] = i18n_sync.test
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
*
|
||||
* Notes:
|
||||
* This module needs to run after taxonomy, i18n, translation. Check module weight.
|
||||
*
|
||||
* @ TODO Test with CCK when possible, api may have changed.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -35,7 +33,7 @@ function i18n_sync($status = NULL) {
|
||||
function i18n_sync_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#i18n_sync' :
|
||||
$output = '<p>' . t('This module synchronizes content taxonomy and fields accross translations:') . '</p>';
|
||||
$output = '<p>' . t('This module synchronizes content taxonomy and fields across translations:') . '</p>';
|
||||
$output .= '<p>' . t('First you need to select which fields should be synchronized. Then, after a node has been updated, all enabled vocabularies and fields will be synchronized as follows:') . '</p>';
|
||||
$output .= '<ul>';
|
||||
$output .= '<li>' . t('All the node fields selected for synchronization will be set to the same value for all translations.') . '</li>';
|
||||
@@ -71,25 +69,6 @@ function i18n_sync_field_info_alter(&$fields) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function i18n_sync_form_node_admin_content_alter(&$form, &$form_state) {
|
||||
if (!empty($form['operation']) && $form['operation']['#value'] == 'delete') {
|
||||
$form['#submit'] = array_merge(array('i18n_sync_node_delete_submit'), $form['#submit']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function i18n_sync_form_node_delete_confirm_alter(&$form, &$form_state) {
|
||||
// Intercept form submission so we can handle uploads, replace callback
|
||||
$form['#submit'] = array_merge(array('i18n_sync_node_delete_submit'), $form['#submit']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
@@ -112,6 +91,7 @@ function i18n_sync_form_node_type_form_alter(&$form, &$form_state) {
|
||||
|
||||
$form['i18n_sync']['i18n_sync_node_type'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#weight' => 1,
|
||||
);
|
||||
|
||||
// Each set provides title and options. We build a big checkboxes control for it to be
|
||||
@@ -142,42 +122,21 @@ function i18n_sync_form_node_type_form_alter(&$form, &$form_state) {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for
|
||||
* - node delete confirm
|
||||
* - node multiple delete confirm
|
||||
*/
|
||||
function i18n_sync_node_delete_submit($form, $form_state) {
|
||||
if ($form_state['values']['confirm']) {
|
||||
if (!empty($form_state['values']['nid'])) {
|
||||
// Single node
|
||||
i18n_sync_node_delete_prepare($form_state['values']['nid']);
|
||||
}
|
||||
elseif (!empty($form_state['values']['nodes'])) {
|
||||
// Multiple nodes
|
||||
foreach ($form_state['values']['nodes'] as $nid => $value) {
|
||||
i18n_sync_node_delete_prepare($nid);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then it will go through normal form submission
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare node for deletion, work out synchronization issues
|
||||
*/
|
||||
function i18n_sync_node_delete_prepare($nid) {
|
||||
$node = node_load($nid);
|
||||
// Delete file associations when files are shared with existing translations
|
||||
// so they are not removed by upload module
|
||||
if (!empty($node->tnid) && module_exists('upload')) {
|
||||
$result = db_query('SELECT u.* FROM {upload} u WHERE u.nid = %d AND u.fid IN (SELECT t.fid FROM {upload} t WHERE t.fid = u.fid AND t.nid <> u.nid)', $nid);
|
||||
while ($up = db_fetch_object($result)) {
|
||||
db_query("DELETE FROM {upload} WHERE fid = %d AND vid = %d", $up->fid, $up->vid);
|
||||
}
|
||||
// Add option to restrict syncing only when editing the translation source.
|
||||
$form['i18n_sync']['i18n_sync_source_description'] = array(
|
||||
'#prefix' => '<div>', '#suffix' => '</div>',
|
||||
'#markup' => t('Restrict synchronization to the translation source.'),
|
||||
'#weight' => 2,
|
||||
);
|
||||
$form['i18n_sync']['i18n_sync_source'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Synchronize translations only when saving the translation source'),
|
||||
'#default_value' => variable_get('i18n_sync_source_' . $type, FALSE),
|
||||
'#disabled' => $disabled,
|
||||
'#weight' => 3,
|
||||
'#description' => t('If not checked each node will trigger the synchronization, whether it\'s the source or not.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,9 +183,17 @@ function i18n_sync_node_insert($node) {
|
||||
*/
|
||||
function i18n_sync_node_update($node) {
|
||||
// Let's go with field synchronization.
|
||||
if (i18n_sync_node_check($node) && !empty($node->tnid) && ($fields = i18n_sync_node_fields($node->type)) && ($translations = i18n_sync_node_get_translations($node, TRUE))) {
|
||||
module_load_include('node.inc', 'i18n_sync');
|
||||
i18n_sync_node_translation($node, $translations, $fields, 'update');
|
||||
if (i18n_sync_node_check($node) && !empty($node->tnid) && (!variable_get('i18n_sync_source_' . $node->type, TRUE) || $node->tnid == $node->nid) && ($fields = i18n_sync_node_fields($node->type)) && ($translations = i18n_sync_node_get_translations($node, TRUE))) {
|
||||
$do_sync = TRUE;
|
||||
if (module_exists('entity_translation')) {
|
||||
if (entity_translation_enabled_bundle('node', $node->type)) {
|
||||
$do_sync = FALSE;
|
||||
}
|
||||
}
|
||||
if ($do_sync) {
|
||||
module_load_include('node.inc', 'i18n_sync');
|
||||
i18n_sync_node_translation($node, $translations, $fields, 'update');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
* Node operation (insert|update).
|
||||
*/
|
||||
function i18n_sync_node_translation($node, $translations, $field_names, $op) {
|
||||
$total = count($translations);
|
||||
$count = 0;
|
||||
// Disable language selection and synchronization temporarily, enable it again later
|
||||
$i18n_select = i18n_select(FALSE);
|
||||
@@ -34,10 +35,12 @@ function i18n_sync_node_translation($node, $translations, $field_names, $op) {
|
||||
foreach ($translations as $translation) {
|
||||
// If translation is the same node, we cannot synchronize with itself
|
||||
if ($node->nid == $translation->nid) {
|
||||
$total--;
|
||||
continue;
|
||||
}
|
||||
// Load full node, we need all data here.
|
||||
$translation = node_load($translation->nid, NULL, TRUE);
|
||||
entity_get_controller('node')->resetCache(array($translation->nid));
|
||||
$translation = node_load($translation->nid);
|
||||
$i18n_options = i18n_sync_node_options($node->type);
|
||||
// Invoke callback for each field, the default is just copy over
|
||||
foreach ($field_names as $field) {
|
||||
@@ -53,6 +56,13 @@ function i18n_sync_node_translation($node, $translations, $field_names, $op) {
|
||||
module_invoke_all('i18n_sync_translation', 'node', $translation, $translation->language, $node, $node->language, $field_names);
|
||||
node_save($translation);
|
||||
$count++;
|
||||
// Flush each entity from the load cache after processing, to
|
||||
// avoid exceeding PHP memory limits. It should be safe to keep
|
||||
// at least one, however; so we retain the final translation in
|
||||
// the cache after saving it.
|
||||
if ($count < $total) {
|
||||
entity_get_controller('node')->resetCache(array($translation->nid));
|
||||
}
|
||||
}
|
||||
i18n_sync(TRUE);
|
||||
i18n_select($i18n_select);
|
||||
@@ -88,7 +98,7 @@ function i18n_sync_node_translation_nodereference_field(&$node, &$translation, $
|
||||
* Example:
|
||||
* English A references English B and English C.
|
||||
* English A and B are translated to German A and B, but English C is not.
|
||||
* The syncronization from English A to German A would it German B and English C.
|
||||
* The synchronization from English A to German A would it German B and English C.
|
||||
*/
|
||||
function i18n_sync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
|
||||
if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Taxonomy translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, provides multiple options to
|
||||
translate taxonomy vocabularies and terms. For each vocabulary, there are four
|
||||
types of behaviors to choose from: Language-independent terms, Language-specific
|
||||
terms, Localized terms, and Mixed-language vocabulary.
|
||||
|
||||
* For a full description of the module visit:
|
||||
https://www.drupal.org/node/1114016
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit:
|
||||
https://www.drupal.org/project/issues/i18n
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
Language-independent terms - only vocabulary will be translatable.
|
||||
1. Navigate to Structure > Taxonomy.
|
||||
2. Select the "edit vocabulary" link.
|
||||
3. Select the "No multilingual options for terms".
|
||||
|
||||
Language-specific terms - vocabulary is only used for content in that language.
|
||||
The terms will only be available if the term language matches the UI language.
|
||||
1. Navigate to Structure > Taxonomy and select the "edit vocabulary link".
|
||||
2. Choose "Fixed Language" and a Language drop-down field will be displayed.
|
||||
3. Select the language.
|
||||
4. Select "Fixed Language" and Save.
|
||||
|
||||
Localized terms - Terms are common for all languages, but their name and
|
||||
description may be localized.
|
||||
1. Navigate to Structure > Taxonomy > vocabulary-to-edit > Edit.
|
||||
2. Select "Localize" and select Save.
|
||||
3. Edit a term and there will be a Translate tab. Select this tab.
|
||||
4. Select Translate, translate the Name and Description, select
|
||||
"Save translation", and repeat for all languages.
|
||||
5. Repeat the process for all terms.
|
||||
6. Navigate to Structure > Content types > term-to-edit > Manage display.
|
||||
7. By default, the term reference is set to Link. Change this to "Link
|
||||
(localized)" and Save.
|
||||
The vocabulary will be appropriate for the language.
|
||||
|
||||
Mixed-language vocabulary - Use for vocabularies with terms in multiple
|
||||
languages.
|
||||
1. Navigate to Structure > Taxonomy > vocabulary-to-edit > Edit.
|
||||
2. Select the Translate radio button and Save.
|
||||
3. Edit a vocabulary term and there will be a new Language field. Choose a
|
||||
language and then select Save and translate.
|
||||
4. There are two options, the user can either select "Add translation link" or
|
||||
the user can select an existing term in the Select translations form.
|
||||
5. Create translations for the terms and add terms for specific languages only.
|
||||
Now if the user edits a node associated with this vocabulary, only the relevant
|
||||
terms will appear.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -48,7 +48,7 @@ function i18n_taxonomy_translation_term_form($form, $form_state, $vocabulary, $t
|
||||
$form['translations'][$lang] = array(
|
||||
'#title' => $langname,
|
||||
'#type' => 'item',
|
||||
'#markup' => $source->name,
|
||||
'#markup' => check_plain($source->name),
|
||||
'#langcode' => $lang,
|
||||
);
|
||||
}
|
||||
@@ -199,7 +199,14 @@ function i18n_taxonomy_translation_term_overview($term) {
|
||||
$path = 'taxonomy/term/' . $translation_term->tid;
|
||||
$title = l($translation_term->name, $path);
|
||||
|
||||
$options[] = l(t('edit'), $path . '/edit');
|
||||
$options['edit'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => t('edit'),
|
||||
'#href' => $path . '/edit',
|
||||
'#options' => array(
|
||||
'query' => drupal_get_destination(),
|
||||
),
|
||||
);
|
||||
|
||||
if ($translation_term->tid == $i18n_tsid) {
|
||||
$language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
|
||||
@@ -208,14 +215,25 @@ function i18n_taxonomy_translation_term_overview($term) {
|
||||
else {
|
||||
// No such translation in the set yet: help user to create it.
|
||||
$title = t('n/a');
|
||||
$options[] = l(t('add translation'), 'admin/structure/taxonomy/' . $term->vocabulary_machine_name . '/add', array('query' => array('translation' => $term->tid, 'target' => $langcode) + drupal_get_destination()));
|
||||
$options['add'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => t('add translation'),
|
||||
'#href' => 'admin/structure/taxonomy/' . $term->vocabulary_machine_name . '/add',
|
||||
'#options' => array(
|
||||
'query' => array('translation' => $term->tid, 'target' => $langcode) + drupal_get_destination()
|
||||
),
|
||||
);
|
||||
}
|
||||
$rows[] = array($language_name, $title, implode(" | ", $options));
|
||||
$rows[$langcode] = array(
|
||||
'language' => $language_name,
|
||||
'title' => $title,
|
||||
'operations' => array('data' => $options),
|
||||
);
|
||||
}
|
||||
|
||||
drupal_set_title(t('Translations of term %title', array('%title' => $term->name)), PASS_THROUGH);
|
||||
|
||||
$build['translation_node_overview'] = array(
|
||||
$build['translation_overview'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
|
||||
@@ -11,9 +11,8 @@ files[] = i18n_taxonomy.pages.inc
|
||||
files[] = i18n_taxonomy.admin.inc
|
||||
files[] = i18n_taxonomy.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
function i18n_taxonomy_install() {
|
||||
module_load_install('i18n');
|
||||
i18n_install_create_fields('taxonomy_vocabulary', array('language', 'i18n_mode'));
|
||||
i18n_install_create_fields('taxonomy_vocabulary', array('language', 'i18n_mode'), TRUE);
|
||||
i18n_install_create_fields('taxonomy_term_data', array('language', 'i18n_tsid'));
|
||||
// Set module weight for it to run after core modules, but before views.
|
||||
db_query("UPDATE {system} SET weight = 5 WHERE name = 'i18n_taxonomy' AND type = 'module'");
|
||||
|
||||
@@ -184,11 +184,11 @@ function i18n_taxonomy_field_formatter_info() {
|
||||
}
|
||||
|
||||
/**
|
||||
- * Implements hook_field_formatter_prepare_view().
|
||||
- *
|
||||
- * This preloads all taxonomy terms for multiple loaded objects at once and
|
||||
- * unsets values for invalid terms that do not exist.
|
||||
- */
|
||||
* Implements hook_field_formatter_prepare_view().
|
||||
*
|
||||
* This preloads all taxonomy terms for multiple loaded objects at once and
|
||||
* unsets values for invalid terms that do not exist.
|
||||
*/
|
||||
function i18n_taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
|
||||
return taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, $items, $displays);
|
||||
}
|
||||
@@ -213,7 +213,12 @@ function i18n_taxonomy_field_formatter_view($entity_type, $entity, $field, $inst
|
||||
);
|
||||
}
|
||||
else {
|
||||
$term = $item['taxonomy_term'];
|
||||
if (isset($item['taxonomy_term'])) {
|
||||
$term = $item['taxonomy_term'];
|
||||
}
|
||||
else {
|
||||
$term = taxonomy_term_load($item['tid']);
|
||||
}
|
||||
$uri = entity_uri('taxonomy_term', $term);
|
||||
$element[$delta] = array(
|
||||
'#type' => 'link',
|
||||
@@ -318,7 +323,7 @@ function i18n_taxonomy_field_storage_details_alter(&$details, &$field) {
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_prepare_translation_alter().
|
||||
|
||||
*
|
||||
* Prepare and synchronize translation for term reference fields.
|
||||
*/
|
||||
function i18n_taxonomy_field_attach_prepare_translation_alter(&$entity, $context) {
|
||||
@@ -372,10 +377,19 @@ function i18n_taxonomy_field_prepare_translation($entity_type, $entity, $field,
|
||||
* The array of valid terms for this field, keyed by term id.
|
||||
*/
|
||||
function i18n_taxonomy_allowed_values($field) {
|
||||
global $language;
|
||||
$options = array();
|
||||
foreach ($field['settings']['allowed_values'] as $tree) {
|
||||
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
|
||||
if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'])) {
|
||||
if (i18n_taxonomy_vocabulary_mode($vocabulary->vid) == I18N_MODE_TRANSLATE) {
|
||||
$parent = i18n_taxonomy_translation_term_tid($tree['parent'], NULL, $tree['parent']);
|
||||
$language = i18n_language_context();
|
||||
$terms = i18n_taxonomy_get_tree($vocabulary->vid, $language->language, $parent);
|
||||
}
|
||||
else {
|
||||
$terms = taxonomy_get_tree($vocabulary->vid, $tree['parent']);
|
||||
}
|
||||
if ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$options[$term->tid] = str_repeat('-', $term->depth) . i18n_taxonomy_term_name($term);
|
||||
}
|
||||
@@ -452,23 +466,13 @@ function i18n_taxonomy_translate_path($path, $path_prefix = 'taxonomy/term/') {
|
||||
return $links;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function i18n_taxonomy_theme() {
|
||||
return array(
|
||||
'i18n_taxonomy_term_page' => array(
|
||||
'arguments' => array('tids' => array(), 'result' => NULL),
|
||||
'file' => 'i18n_taxonomy.pages.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized term name unfiltered.
|
||||
*/
|
||||
function i18n_taxonomy_term_name($term, $langcode = NULL) {
|
||||
return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->tid, 'name'), $term->name, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->name;
|
||||
$key = i18n_object_info('taxonomy_term', 'key');
|
||||
return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->{$key}, 'name'), $term->name, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->name;
|
||||
}
|
||||
|
||||
|
||||
@@ -476,7 +480,8 @@ function i18n_taxonomy_term_name($term, $langcode = NULL) {
|
||||
* Get localized term description unfiltered.
|
||||
*/
|
||||
function i18n_taxonomy_term_description($term, $langcode = NULL) {
|
||||
return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->tid, 'description'), $term->description, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->description;
|
||||
$key = i18n_object_info('taxonomy_term', 'key');
|
||||
return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->{$key}, 'description'), $term->description, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->description;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -751,6 +756,28 @@ function i18n_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) {
|
||||
// Add language field or not depending on taxonomy mode.
|
||||
switch (i18n_taxonomy_vocabulary_mode($vocabulary->vid)) {
|
||||
case I18N_MODE_TRANSLATE:
|
||||
// Set $form_state['storage'] default as empty array because we will add
|
||||
// the translation and target from $_GET. So we still have it when the
|
||||
// page partially reloads with ajax.
|
||||
if(!isset($form_state['storage'])) {
|
||||
$form_state['storage'] = array();
|
||||
}
|
||||
// get translation from $_GET or $form_state['storage']
|
||||
$translation = null;
|
||||
if(isset($_GET['translation'])) {
|
||||
$translation = $_GET['translation'];
|
||||
$form_state['storage']['translation'] = $translation;
|
||||
} else if(isset($form_state['storage']) && isset($form_state['storage']['translation'])){
|
||||
$translation = $form_state['storage']['translation'];
|
||||
}
|
||||
// get target from $_GET or $form_state['storage']
|
||||
$target = null;
|
||||
if(isset($_GET['target'])) {
|
||||
$target = $_GET['target'];
|
||||
$form_state['storage']['target'] = $target;
|
||||
} else if(isset($form_state['storage']) && isset($form_state['storage']['target'])){
|
||||
$target = $form_state['storage']['target'];
|
||||
}
|
||||
$form['language'] = array(
|
||||
'#description' => t('This term belongs to a multilingual vocabulary. You can set a language for it.'),
|
||||
) + i18n_element_language_select($term);
|
||||
@@ -758,7 +785,7 @@ function i18n_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) {
|
||||
// If the term to be added will be a translation of a source term,
|
||||
// set the default value of the option list to the target language and
|
||||
// create a form element for storing the translation set of the source term.
|
||||
if (empty($term->tid) && isset($_GET['translation']) && isset($_GET['target']) && ($source_term = taxonomy_term_load($_GET['translation'])) && ($target_language = i18n_language_object($_GET['target']))) {
|
||||
if (empty($term->tid) && isset($translation) && isset($target) && ($source_term = taxonomy_term_load($translation)) && ($target_language = i18n_language_object($target))) {
|
||||
// Set context language to target language.
|
||||
i18n_language_context($target_language);
|
||||
|
||||
@@ -1000,7 +1027,7 @@ function i18n_taxonomy_translate_terms($taxonomy, $langcode, $fullterms = TRUE)
|
||||
}
|
||||
elseif ($term->language && $term->language != $langcode) {
|
||||
$translation_set = i18n_translation_set_load($term->i18n_tsid);
|
||||
$translations = $translation_set->get_translations();
|
||||
$translations = ($translation_set) ? $translation_set->get_translations() : NULL;
|
||||
|
||||
if ($translations && !empty($translations[$langcode])) {
|
||||
$newterm = $translations[$langcode];
|
||||
@@ -1029,6 +1056,10 @@ function i18n_taxonomy_localize_terms($terms) {
|
||||
if (!i18n_string_translate_langcode()) {
|
||||
return $terms;
|
||||
}
|
||||
// $terms is not a valid array or term.
|
||||
if (empty($terms)) {
|
||||
return $terms;
|
||||
}
|
||||
$object_info = i18n_object_info('taxonomy_term');
|
||||
$list = is_array($terms) ? $terms : array($terms);
|
||||
foreach ($list as $index => $term) {
|
||||
@@ -1096,7 +1127,7 @@ function i18n_taxonomy_get_tree($vid, $langcode, $parent = 0, $max_depth = NULL,
|
||||
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
|
||||
$result = $query
|
||||
->addTag('translatable')
|
||||
->addTag('term_access')
|
||||
->addTag('taxonomy_term_access')
|
||||
->fields('t')
|
||||
->fields('h', array('parent'))
|
||||
->condition('t.vid', $vid)
|
||||
@@ -1225,10 +1256,18 @@ function i18n_taxonomy_field_uuid_presave($entity_type, $entity, $field, $instan
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function i18n_taxonomy_entity_info_alter(&$entity_info) {
|
||||
if (isset($entity_info['taxonomy_vocabulary'])) {
|
||||
// Add altered vocabulary schema fields.
|
||||
$entity_info['taxonomy_vocabulary']['schema_fields_sql']['base table'][] = 'i18n_mode';
|
||||
$entity_info['taxonomy_vocabulary']['schema_fields_sql']['base table'][] = 'language';
|
||||
}
|
||||
if (isset($entity_info['taxonomy_term'])) {
|
||||
// Core doesn't provide a label callback for taxonomy terms. By setting one
|
||||
// we can use it to return the correct localized term name.
|
||||
$entity_info['taxonomy_term']['label callback'] = 'i18n_taxonomy_taxonomy_term_label';
|
||||
|
||||
// Also let core know terms have languages, now.
|
||||
$entity_info['taxonomy_term']['entity keys']['language'] = 'language';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,10 +1299,29 @@ function i18n_taxonomy_modules_enabled($modules) {
|
||||
$modules = drupal_map_assoc($modules);
|
||||
if (isset($modules['i18n_taxonomy'])) {
|
||||
foreach (field_info_fields() as $fieldname => $field) {
|
||||
if ($field['type'] == 'taxonomy_term_reference') {
|
||||
$field['settings']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
|
||||
field_update_field($field);
|
||||
}
|
||||
if ($field['type'] == 'taxonomy_term_reference') {
|
||||
$field['settings']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
|
||||
field_update_field($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_pre_render().
|
||||
*/
|
||||
function i18n_taxonomy_views_pre_render(&$view) {
|
||||
if($view->base_table !== 'rules_scheduler') {
|
||||
global $language;
|
||||
|
||||
foreach ($view->result as $delta => $term){
|
||||
if (isset($term->tid)) {
|
||||
i18n_string_translate_langcode($language->language);
|
||||
$localized_term = i18n_taxonomy_localize_terms(taxonomy_term_load($term->tid));
|
||||
$term->tid = $localized_term->tid;
|
||||
$term->taxonomy_term_data_name = $localized_term->name;
|
||||
$term->taxonomy_term_data_description = $localized_term->description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,37 +62,6 @@ function i18n_taxonomy_term_page($term) {
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a taxonomy term page HTML output.
|
||||
*
|
||||
* @param $tids
|
||||
* An array of term ids.
|
||||
* @param $result
|
||||
* A pager_query() result, such as that performed by taxonomy_select_nodes().
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_i18n_taxonomy_term_page($tids, $result) {
|
||||
drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
|
||||
|
||||
$output = '';
|
||||
|
||||
// Only display the description if we have a single term, to avoid clutter and confusion.
|
||||
if (count($tids) == 1) {
|
||||
$term = i18n_taxonomy_localize_terms(taxonomy_term_load($tids[0]));
|
||||
// Check that a description is set.
|
||||
if (!empty($term->description)) {
|
||||
$output .= '<div class="taxonomy-term-description">';
|
||||
$output .= filter_xss_admin($term->description);
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$output .= taxonomy_render_nodes($result);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for autocompletion. Replacement for taxonomy_autocomplete
|
||||
*/
|
||||
@@ -131,7 +100,7 @@ function _i18n_taxonomy_autocomplete($langcode, $vids, $tags_typed = '') {
|
||||
$query = db_select('taxonomy_term_data', 't')
|
||||
->fields('t', array('tid', 'name'));
|
||||
$query->addTag('translatable');
|
||||
$query->addTag('term_access');
|
||||
$query->addTag('taxonomy_term_access');
|
||||
// Disable i18n_select for this query
|
||||
$query->addTag('i18n_select');
|
||||
// Add language condition
|
||||
|
||||
@@ -15,6 +15,10 @@ function i18n_taxonomy_token_info() {
|
||||
'name' => t("Name (localized)"),
|
||||
'description' => t("The name of the taxonomy term."),
|
||||
);
|
||||
$term['localized-name'] = array(
|
||||
'name' => t("Name in current language"),
|
||||
'description' => t("The name of the taxonomy term in current language."),
|
||||
);
|
||||
$term['i18n-description'] = array(
|
||||
'name' => t("Description (localized)"),
|
||||
'description' => t("The optional description of the taxonomy term."),
|
||||
@@ -69,6 +73,12 @@ function i18n_taxonomy_tokens($type, $tokens, array $data = array(), array $opti
|
||||
$replacements[$original] = $sanitize ? check_plain($name) : $name;
|
||||
break;
|
||||
|
||||
case 'localized-name':
|
||||
$translated_term = i18n_taxonomy_term_get_translation($term, $langcode);
|
||||
$name = i18n_taxonomy_term_name($translated_term, $langcode);
|
||||
$replacements[$original] = $sanitize ? check_plain($name) : $name;
|
||||
break;
|
||||
|
||||
case 'i18n-description':
|
||||
$replacements[$original] = i18n_string_text(array('taxonomy', 'term', $term->tid, 'description'), $term->description, array('langcode' => $langcode, 'format' => $term->format, 'sanitize' => $sanitize, 'cache' => TRUE));
|
||||
break;
|
||||
|
||||
@@ -6,9 +6,8 @@ core = 7.x
|
||||
|
||||
files[] = i18n_translation.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ class I18nTranslationSetController extends DrupalDefaultEntityController {
|
||||
* @param $queried_entities
|
||||
* Associative array of query results, keyed on the entity ID.
|
||||
* @param $revision_id
|
||||
* ID of the revision that was loaded, or FALSE if teh most current revision
|
||||
* ID of the revision that was loaded, or FALSE if the most current revision
|
||||
* was loaded.
|
||||
*/
|
||||
protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The User mail translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, translates emails sent from the
|
||||
User module.
|
||||
|
||||
* For a full description of the module, visit this page:
|
||||
https://www.drupal.org/node/133977.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes:
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following module:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
To configure email translations
|
||||
1. Navigate to Administration > Configuration > Regional and language >
|
||||
Multilingual settings and select the variables tab.
|
||||
2. Select the "User emails" tab. Select the variables to be translated. Save
|
||||
configuration.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -4,9 +4,8 @@ core = 7.x
|
||||
package = Multilingual - Internationalization
|
||||
dependencies[] = i18n_variable
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -9,7 +9,14 @@
|
||||
*/
|
||||
function i18n_user_mail_alter(&$message) {
|
||||
if ($message['module'] == 'user') {
|
||||
$language = $message['language'];
|
||||
$message['language'] = language_default();
|
||||
if (isset($message['params']['account'])) {
|
||||
$user_preferred = user_preferred_language($message['params']['account']);
|
||||
if (isset($user_preferred)) {
|
||||
$message['language'] = $user_preferred;
|
||||
}
|
||||
}
|
||||
$language = (isset($message['language']) ? $message['language'] : language_default());
|
||||
$variables = array('user' => $message['params']['account']);
|
||||
$key = $message['key'];
|
||||
|
||||
@@ -54,7 +61,7 @@ function i18n_user_user_mail_tokens(&$replacements, $data, $options) {
|
||||
*/
|
||||
function i18n_user_user_pass_reset_url($account) {
|
||||
$timestamp = REQUEST_TIME;
|
||||
return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
|
||||
return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,5 +72,5 @@ function i18n_user_user_pass_reset_url($account) {
|
||||
*/
|
||||
function i18n_user_user_cancel_url($account) {
|
||||
$timestamp = REQUEST_TIME;
|
||||
return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
|
||||
return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
CONTENTS OF THIS FILE
|
||||
---------------------
|
||||
|
||||
* Introduction
|
||||
* Requirements
|
||||
* Recommended modules
|
||||
* Installation
|
||||
* Configuration
|
||||
* Maintainers
|
||||
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
The Variable translation module, part of the Internationalization
|
||||
(https://www.drupal.org/project/i18n) package, allows the user to translate text
|
||||
and settings that are stored in Drupal as variables. These variables include
|
||||
text such as 'site name' and 'site slogan', as well as settings like 'Default
|
||||
front page' and 'Default 404 page'.
|
||||
|
||||
* For a full description of the module, visit:
|
||||
https://www.drupal.org/node/1113374.
|
||||
|
||||
* To submit bug reports and feature suggestions, or to track changes visit:
|
||||
https://www.drupal.org/project/issues/i18n.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
This module requires the following modules:
|
||||
|
||||
* Internationalization - https://www.drupal.org/project/i18n
|
||||
* Variable - https://www.drupal.org/project/variable
|
||||
|
||||
|
||||
RECOMMENDED MODULES
|
||||
-------------------
|
||||
|
||||
* Internationalization Views - https://www.drupal.org/project/i18nviews
|
||||
* Language Icons - https://www.drupal.org/project/languageicons
|
||||
* Translation Overview - https://www.drupal.org/project/translation_overview
|
||||
* Localization Client - https://www.drupal.org/project/l10n_client
|
||||
* Internationalization contributions -
|
||||
https://www.drupal.org/project/i18n_contrib
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
This is a submodule of the Internationalization module. Install the
|
||||
Internationalization module as you would normally install a contributed Drupal
|
||||
module. Visit https://www.drupal.org/node/895232 for further information.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
To enable multilingual variables
|
||||
|
||||
1. Enable the Variable translation module included with the Internationalization
|
||||
package.
|
||||
2. Go to Administration > Configuration > Regional and language > Multilingual
|
||||
settings.
|
||||
3. Select on the Variables tab.
|
||||
4. Select the variables that will be multilingual.
|
||||
5. Save configuration.
|
||||
|
||||
Once the user has the correct settings, they'll be marked with "This is a
|
||||
multilingual variable" when the user navigates to the corresponding
|
||||
administration pages. The user must switch the site language while in the
|
||||
administration pages to set the variables for each language. A language switcher
|
||||
link will appear at the top of each administrative page that has multilingual
|
||||
variables.
|
||||
|
||||
|
||||
MAINTAINERS
|
||||
-----------
|
||||
|
||||
* Jose Reyero - https://www.drupal.org/u/jose-reyero
|
||||
* Florian Weber (webflo) - https://www.drupal.org/u/webflo
|
||||
* Peter Philipp - https://www.drupal.org/u/das-peter
|
||||
* Joseph Olstad - https://www.drupal.org/u/joseph.olstad
|
||||
* Nathaniel Catchpole - https://www.drupal.org/u/catch
|
||||
@@ -10,9 +10,8 @@ configure = admin/config/regional/i18n/variable
|
||||
files[] = i18n_variable.class.inc
|
||||
files[] = i18n_variable.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -7,9 +7,8 @@ package = Testing
|
||||
core = 6.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-01-13
|
||||
version = "7.x-1.8"
|
||||
; Information added by Drupal.org packaging script on 2020-06-17
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "i18n"
|
||||
datestamp = "1358075001"
|
||||
|
||||
datestamp = "1592373390"
|
||||
|
||||
@@ -36,6 +36,12 @@ function i18n_test_i18n_string_info() {
|
||||
'format' => FALSE, // This group doesn't have strings with format
|
||||
'refresh callback' => 'i18n_test_i18n_string_refresh',
|
||||
);
|
||||
$groups['test_cached'] = array(
|
||||
'title' => t('Test Cached Strings'),
|
||||
'description' => t('Translatable items of a textgroup with caching enabled.'),
|
||||
'format' => FALSE, // This group doesn't have strings with format
|
||||
'class' => variable_get('i18n_string_textgroup_class_test_cached', 'i18n_string_textgroup_cached_logged'),
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
/**
|
||||
@@ -43,4 +49,53 @@ function i18n_test_i18n_string_info() {
|
||||
*/
|
||||
function i18n_test_i18n_string_refresh() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function i18n_test_menu() {
|
||||
// Required for the i18n_string caching tests.
|
||||
$items['tests/i18n/i18n_string_build/%'] = array(
|
||||
'title' => 'Load string',
|
||||
'access callback' => TRUE,
|
||||
'page callback' => 'i18n_string_build',
|
||||
'page arguments' => array(3),
|
||||
'type' => MENU_CALLBACK,
|
||||
'delivery callback' => 'drupal_json_output',
|
||||
);
|
||||
$items['tests/i18n/i18n_string_build/%/%'] = array(
|
||||
'title' => 'Load string',
|
||||
'access callback' => TRUE,
|
||||
'page callback' => 'i18n_string_build',
|
||||
'page arguments' => array(3, 4),
|
||||
'type' => MENU_CALLBACK,
|
||||
'delivery callback' => 'drupal_json_output',
|
||||
);
|
||||
$items['tests/i18n/i18n_string_translation_search/%'] = array(
|
||||
'title' => 'Search string translations',
|
||||
'access callback' => TRUE,
|
||||
'page callback' => 'i18n_string_translation_search',
|
||||
'page arguments' => array(3),
|
||||
'type' => MENU_CALLBACK,
|
||||
'delivery callback' => 'drupal_json_output',
|
||||
);
|
||||
$items['tests/i18n/i18n_string_translation_search/%/%'] = array(
|
||||
'title' => 'Search string translations',
|
||||
'access callback' => TRUE,
|
||||
'page callback' => 'i18n_string_translation_search',
|
||||
'page arguments' => array(3, 4),
|
||||
'type' => MENU_CALLBACK,
|
||||
'delivery callback' => 'drupal_json_output',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
class i18n_string_textgroup_cached_logged extends i18n_string_textgroup_cached {
|
||||
public static function load_translation($i18nstring, $langcode) {
|
||||
$strings = variable_get('i18n_loaded_translations', array());
|
||||
$strings[$i18nstring->get_name()] = true;
|
||||
variable_set('i18n_loaded_translations', $strings);
|
||||
parent::load_translation($i18nstring, $langcode);
|
||||
}
|
||||
}
|
||||
@@ -26,42 +26,43 @@ Installation
|
||||
Download, unpack the module the usual way.
|
||||
Enable this module and the Locale module (core).
|
||||
|
||||
You need at least one language other than English.
|
||||
On Administration > Configuration > Regional and language:
|
||||
Click "Add language"
|
||||
Pull-down menu: Choose your new language
|
||||
Then click "Add language"
|
||||
You need at least one language (besides the default English).
|
||||
On Administration > Configuration > Regional and language > Languages:
|
||||
Click "Add language".
|
||||
Select a language from the select list "Language name".
|
||||
Then click the "Add language" button.
|
||||
|
||||
Drupal is now importing interface translations. This can take a few minutes.
|
||||
When it's finished, you'll get a confirmation with a summary of all
|
||||
translation files that have been pulled in.
|
||||
When it's finished, you'll get a confirmation with a summary of the
|
||||
translations that have been imported.
|
||||
|
||||
If required, enable the new language as default language.
|
||||
Home > Administration > Configuration > Regional and language:
|
||||
Select your new language as default
|
||||
Administration > Configuration > Regional and language > Languages:
|
||||
Select your new default language.
|
||||
|
||||
Update interface translations
|
||||
-----------------------------
|
||||
On Home > Administration > Configuration > Regional and language:
|
||||
Choose the "Translation updates" tab
|
||||
Change "Check for updates" to Daily or Weekly instead of the default "Never".
|
||||
You want to import translations regularly using cron. You can enable this
|
||||
on Administration > Configuration > Regional and language > Languages:
|
||||
Choose the "Translation updates" tab.
|
||||
Change "Check for updates" to "Daily" or "Weekly" instead of the default "Never".
|
||||
From now on cron will check for updated translations, and import them is required.
|
||||
|
||||
Cron will from now on check for updated translations, and will report the
|
||||
update status on the status page (Home > Administration > Reports).
|
||||
The status of the translations is reported on the "Status report" page at
|
||||
Administration > Reports.
|
||||
|
||||
To check the translation status and execute updates manually, go to
|
||||
Administration > Configuration > Regional and language > Translate inteface
|
||||
Here you see English and your new language.
|
||||
Choose the "Update" tab
|
||||
Administration > Configuration > Regional and language > Translate inteface
|
||||
Choose the "Update" tab.
|
||||
You see a list of all modules and their translation status.
|
||||
On the bottom of the page, you can manually update using "Update translations".
|
||||
|
||||
Use Drush
|
||||
---------
|
||||
You can also use drush to update your translations:
|
||||
drush l10n-update # Update translations.
|
||||
drush l10n-update-refresh # Refresh available information.
|
||||
drush l10n-update-status # Show translation status of available project
|
||||
drush l10n-update # Update translations.
|
||||
drush l10n-update-refresh # Refresh available information.
|
||||
drush l10n-update-status # Show translation status of available project
|
||||
|
||||
|
||||
Summary of administrative pages
|
||||
|
||||
@@ -59,7 +59,7 @@ function l10n_update_admin_overview() {
|
||||
$build['admin_import_form'] = drupal_get_form('l10n_update_admin_import_form', $projects, $updates);
|
||||
}
|
||||
else {
|
||||
$build['no_projects'] = array('#markup' => t('No projects or languages to update.'));
|
||||
$build['no_languages'] = array('#markup' => t('No translatable language defined. <a href="/admin/config/regional/language">Add a language</a>.'));
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
@@ -96,30 +96,29 @@ function l10n_update_admin_import_form($form, $form_state, $projects, $updates)
|
||||
'#type' => 'value',
|
||||
'#value' => $updates,
|
||||
);
|
||||
// @todo Only show this language fieldset if we have more than 1 language.
|
||||
$form['lang'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Languages'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#description' => t('Select one or more languages to download and update. If you select none, all of them will be updated.'),
|
||||
);
|
||||
$form['lang']['languages'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $languages,
|
||||
);
|
||||
$form['mode'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Update mode'),
|
||||
'#default_value' => variable_get('l10n_update_import_mode', LOCALE_IMPORT_KEEP),
|
||||
'#options' => _l10n_update_admin_import_options(),
|
||||
);
|
||||
$form['buttons']['download'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update translations'),
|
||||
);
|
||||
|
||||
if (count($languages) > 1) {
|
||||
$form['lang'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Languages'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE ,
|
||||
'#description' => t('Select one or more languages to download and update. If you select none, all of them will be updated.'),
|
||||
);
|
||||
$form['lang']['languages'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $languages,
|
||||
);
|
||||
}
|
||||
|
||||
if ($updates) {
|
||||
$form['actions']['download'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update translations'),
|
||||
);
|
||||
}
|
||||
}
|
||||
$form['buttons']['refresh'] = array(
|
||||
$form['actions']['refresh'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Refresh information'),
|
||||
);
|
||||
@@ -137,9 +136,10 @@ function l10n_update_admin_import_form_submit($form, $form_state) {
|
||||
$projects = l10n_update_get_projects();
|
||||
|
||||
if ($op == t('Update translations')) {
|
||||
$languages = array_filter($form_state['values']['languages']);
|
||||
$languages = isset($form_state['values']['languages']) ? array_filter($form_state['values']['languages']) : NULL;
|
||||
$updates = $form_state['values']['updates'];
|
||||
$mode = $form_state['values']['mode'];
|
||||
$mode = variable_get('l10n_update_import_mode', LOCALE_IMPORT_KEEP);
|
||||
|
||||
if ($projects && $updates) {
|
||||
module_load_include('batch.inc', 'l10n_update');
|
||||
// Filter out updates in other languages. If no languages, all of them will be updated
|
||||
@@ -147,9 +147,6 @@ function l10n_update_admin_import_form_submit($form, $form_state) {
|
||||
$batch = l10n_update_batch_multiple($updates, $mode);
|
||||
batch_set($batch);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Cannot find any translation updates.'), 'error');
|
||||
}
|
||||
}
|
||||
elseif ($op == t('Refresh information')) {
|
||||
// Get current version of projects.
|
||||
@@ -205,7 +202,11 @@ function l10n_update_admin_settings_form($form, &$form_state) {
|
||||
'#default_value' => variable_get('l10n_update_download_store', ''),
|
||||
'#description' => t('A path relative to the Drupal installation directory where translation files will be stored, e.g. sites/all/translations. Saved translation files can be reused by other installations. If left empty the downloaded translation will not be saved.'),
|
||||
);
|
||||
return system_settings_form($form);
|
||||
|
||||
$form = system_settings_form($form);
|
||||
$form['#submit'][] = 'l10n_update_admin_settings_form_submit';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +224,14 @@ function l10n_update_admin_settings_form_validate($form, &$form_state) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional submit handler for update settings.
|
||||
*/
|
||||
function l10n_update_admin_settings_form_submit($form, &$form_state) {
|
||||
// Add .htaccess file to the translations directory.
|
||||
l10n_update_ensure_htaccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of import options.
|
||||
*
|
||||
@@ -344,6 +353,7 @@ function theme_l10n_update_project_status($variables) {
|
||||
// Plus add our own css and js.
|
||||
drupal_add_css(drupal_get_path('module', 'update') . '/update.css');
|
||||
drupal_add_css(drupal_get_path('module', 'l10n_update') . '/css/l10n_update.admin.css');
|
||||
drupal_add_js('misc/collapse.js');
|
||||
drupal_add_js(drupal_get_path('module', 'l10n_update') . '/js/l10n_update.js');
|
||||
|
||||
return $output;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user