summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Reyero2011-02-14 11:36:29 (GMT)
committer Jose Reyero2011-02-14 11:36:29 (GMT)
commit55429c3ee5e2bc9161849e2926b0719d36568009 (patch)
treea11e1e4e376f77a7873fe072207f12afb4fe9a27
parentfbc239e60000bdd76a6e0c02fc530e872947cdc7 (diff)
Updating from github, http://githup.com/josereyero/i18n7.x-1.0-alpha2
0eaf308 Fixed edition of taxonomy translations 59296bf Updated taxonomy translation forms b54d118 Updating link alter hooks f63892c Updated block module install ed2c9d0 Fixed issues with text formats 0bde20c Simplified node_translations, properly check language code b2e825e Simplified node_translations, properly check language code 1472068 Generalized i18n_sync hooks for entities 52b7e5d Added NEW! path translation module. edd8bd3 Fixed issues with taxonomy forms a0dfaff Implemented language selection for taxonomy 8c79dcd Added proper undefined language to menus and taxonomy b8825f0 Moved node options to the right module. Added default language list options 7a6598f Added some variable information, using new variable api 520916f Removing obsoleted options, updating links a289c8e Merge branch 'master' of github.com:josereyero/i18n efe315e Replaced existance condition 8e64648 #1026784 bug fix c3747d3 commented dpm function f3eda0d #899152 coding standars and link to change the variable for other languages next to the variable bd9380d Migrated hook_link_alter to hook_node_view_alter 4d6402d Fixed hiding translation links in node view 86e84dd Adapted JS file to D7 2b0a87d Fixed a notice error (1030548) 38c1ccf Fixed an undefinied index error (1048436) 397e4ac #1027110 fixed d7 compatibility code 1356194 Fixed bug, typo dae8cf5 Fixed wrong funcion names in install files 42312f8 minor: i18n_block: Implements hook_uninstall(). 35cf61d fix "Require language (Do not allow Language Neutral)." df093b6 fix language-selection bug. set current language as default language for new nodes. 0e20b8a Cleaned install files and added update 7000 for all of them, to be filled later. cc707c1 Fixed issues with syn, some fields working 688cf3c Fixed issues with language/language code 8cc8ad2 Added proper handling of theme settings 4e9d4b1 Commented out not updated fields 324da98 Added fields to select for synchronization 14db0fd Node synchronization 37c7dbf Better naming for includes 943092f Moved synchronization functions into separate file 96ae625 Reworked field info collection to use more flexible hooks 1096c4a Fixed notice when listing menu strings e2f9c88 fixed notice in i18n_node e30573c Integrated with Variable module, see http://drupal.org/project/variable 3d93fa9 Few more improvements for i18n sync
-rw-r--r--i18n.admin.inc47
-rw-r--r--i18n.install1
-rw-r--r--i18n.module36
-rw-r--r--i18n.variable.inc18
-rw-r--r--i18n_block/i18n_block.install17
-rw-r--r--i18n_field/i18n_field.install14
-rw-r--r--i18n_menu/i18n_menu.install24
-rw-r--r--i18n_menu/i18n_menu.module16
-rw-r--r--i18n_node/i18n_node.admin.inc31
-rw-r--r--i18n_node/i18n_node.install15
-rw-r--r--i18n_node/i18n_node.js14
-rw-r--r--i18n_node/i18n_node.module84
-rw-r--r--i18n_path/i18n_path.admin.inc108
-rw-r--r--i18n_path/i18n_path.info6
-rw-r--r--i18n_path/i18n_path.install58
-rw-r--r--i18n_path/i18n_path.module89
-rw-r--r--i18n_select/i18n_select.admin.inc4
-rw-r--r--i18n_select/i18n_select.module155
-rw-r--r--i18n_string/i18n_string.admin.inc16
-rw-r--r--i18n_string/i18n_string.inc5
-rw-r--r--i18n_string/i18n_string.install27
-rw-r--r--i18n_string/i18n_string.module12
-rw-r--r--i18n_sync/i18n_sync.info7
-rw-r--r--i18n_sync/i18n_sync.install17
-rw-r--r--i18n_sync/i18n_sync.module465
-rw-r--r--i18n_sync/i18n_sync.modules.inc66
-rw-r--r--i18n_sync/i18n_sync.node.inc214
-rw-r--r--i18n_taxonomy/i18n_taxonomy.admin.inc182
-rw-r--r--i18n_taxonomy/i18n_taxonomy.install10
-rw-r--r--i18n_taxonomy/i18n_taxonomy.module189
-rw-r--r--i18n_taxonomy/i18n_taxonomy.pages.inc13
-rw-r--r--i18n_variable/i18n_variable.admin.inc54
-rw-r--r--i18n_variable/i18n_variable.info4
-rw-r--r--i18n_variable/i18n_variable.install12
-rw-r--r--i18n_variable/i18n_variable.module89
-rw-r--r--i18n_variable/i18n_variable.variable.inc22
36 files changed, 1430 insertions, 711 deletions
diff --git a/i18n.admin.inc b/i18n.admin.inc
index 274c49d..f5fea23 100644
--- a/i18n.admin.inc
+++ b/i18n.admin.inc
@@ -15,49 +15,18 @@
* - Language dependent tables are authomatically used if defined in settings file.
*/
function i18n_admin_settings() {
- // Content selection options.
- $form['selection'] = array(
- '#type' => 'fieldset',
- '#title' => t('Content selection'),
- //'#collapsible' => TRUE,
- //'#collapsed' => TRUE,
- );
- $form['selection']['i18n_selection_mode'] = array(
+ $form['i18n_language_list'] = array(
'#type' => 'radios',
- '#title' => t('Content selection mode'),
- '#default_value' => variable_get('i18n_selection_mode', 'simple'),
- '#options' => _i18n_selection_mode(),
- '#description' => t('Determines which content to show depending on the current page language and the default language of the site.'),
+ '#title' => t('Languages for content'),
+ '#default_value' => variable_get('i18n_language_list', I18N_LANGUAGE_ENABLED),
+ '#options' => array(
+ I18N_LANGUAGE_ENABLED => t('Enabled languages only.'),
+ I18N_LANGUAGE_EXTENDED => t('All defined languages will be allowed.'),
+ ),
+ '#description' => t('Determines which languages will be allowed for content creation.'),
);
- // Node translation links setting.
- $form['links'] = array(
- '#type' => 'fieldset',
- '#title' => t('Content translation links'),
- );
- $form['links']['i18n_hide_translation_links'] = array(
- '#type' => 'checkbox',
- '#title' => t('Hide content translation links'),
- '#description' => t('Hide the links to translations in content body and teasers. If you choose this option, switching language will only be available from the language switcher block.'),
- '#default_value' => variable_get('i18n_hide_translation_links', 0),
- );
- $form['links']['i18n_translation_switch'] = array(
- '#type' => 'checkbox',
- '#title' => t('Switch interface for translating'),
- '#default_value' => variable_get('i18n_translation_switch', 0),
- '#description' => t('Switch interface language to fit node language when creating or editing a translation. If not checked the interface language will be independent from node language.'),
- );
return system_settings_form($form);
}
-// List of selection modes
-function _i18n_selection_mode() {
- return array(
- 'simple' => t('Current language and language neutral.'),
- 'mixed' => t('Mixed current language (if available) or default language (if not) and language neutral.'),
- 'default' => t('Only default language and language neutral.'),
- 'strict' => t('Only current language.'),
- 'off' => t('All content. No language conditions apply.'),
- );
-}
diff --git a/i18n.install b/i18n.install
index 8be7164..5736487 100644
--- a/i18n.install
+++ b/i18n.install
@@ -39,4 +39,5 @@ function i18n_install_create_fields($table, $fields) {
*/
function i18n_update_7000() {
variable_set('i18n_drupal6_update', TRUE);
+ variable_del('i18n_selection_mode');
} \ No newline at end of file
diff --git a/i18n.module b/i18n.module
index 9bbc38e..2507cae 100644
--- a/i18n.module
+++ b/i18n.module
@@ -89,7 +89,7 @@ function i18n_language_name($lang) {
return t('Undefined');
}
elseif (isset($list[$lang])) {
- return $list[$lang];
+ return check_plain($list[$lang]);
}
else {
return t('Unknown');
@@ -158,22 +158,6 @@ function i18n_menu() {
}
/**
- * Implements hook_modules_installed()
- *
- * Invoke hook_i18n_update_drupal6()
- */
-function i18n_modules_installed($modules) {
- if (variable_get('i18n_drupal6_update')) {
- foreach ($modules as $module) {
- if (strpos($module, 'i18n') === 0) {
- module_load_install($module);
- module_invoke($module, 'i18n_update_drupal6');
- }
- }
- }
-}
-
-/**
* Simple i18n API
*/
@@ -251,4 +235,22 @@ function i18n_context_language() {
}
}
return i18n_language();
+}
+
+/**
+ * Get object language code
+ *
+ * @param $object
+ * Object or array having language field or plain language field
+ */
+function i18n_object_langcode($object, $field = 'language') {
+ if (is_object($object)) {
+ return !empty($object->$field) && $object->$field != LANGUAGE_NONE ? $object->$field : FALSE;
+ }
+ elseif (is_array($object)) {
+ return !empty($object[$field]) && $object[$field] != LANGUAGE_NONE ? $object[$field] : FALSE;
+ }
+ else {
+ return $object && $object != LANGUAGE_NONE ? $object : FALSE;
+ }
} \ No newline at end of file
diff --git a/i18n.variable.inc b/i18n.variable.inc
new file mode 100644
index 0000000..6049989
--- /dev/null
+++ b/i18n.variable.inc
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_group_info()
+ */
+function i18n_variable_group_info() {
+ $groups['i18n'] = array(
+ 'title' => t('Multilingual settings'),
+ 'description' => t('Mixed options for multilingual sites.'),
+ 'access' => 'administer site configuration',
+ 'path' => 'admin/config/regional/i18n',
+ );
+ return $groups;
+} \ No newline at end of file
diff --git a/i18n_block/i18n_block.install b/i18n_block/i18n_block.install
index 12e78be..bf589c7 100644
--- a/i18n_block/i18n_block.install
+++ b/i18n_block/i18n_block.install
@@ -10,9 +10,21 @@
* Implements hook_install()
*/
function i18n_block_install() {
- db_add_field('block', 'i18n_mode', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Block multilingual mode.'));
+ module_load_install('i18n');
+ i18n_install_create_fields('block', array('i18n_mode'));
// Set module weight for it to run after all block visibility modules have run
db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18n_block' AND type = 'module'");
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_block_update_7000();
+ }
+}
+
+/**
+ * Implements hook_uninstall()
+ */
+function i18n_block_uninstall() {
+ db_drop_field('block', 'i18n_mode');
}
/**
@@ -62,7 +74,7 @@ function i18n_block_schema_alter(&$schema) {
/**
* Implements hook i18n_update_drupal6()
*/
-function i18n_block_i18n_update_drupal6() {
+function i18n_block_update_7000() {
// D6-D7 updates, to be written
// move block language from i18n_blocks into i18n_block_language
// Move block type from i18n_blocks into block table (i18n_mode)
@@ -84,5 +96,6 @@ function i18n_block_i18n_update_drupal6() {
->execute();
}
}
+ // @todo Drop table if everything is ok
}
}
diff --git a/i18n_field/i18n_field.install b/i18n_field/i18n_field.install
index 2f8c50c..3d5fbde 100644
--- a/i18n_field/i18n_field.install
+++ b/i18n_field/i18n_field.install
@@ -7,10 +7,20 @@
*/
/**
+ * Implements hook_install()
+ */
+function i18n_field_install() {
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_field_update_7000();
+ }
+}
+
+/**
* Implements hook_i18n_drupal6_update
*
* Update old string names
*/
-function i18n_field_i18n_update_drupal6() {
-
+function i18n_field_update_7000() {
+ // @todo
}
diff --git a/i18n_menu/i18n_menu.install b/i18n_menu/i18n_menu.install
index cf817ed..05870f0 100644
--- a/i18n_menu/i18n_menu.install
+++ b/i18n_menu/i18n_menu.install
@@ -7,17 +7,33 @@
*/
/**
- * Implements hook_install()
+ * Implements hook_install().
*/
function i18n_menu_install() {
module_load_install('i18n');
- i18n_install_create_fields('menu_links', array('language'));
+ i18n_install_create_fields('menu_links', array('language'));
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_menu_update_7000();
+ }
}
-
+/**
+ * Implements hook_uninstall()
+ */
+function i18n_menu_uninstall() {
+ db_drop_field('menu_links', 'language');
+}
/**
* Implements hook_schema_alter()
*/
function i18n_menu_schema_alter(&$schema) {
- $schema['menu_links']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '');
+ $schema['menu_links']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
+}
+
+/**
+ * Update menu items language field from Drupal 6
+ */
+function i18n_menu_update_7000() {
+ // @todo
} \ No newline at end of file
diff --git a/i18n_menu/i18n_menu.module b/i18n_menu/i18n_menu.module
index 9cb022c..880dbb1 100644
--- a/i18n_menu/i18n_menu.module
+++ b/i18n_menu/i18n_menu.module
@@ -51,14 +51,14 @@ function i18n_menu_i18n_string_list($group) {
}
$query = db_select('menu_links', 'm')
->fields('m')
- ->condition('language', '')
+ ->condition('language', LANGUAGE_NONE)
->condition('customized', 1);
foreach ($query->execute()->fetchAll() as $link) {
$options = unserialize($link->options);
- $strings['menu']['item'][$link->mlid] = array(
- 'title' => $link->link_title,
- 'description' => $options['attributes']['title']
- );
+ $strings['menu']['item'][$link->mlid]['title'] = $link->link_title;
+ if (isset($options['attributes']['title'])) {
+ $strings['menu']['item'][$link->mlid]['description'] = $options['attributes']['title'];
+ }
}
return $strings;
}
@@ -72,7 +72,7 @@ function i18n_menu_i18n_string_list($group) {
function i18n_menu_menu_link_alter(&$item) {
// If we set option to language it causes an error with the link system
// This should handle language only as the links are being manually updated
- if (!empty($item['language'])) {
+ if (i18n_object_langcode($item)) {
$item['options']['langcode'] = $item['language'];
}
elseif (isset($item['language'])) {
@@ -80,7 +80,7 @@ function i18n_menu_menu_link_alter(&$item) {
}
// If we are handling custom menu items of menu module and no language is set,
// invoke translation via i18n_string module.
- if (empty($item['language']) && $item['module'] == 'menu') {
+ if (isset($item['language']) && empty($item['language']) && $item['module'] == 'menu') {
// Set title_callback to FALSE to avoid calling t().
$item['title_callback'] = FALSE;
// Setting the alter option to true ensures that
@@ -474,4 +474,4 @@ function i18n_menu_edit_item_form_submit($form, &$form_state) {
db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", array($form_state['values']['language'], $mid));
return 'admin/build/menu';
}
-*/ \ No newline at end of file
+*/
diff --git a/i18n_node/i18n_node.admin.inc b/i18n_node/i18n_node.admin.inc
new file mode 100644
index 0000000..70a6c51
--- /dev/null
+++ b/i18n_node/i18n_node.admin.inc
@@ -0,0 +1,31 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Extended multilanguage administration and module settings UI.
+ */
+
+/**
+ * Form builder function.
+ */
+function i18n_node_admin_settings() {
+ // Node translation links setting.
+ $form['links'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Content translation links'),
+ );
+ $form['links']['i18n_hide_translation_links'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Hide content translation links'),
+ '#description' => t('Hide the links to translations in content body and teasers. If you choose this option, switching language will only be available from the language switcher block.'),
+ '#default_value' => variable_get('i18n_hide_translation_links', 0),
+ );
+ $form['links']['i18n_translation_switch'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Switch interface for translating'),
+ '#default_value' => variable_get('i18n_translation_switch', 0),
+ '#description' => t('Switch interface language to fit node language when creating or editing a translation. If not checked the interface language will be independent from node language.'),
+ );
+ return system_settings_form($form);
+} \ No newline at end of file
diff --git a/i18n_node/i18n_node.install b/i18n_node/i18n_node.install
index 28f4f54..a23ccdf 100644
--- a/i18n_node/i18n_node.install
+++ b/i18n_node/i18n_node.install
@@ -7,12 +7,13 @@
*/
/**
- * Implements hook_enable
+ * Implements hook_install().
*/
-function i18n_node_enable() {
- if (module_exists('i18n_string')) {
- i18n_node_locale_refresh();
- }
+function i18n_node_install() {
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_node_update_7000();
+ }
}
/**
@@ -31,6 +32,6 @@ function i18n_node_uninstall() {
*
* Update old string names
*/
-function i18n_node_i18n_update_drupal6() {
-
+function i18n_node_update_7000() {
+ // @todo Update from D6 i18n
}
diff --git a/i18n_node/i18n_node.js b/i18n_node/i18n_node.js
index c84192f..4ffbc71 100644
--- a/i18n_node/i18n_node.js
+++ b/i18n_node/i18n_node.js
@@ -1,4 +1,5 @@
// $Id$
+(function ($) {
/**
* Rewrite autocomplete inputs to pass the language of the node currently being
@@ -7,11 +8,14 @@
* This functionality ensures node autocompletes get suggestions for the node's
* language rather than the current interface language.
*/
-Drupal.behaviors.i18n = function (context) {
- if (Drupal.settings && Drupal.settings.i18n) {
- $('form[id^=node-form]', context).find('input.autocomplete[value^=' + Drupal.settings.i18n.interface_path + ']').each(function () {
- $(this).val($(this).val().replace(Drupal.settings.i18n.interface_path, Drupal.settings.i18n.content_path));
- });
+Drupal.behaviors.i18n = {
+ attach : function (context) {
+ if (Drupal.settings && Drupal.settings.i18n) {
+ $('form[id^=node-form]', context).find('input.autocomplete[value^=' + Drupal.settings.i18n.interface_path + ']').each(function () {
+ $(this).val($(this).val().replace(Drupal.settings.i18n.interface_path, Drupal.settings.i18n.content_path));
+ });
+ }
}
};
+})(jQuery);
diff --git a/i18n_node/i18n_node.module b/i18n_node/i18n_node.module
index d2eafa6..fd355a8 100644
--- a/i18n_node/i18n_node.module
+++ b/i18n_node/i18n_node.module
@@ -66,6 +66,23 @@ function i18n_node_add_page() {
}
/**
+ * Implements hook_menu().
+ */
+function i18n_node_menu() {
+ $items['admin/config/regional/i18n/node'] = array(
+ 'title' => 'Node options',
+ 'description' => 'Configure extended options for multilingual content and translations.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_node_admin_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n_node.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 10,
+ );
+ return $items;
+}
+
+/**
* Implements hook_block_view_MODULE_DELTA_alter().
*
* Set translated help text for node/add pages, replace node_help() text.
@@ -225,10 +242,10 @@ function i18n_node_language_mode($type) {
/**
* Implements hook_node_prepare().
*/
-function i18n_node_nodeapi($node) {
+function i18n_node_node_prepare($node) {
global $language;
- if (variable_get('language_content_type_' . $node->type, 0) && empty($node->nid) && empty($node->language) && variable_get('i18n_newnode_current_' . $node->type, 0)) {
+ if (variable_get('language_content_type_' . $node->type, 0) && empty($node->nid) && (empty($node->language) OR $node->language == 'und') && variable_get('i18n_newnode_current_' . $node->type, 0)) {
// Set current language for new nodes if option enabled
$node->language = $language->language;
}
@@ -290,37 +307,32 @@ function i18n_node_translation_link_alter(&$links, $path) {
}
}
-
/**
- * Implements hook_link_alter().
+ * Implements hook_node_view_alter().
*
* Handles links for extended languages. Sets current interface language.
- *
- * @todo Update D7
*/
-function i18n_node_link_alter(&$links, $node) {
+function i18n_node_node_view_alter(&$build) {
global $language;
-
+ $node = $build['#node'];
+
$language_support = i18n_node_language_mode($node);
-
+
// Hide node translation links.
if (variable_get('i18n_hide_translation_links', 0) == 1) {
- foreach ($links as $module => $link) {
- if (strpos($module, 'node_translation') === 0) {
- unset($links[$module]);
- }
- }
+ unset($build['links']['translation']);
}
-
+ //dpm($build);
if (!empty($node->tnid)) {
- foreach (i18n_node_language_list($node) as $langcode) {
- $index = 'node_translation_'. $langcode;
+ $links = $build['links']['translation']['#links'];
+ foreach (i18n_node_language_list($node) as $langcode => $langname) {
+ $index = 'translation_'. $langcode;
if (!empty($links[$index])) {
if (!($language_support & I18N_LANGUAGE_EXTENDED) && $links[$index]['language']->enabled == 0) {
- unset($links[$index]);
+ unset($build['links']['translation']['#links'][$index]);
}
else {
- $links[$index]['language'] = $language;
+ $build['links']['translation']['#links'][$index]['language'] = $language;
}
}
}
@@ -342,11 +354,11 @@ function i18n_node_node_type_update($info) {
if(!empty($info->old_type) && $info->old_type != $info->type) {
i18n_string_update_context("nodetype:type:$old_type:*", "nodetype:type:$type:*");
}
- i18n_string_update(array('node', 'type', $type->type), array(
- 'name' => $type->name,
- 'title_label' => $type->title_label,
- 'description' => $type->description,
- 'help' => $type->help,
+ i18n_string_update(array('node', 'type', $info->type), array(
+ 'name' => $info->name,
+ 'title_label' => $info->title_label,
+ 'description' => $info->description,
+ 'help' => $info->help,
));
}
}
@@ -497,6 +509,26 @@ function i18n_node_form_node_type_form_alter(&$form, &$form_state) {
* Implements hook_form_BASE_FORM_ID_alter().
*/
function i18n_node_form_node_form_alter(&$form, $form_state) {
+ /**
+ * i18n has to override locale.module
+ * drupal_alter() fails to order modules correctly in some cases
+ * for example specific hooks like hook_form_BASE_FORM_ID_alter
+ *
+ * its not possbile to reorder hook_form_BASE_FORM_ID_alter with
+ * hook_module_implements_alter
+ *
+ * @see http://drupal.org/node/765860
+ */
+
+ // call a 'private' implemenation of i18n_node_form_node_form_alter()
+ $form['#after_build'][] = '_i18n_node_form_node_form_alter';
+}
+
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ * Called by i18n_node_form_node_form_alter
+ */
+function _i18n_node_form_node_form_alter($form, $form_state) {
$node = $form['#node'];
if (variable_get('language_content_type_'. $node->type, 0)) {
if (!empty($form['language']['#options'])) {
@@ -515,6 +547,8 @@ function i18n_node_form_node_form_alter(&$form, $form_state) {
if (!empty($form['body_field']['body']['#title'])) {
$form['body_field']['body']['#title'] = i18n_node_translate_type($type, 'body', $form['body_field']['body']['#title']);
}
+
+ return $form;
}
/**
@@ -557,4 +591,4 @@ function i18n_node_translate_type($type, $property, $source = NULL, $options = a
else {
return $source;
}
-} \ No newline at end of file
+}
diff --git a/i18n_path/i18n_path.admin.inc b/i18n_path/i18n_path.admin.inc
new file mode 100644
index 0000000..fe59435
--- /dev/null
+++ b/i18n_path/i18n_path.admin.inc
@@ -0,0 +1,108 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Administration pages for path translation.
+ */
+
+/**
+ * Path overview page
+ */
+function i18n_path_admin_overview() {
+ $default = language_default('language');
+ $result = db_select('i18n_path', 'p')->fields('p')->execute();
+ foreach ($result as $path) {
+ $paths[$path->tpid][$path->language] = $path->path;
+ }
+ if (!empty($paths)) {
+ $build['paths'] = array(
+ '#theme' => 'table',
+ '#header' => array(t('Paths'), t('Operations')),
+ );
+ foreach ($paths as $tpid => $set) {
+ $items = array();
+ foreach ($set as $lang => $path) {
+ // We'll see the path alias if any on the link
+ $items[] = l($path, $path, array('language' => i18n_language($lang)));
+ }
+ $build['paths']['#rows'][] = array(
+ theme('item_list', array('items' => $items)),
+ l(t('Edit'), 'admin/config/regional/i18n/path/edit/' . $tpid),
+ );
+ }
+ }
+ else {
+ $build['message']['#markup'] = t('No path translations.');
+ }
+ return $build;
+}
+
+/**
+ * Path add/edit form
+ */
+function i18n_path_admin_form($form, $form_state, $tpid = NULL) {
+ if ($tpid && is_numeric($tpid)) {
+ $paths = db_query('SELECT language, path FROM {i18n_path} WHERE tpid = :tpid', array(':tpid' => $tpid))->fetchAllKeyed();
+ $form['tpid'] = array('#type' => 'value', '#value' => $paths ? $tpid : 0);
+ }
+ else {
+ $paths = array();
+ }
+ $form['paths'] = array('#tree' => TRUE);
+ foreach (i18n_language_list() as $langcode => $name) {
+ $form['paths'][$langcode] = array(
+ '#type' => 'textfield',
+ '#title' => check_plain($name),
+ '#default_value' => !empty($paths[$langcode]) ? $paths[$langcode] : '',
+ );
+ }
+ $form['controls']['update'] = array('#type' => 'submit', '#value' => t('Save'));
+ if ($paths) {
+ $form['controls']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
+ }
+ return $form;
+}
+
+/**
+ * Process form submission
+ */
+function i18n_path_admin_form_submit($form, &$form_state) {
+ $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : NULL;
+ $tpid = !empty($form_state['values']['tpid']) ? $form_state['values']['tpid'] : 0;
+
+ if ($op == t('Save') && ($paths = array_filter($form_state['values']['paths']))) {
+ if (i18n_path_save_translations($paths, $tpid)) {
+ drupal_set_message(t('The path translation has been saved.'));
+ }
+ else {
+ drupal_set_message(t('Cannot save the path translation. Please try again later.'), 'error');
+ }
+ }
+ elseif ($op == t('Delete')) {
+ db_delete('i18n_path')->condition('tpid', $tpid)->execute();
+ drupal_set_message(t('The path translation has been deleted.'));
+ }
+ $form_state['redirect'] = 'admin/config/regional/i18n/path';
+}
+
+/**
+ * Save path translation set
+ */
+function i18n_path_save_translations($paths, $tpid = NULL) {
+ $paths = array_filter($paths);
+ if (lock_acquire('i18n_path')) {
+ if ($tpid) {
+ db_delete('i18n_path')->condition('tpid', $tpid)->execute();
+ }
+ else {
+ $tpid = 1 + (int)db_query('SELECT MAX(tpid) FROM {i18n_path}')->fetchField();
+ }
+ foreach ($paths as $langcode => $path) {
+ db_insert('i18n_path')
+ ->fields(array('tpid' => $tpid, 'language' => $langcode, 'path' => $path))
+ ->execute();
+ }
+ lock_release('i18n_path');
+ return $tpid;
+ }
+} \ No newline at end of file
diff --git a/i18n_path/i18n_path.info b/i18n_path/i18n_path.info
new file mode 100644
index 0000000..930cc7b
--- /dev/null
+++ b/i18n_path/i18n_path.info
@@ -0,0 +1,6 @@
+; $Id$
+name = Path translation
+description = Define translations for generic paths
+dependencies[] = i18n
+package = Multilanguage
+core = 7.x
diff --git a/i18n_path/i18n_path.install b/i18n_path/i18n_path.install
new file mode 100644
index 0000000..d5cf728
--- /dev/null
+++ b/i18n_path/i18n_path.install
@@ -0,0 +1,58 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the text module.
+ */
+
+/**
+ * Implements hook_install()
+ */
+function i18n_path_install() {
+
+}
+
+/**
+ * Implements hook_schema()
+ */
+function i18n_path_schema() {
+ $schema['i18n_path'] = array(
+ 'description' => 'Path translation',
+ 'fields' => array(
+ 'tpid' => array(
+ 'description' => 'The primary identifier for a translation set.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'path' => array(
+ 'description' => 'The Drupal path this alias is for; e.g. node/12.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'language' => array(
+ 'description' => "The language for which this path is a translation.",
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'pid' => array(
+ 'description' => 'A unique path alias identifier if the path has an alias.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'path' => array('path'),
+ 'path_language' => array('language', 'path'),
+ ),
+ 'primary key' => array('tpid', 'language'),
+ );
+ return $schema;
+}
diff --git a/i18n_path/i18n_path.module b/i18n_path/i18n_path.module
new file mode 100644
index 0000000..39bfeda
--- /dev/null
+++ b/i18n_path/i18n_path.module
@@ -0,0 +1,89 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Internationalization (i18n) module - Path translation
+ */
+
+/**
+ * Get translations for path
+ */
+function i18n_path_get_translations($path) {
+ static $translations;
+
+ if (!isset($translations)) {
+ $translations = drupal_static(__FUNCTION__, array());
+ }
+ if (!isset($translations[$path])) {
+ $translations[$path] = db_query('SELECT p.language, p.path FROM {i18n_path} p INNER JOIN {i18n_path} ps ON p.tpid = ps.tpid WHERE ps.path = :path',
+ array(':path' => $path)
+ )->fetchAllKeyed();
+ }
+ return $translations[$path];
+}
+
+/**
+ * Implements hook_menu()
+ */
+function i18n_path_menu() {
+ $items['admin/config/regional/i18n/path'] = array(
+ 'title' => 'Paths',
+ 'description' => 'Path translation.',
+ 'page callback' => 'i18n_path_admin_overview',
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n_path.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 10,
+ );
+ $items['admin/config/regional/i18n/path/list'] = array(
+ 'title' => 'Paths',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ );
+ $items['admin/config/regional/i18n/path/add'] = array(
+ 'title' => 'Add path translation',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_path_admin_form'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n_path.admin.inc',
+ 'type' => MENU_LOCAL_ACTION,
+ );
+ $items['admin/config/regional/i18n/path/edit/%'] = array(
+ 'title' => 'Edit path translation',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_path_admin_form', 6),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n_path.admin.inc',
+ 'type' => MENU_CALLBACK,
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_url_outbound_alter()
+ */
+/*
+function i18n_path_url_outbound_alter(&$path, &$options, $original_path) {
+ if (!empty($options['language'])) {
+ $langcode = $options['language']->language;
+ $original = $options['alias'] ? drupal_get_normal_path($path, $langcode) : $original_path;
+ if (($translations = i18n_path_get_translations($path)) && !empty($translations[$langcode])) {
+ $path = $options['alias'] ? drupal_get_path_alias($translations[$langcode], $langcode) : $translations[$langcode];
+ }
+ }
+}
+*/
+
+/**
+ * Implements hook_language_switch_links_alter().
+ */
+function i18n_path_language_switch_links_alter(array &$links, $type, $path) {
+ if (($translations = i18n_path_get_translations($path))) {
+ foreach ($links as $lang => &$link) {
+ if (!empty($translations[$lang])) {
+ $link['href'] = $translations[$lang];
+ }
+ }
+ }
+}
diff --git a/i18n_select/i18n_select.admin.inc b/i18n_select/i18n_select.admin.inc
index 4a7dbbe..4cc2c04 100644
--- a/i18n_select/i18n_select.admin.inc
+++ b/i18n_select/i18n_select.admin.inc
@@ -20,7 +20,7 @@ function i18n_select_admin_settings() {
$form['selection']['types'] = array(
'#type' => 'markup',
- '#markup' => '<strong>' . t('Content types to filter by language') . '</strong>',
+ '#markup' => '<strong>' . t('Content to filter by language') . '</strong>',
);
$form['selection']['i18n_select_nodes'] = array(
'#type' => 'checkbox',
@@ -34,7 +34,7 @@ function i18n_select_admin_settings() {
);
$form['selection']['i18n_select_taxonomy'] = array(
'#type' => 'checkbox',
- '#title' => t('Taxonomy (not implemented)'),
+ '#title' => t('Taxonomy'),
'#default_value' => variable_get('i18n_select_taxonomy', TRUE),
);
diff --git a/i18n_select/i18n_select.module b/i18n_select/i18n_select.module
index 6f1df46..85121ed 100644
--- a/i18n_select/i18n_select.module
+++ b/i18n_select/i18n_select.module
@@ -1,6 +1,13 @@
<?php
// $Id$
-
+/**
+ * @file
+ * Multilingual content selection module.
+ *
+ * Alters content queries to add language conditions.
+ *
+ * Queries tagged with 'i18n_select' or that already have a language condition will not be altered.
+ */
/**
* Implementation of hook_menu().
*/
@@ -23,59 +30,109 @@ function i18n_select_menu() {
*
* Rewrite node queries so language selection options are enforced.
*/
-
function i18n_select_query_node_access_alter(QueryAlterableInterface $query) {
-
-
- if (variable_get('i18n_select_nodes', TRUE)) {
+ if (variable_get('i18n_select_nodes', TRUE) && ($table_alias = i18n_select_check_table($query, 'node')) && i18n_select_check_query($query, $table_alias)) {
+ // if the language field is present we don't want to do any filtering.
+ $fields = $query->getFields();
+ if (isset($fields['language'])) {
+ return;
+ }
- // make sure we are dealing with the node table
- $tables = $query->getTables();
- $node_table = '';
- foreach ($tables as $table) {
- if ($table['table'] == 'node') {
- $node_table = $table['alias'];
- break;
- }
+ $language = i18n_select_language();
+ $default_lang = language_default('language');
+
+ if (variable_get('i18n_select_missing_translation', FALSE) && $language != $default_lang) {
+ // we want to include nodes from the default language that haven't been
+ // translated to the current language
+ // SELECT * FROM node n
+ // LEFT JOIN node i18n ON n.tnid > 0 AND n.tnid = i18n.tnid AND i18n.language = 'es'
+ // WHERE n.language='es' OR (n.language='en' AND i18n.nid IS NULL)
+
+ // Make sure the conditions refer to the node table. eg using 'n.promote' instead of 'promote'
+ i18n_select_check_conditions($query, $table_alias);
+
+ $query->leftjoin('node', 'i18n', 'n.tnid > 0 AND n.tnid = i18n.tnid AND i18n.language = :lang', array(':lang' => $language));
+ $query->where($table_alias . '.language=:lang OR (' . $table_alias . '.language=:default_lang AND i18n.nid IS NULL)', array(':lang' => $language, ':default_lang' => $default_lang));
+ } else {
+ $query->where($table_alias . '.language=:lang', array(':lang' => $language));
+ }
+ // Mark query as altered
+ $query->addTag('i18n_select');
+ }
+}
+
+/**
+ * Implementation of hook_query_term_access_alter().
+ *
+ * Rewrite taxonomy term queries so language selection options are enforced.
+ */
+function i18n_select_query_term_access_alter(QueryAlterableInterface $query) {
+ if (variable_get('i18n_select_taxonomy', TRUE) && ($table_alias = i18n_select_check_table($query, 'taxonomy_term_data')) && i18n_select_check_query($query, $table_alias)) {
+ $query->condition($table_alias . '.language', i18n_select_langcodes());
+ // Mark query as altered
+ $query->addTag('i18n_select');
+ }
+}
+
+/**
+ * Check table exists in query and get alias for it
+ *
+ * @todo Should we add the table if not there?
+ */
+function i18n_select_check_table($query, $table_name) {
+ foreach ($query->getTables() as $table) {
+ if ($table['table'] == $table_name) {
+ return !empty($table['alias']) ? $table['alias'] : $table_name;
}
-
- if ($node_table != '') {
-
- // if the language field is present we don't want to do any filtering.
- $fields = $query->getFields();
- if (isset($fields['language'])) {
- return;
+ }
+}
+
+/**
+ * Check all query conditions have a table alias
+ */
+function i18n_select_check_conditions($query, $table_alias) {
+ $conditions =& $query->conditions();
+ foreach ($conditions as $index => $condition) {
+ if (is_array($condition) && isset($condition['field'])) {
+ if (strpos($condition['field'], '.') === FALSE) {
+ $conditions[$index]['field'] = $table_alias . '.' . $condition['field'];
}
-
-
- $language = $GLOBALS['language_content']->language;
- $default_lang = language_default('language');
-
- if (variable_get('i18n_select_missing_translation', FALSE) && $language != $default_lang) {
-
- // we want to include nodes from the default language that haven't been
- // translated to the current language
-
- // SELECT * FROM node n
- // LEFT JOIN node i18n ON n.tnid > 0 AND n.tnid = i18n.tnid AND i18n.language = 'es'
- // WHERE n.language='es' OR (n.language='en' AND i18n.nid IS NULL)
-
-
- $conditions =& $query->conditions();
- // Make sure the conditions refer to the node table. eg using 'n.promote' instead of 'promote'
- foreach ($conditions as $index => $condition) {
- if (is_array($condition) && isset($condition['field'])) {
- if (strpos($condition['field'], '.') === FALSE) {
- $conditions[$index]['field'] = 'n.' . $condition['field'];
- }
- }
- }
-
- $query->leftjoin('node', 'i18n', 'n.tnid > 0 AND n.tnid = i18n.tnid AND i18n.language = :lang', array(':lang' => $language));
- $query->where('n.language=:lang OR (n.language=:default_lang AND i18n.nid IS NULL)', array(':lang' => $language, ':default_lang' => $default_lang));
- } else {
- $query->where('language=:lang', array(':lang' => $language));
+ }
+ }
+}
+
+/**
+ * Check whether we should apply language conditions here:
+ * - The query has not been tagged with 'i18n_select'
+ * - The query doesn't include a language field for selection ??
+ * - The query doesn't have already a language condition
+ */
+function i18n_select_check_query($query, $table_alias, $field_name = 'language') {
+ if (!$query->hasTag('i18n_select')) {
+ $fields = $query->getFields();
+ $table_field = $table_alias . '.' . $field_name;
+ if (isset($fields[$field_name]) || isset($fields[$table_field])) {
+ return FALSE;
+ }
+ foreach ($query->conditions() as $condition) {
+ if (is_array($condition) && isset($condition['field']) && ($condition['field'] == $field_name || $condition['field'] == $table_field)) {
+ return FALSE;
}
}
+ return TRUE;
}
}
+
+/**
+ * Get main language code for content selection
+ */
+function i18n_select_language() {
+ return $GLOBALS['language_content']->language;
+}
+
+/**
+ * Get language codes for content selection to use in query conditions
+ */
+function i18n_select_langcodes() {
+ return array(i18n_select_language(), LANGUAGE_NONE);
+} \ No newline at end of file
diff --git a/i18n_string/i18n_string.admin.inc b/i18n_string/i18n_string.admin.inc
index ba4a128..4699f82 100644
--- a/i18n_string/i18n_string.admin.inc
+++ b/i18n_string/i18n_string.admin.inc
@@ -120,7 +120,7 @@ function i18n_string_admin_settings() {
'#title' => t('Translatable text formats'),
'#options' => $format_list,
'#type' => 'checkboxes',
- '#default_value' => variable_get('i18n_string_allowed_formats', array(variable_get('filter_default_format', 1))),
+ '#default_value' => i18n_string_allowed_formats(),
'#description' => t('Only the strings that have the text formats selected will be allowed by the translation system. All the others will be deleted next time the strings are refreshed.'),
);
// Whitelist text groups without formatted strings for backwards compatibility
@@ -133,20 +133,6 @@ function i18n_string_admin_settings() {
unset($textgroups[$group]);
}
}
- // If there are 'old' textgroups, display the bypass option
- if ($textgroups) {
- $form['i18n_string_allowed_textgroups'] = array(
- '#title' => t('Safe text groups'),
- '#options' => $textgroups,
- '#type' => 'checkboxes',
- '#default_value' => variable_get('i18n_string_allowed_textgroups', array()),
- '#description' => t('Select text groups to bypass filter format checking. . <strong>It is unsafe to check this option unless you are sure all the strings from that text groups are safe for translators</strong>. This option is just for backwards compatibility until all the contributed modules implement the new strings API.'),
- );
- }
- elseif (variable_get('i18n_string_allowed_textgroups', 0)) {
- // Just in case there's a leftover variable before we updated some of the modules
- variable_del('i18n_string_allowed_textgroups');
- }
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
return system_settings_form($form);
}
diff --git a/i18n_string/i18n_string.inc b/i18n_string/i18n_string.inc
index 7bcfab5..600e114 100644
--- a/i18n_string/i18n_string.inc
+++ b/i18n_string/i18n_string.inc
@@ -58,8 +58,7 @@ class i18n_string_default {
* Text format key or NULL if not format (will be allowed)
*/
public static function allowed_format($format = NULL) {
- $allowed_formats = variable_get('i18n_string_allowed_formats', array(variable_get('filter_default_format', 1)));
- return !isset($format) || in_array($format, $allowed_formats);
+ return !$format || in_array($format, i18n_string_allowed_formats());
}
/**
* Add source string to the locale tables for translation.
@@ -482,7 +481,7 @@ class i18n_string_default {
* - 'messages', Whether to print out status messages
*/
public function update($context, $string, $options = array()) {
- $options += array('format' => 0, 'messages' => TRUE, 'watchdog' => TRUE);
+ $options += array('format' => FALSE, 'messages' => TRUE, 'watchdog' => TRUE);
$i18nstring = $this->build_string($context, $string, $options);
$i18nstring->format = $options['format'];
if (!$this->check_string($i18nstring, $options)) {
diff --git a/i18n_string/i18n_string.install b/i18n_string/i18n_string.install
index 39f3852..4e11f3c 100644
--- a/i18n_string/i18n_string.install
+++ b/i18n_string/i18n_string.install
@@ -7,16 +7,27 @@
*/
/**
+ * Implements hook_enable().
+ */
+function i18n_string_enable() {
+ // Refresh locales for enabled modules
+ $modules = module_implements('i18n_string_refresh');
+ i18n_string_modules_enabled($modules);
+}
+
+/**
* Implements hook_install().
*/
function i18n_string_install() {
+ // Add a field to track whether a translation needs updating.
+ module_load_install('i18n');
+ i18n_install_create_fields('locales_target', array('i18n_status'));
// Set module weight for it to run after core modules.
db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18n_string' AND type = 'module'");
- // Add a field to track whether a translation needs updating.
- db_add_field('locales_target', 'i18n_status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
- // Refresh locales for enabled modules
- $modules = module_implements('i18n_string_refresh');
- i18n_string_modules_enabled($modules);
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_string_update_7000();
+ }
}
/**
@@ -111,12 +122,12 @@ function i18n_string_schema_alter(&$schema) {
}
/**
- * Implements hook_i18n_drupal6_update
- *
* Populate fields from old locale table (textgroup, location) and drop indexes from locales_source
*/
-function i18n_string_i18n_drupal6_update() {
+function i18n_string_update_7000() {
// @todo Update from d6
+ variable_del('i18nstrings_allowed_textgroups');
+
}
/**
diff --git a/i18n_string/i18n_string.module b/i18n_string/i18n_string.module
index d44e26d..706be9e 100644
--- a/i18n_string/i18n_string.module
+++ b/i18n_string/i18n_string.module
@@ -70,6 +70,13 @@ function i18n_string_textgroup($textgroup) {
}
/**
+ * Get list of allowed formats for text translation
+ */
+function i18n_string_allowed_formats() {
+ return variable_get('i18n_string_allowed_formats', array(filter_fallback_format()));
+}
+
+/**
* Implements hook_help().
*/
function i18n_string_help($path, $arg) {
@@ -100,7 +107,7 @@ function i18n_string_help($path, $arg) {
case 'admin/config/regional/i18n/strings':
$output = '<p>' . t('When translating user defined strings that have a text format associated, translators will be able to edit the text before it is filtered which may be a security risk for some filters. An obvious example is when using the PHP filter but other filters may also be dangerous.') . '</p>';
$output .= '<p>' . t('As a general rule <strong>do not allow any filtered text to be translated unless the translators already have access to that text format</strong>. However if you are doing all your translations through this site\'s translation UI or the Localization client, and never importing translations for other textgroups than <i>default</i>, filter access will be checked for translators on every translation page.') . '</p>';
- $output .= '<p>' . t('<strong>Important:</strong> After disallowing some text format, use the <a href="@refresh-strings">refresh strings</a> page so forbidden strings are deleted and not allowed anymore for translators.', array('@refresh-strings' => url('admin/build/translate/refresh'))) . '</p>';
+ $output .= '<p>' . t('<strong>Important:</strong> After disallowing some text format, use the <a href="@refresh-strings">refresh strings</a> page so forbidden strings are deleted and not allowed anymore for translators.', array('@refresh-strings' => url('admin/config/regional/translate/i18n_string'))) . '</p>';
return $output;
case 'admin/config/filters':
return '<p>' . t('After updating your text formats do not forget to review the list of formats allowed for string translations on the <a href="@configure-strings">configure translatable strings</a> page.', array('@configure-strings' => url('admin/config/regional/i18n/strings'))) . '</p>';
@@ -122,7 +129,8 @@ function i18n_string_menu() {
'access arguments' => array('translate interface'),
);
$items['admin/config/regional/i18n/strings'] = array(
- 'title' => 'String translation',
+ 'title' => 'Strings',
+ 'description' => 'Options for user defined strings.',
'weight' => 20,
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
diff --git a/i18n_sync/i18n_sync.info b/i18n_sync/i18n_sync.info
index 0892d2f..993062f 100644
--- a/i18n_sync/i18n_sync.info
+++ b/i18n_sync/i18n_sync.info
@@ -3,4 +3,9 @@ name = Synchronize translations
description = Synchronizes taxonomy and fields accross translations of the same content.
dependencies[] = i18n
package = Multilanguage
-core = 6.x \ No newline at end of file
+core = 7.x
+
+files[] = i18n_sync.module
+files[] = i18n_sync.install
+files[] = i18n_sync.module.inc
+files[] = i18n_sync.node.inc \ No newline at end of file
diff --git a/i18n_sync/i18n_sync.install b/i18n_sync/i18n_sync.install
index 6bd7e98..690dcf6 100644
--- a/i18n_sync/i18n_sync.install
+++ b/i18n_sync/i18n_sync.install
@@ -14,4 +14,21 @@
*/
function i18n_sync_install() {
db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18n_sync' AND type = 'module'");
+ // If updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_sync_update_7000();
+ }
+}
+
+/*
+ * D6 update. Variable names
+ */
+// i18nsync_nodeapi_* -> i18n_sync_node_type_*
+function i18n_sync_update_7000() {
+ foreach (node_type_get_types() as $type => $info) {
+ if ($fields = variable_get('i18nsync_nodeapi_' . $type)) {
+ variable_set('i18n_sync_node_type_' . $type, $fields);
+ variable_del('i18nsync_nodeapi_' . $type);
+ }
+ }
} \ No newline at end of file
diff --git a/i18n_sync/i18n_sync.module b/i18n_sync/i18n_sync.module
index 4b83762..a139f87 100644
--- a/i18n_sync/i18n_sync.module
+++ b/i18n_sync/i18n_sync.module
@@ -27,7 +27,7 @@ function i18n_sync($status = NULL) {
if (isset($status)) {
$current = $status;
}
- return $status;
+ return $current;
}
/**
@@ -52,17 +52,6 @@ function i18n_sync_help($path, $arg) {
}
/**
- * Implements hook_theme().
- */
-function i18n_sync_theme() {
- return array(
- 'i18n_sync_workflow_checkbox' => array(
- 'arguments' => array('item' => NULL),
- ),
- );
-}
-
-/**
* Implements hook_form_FORM_ID_alter().
*/
function i18n_sync_form_node_admin_content_alter(&$form, &$form_state) {
@@ -100,27 +89,37 @@ function i18n_sync_form_node_type_form_alter(&$form, &$form_state) {
'#description' => t('Select which fields to synchronize for all translations of this content type.'),
'#disabled' => $disabled,
);
+
$form['i18n_sync']['i18n_sync_node_type'] = array(
- '#title' => t('Synchronize fields'),
- '#type' => 'fieldset',
'#tree' => TRUE,
);
+
// Each set provides title and options. We build a big checkboxes control for it to be
- // saved as an array. Special themeing for group titles.
- foreach (i18n_sync_node_available_fields($type) as $group => $data) {
- $title = $data['#title'];
- if (!empty($data['#options'])) {
- foreach ($data['#options'] as $field => $name) {
- $form['i18n_sync']['i18n_sync_nodeapi'][$field] = array(
- '#group_title' => $title,
- '#title' => $name,
- '#type' => 'checkbox',
- '#default_value' => in_array($field, $current),
- '#theme' => 'i18n_sync_workflow_checkbox',
- '#disabled' => $disabled,
- );
- $title = '';
- }
+ // saved as an array.
+ $current = i18n_sync_node_fields($type);
+ // Group options, group fields by type.
+ $groups = array(
+ 'node' => t('Standard node fields'),
+ 'fields' => t('Configurable fields'),
+ );
+ $fields = array();
+ foreach (i18n_sync_node_options($type) as $field => $info) {
+ $group = isset($info['group']) && isset($groups[$info['group']]) ? $info['group'] : 'node';
+ $fields[$group][$field] = $info;
+ }
+ foreach ($fields as $group => $group_fields) {
+ $form['i18n_sync']['i18n_sync_node_type']['i18n_sync_group_' . $group] = array(
+ '#prefix' => '<strong>', '#suffix' => '</strong>',
+ '#markup' => $groups[$group],
+ );
+ foreach ($group_fields as $field => $info) {
+ $form['i18n_sync']['i18n_sync_node_type'][$field] = array(
+ '#title' => $info['title'],
+ '#type' => 'checkbox',
+ '#default_value' => in_array($field, $current),
+ '#disabled' => $disabled,
+ '#description' => isset($info['description']) ? $info['description'] : '',
+ );
}
}
}
@@ -163,42 +162,35 @@ function i18n_sync_node_delete_prepare($nid) {
}
/**
- * Theming function for workflow checkboxes.
- */
-function theme_i18n_sync_workflow_checkbox($element) {
- $output = $element['#group_title'] ? '<div class="description">'. $element['#group_title'] .'</div>' : '';
- $output .= theme('checkbox', $element);
- return $output;
-}
-
-/**
* Check whether this node is to be synced
*/
function i18n_sync_node_check($node) {
- return translation_supported_type($node->type) && !empty($node->language) && i18n_sync();
+ return translation_supported_type($node->type) && i18n_object_langcode($node) && i18n_sync();
}
/**
* Implements hook_node_load().
*/
-function i18n_sync_node_load($node) {
- // Add instance count for cck fields so we can use the information later, see hook_file_references()
- if (i18n_sync_node_check($node) && !empty($node->tnid) && ($sync_fields = i18n_sync_node_fields($node->type)) && ($content_fields = _i18n_sync_cck_fields($node->type))) {
- if ($translations = _i18n_sync_node_translations($node, TRUE)) {
- $count = count($translations);
- foreach ($sync_fields as $field) {
- if (isset($content_fields[$field]) && !empty($node->$field) && is_array($node->$field)) {
- // The node field should be an array with one or more fields
- // Reminder: Use brackets for $node->{$field}[$key] as $node->$field[$key] won't work
- foreach (array_keys($node->$field) as $key) {
- if (is_array($node->{$field}[$key])) {
- $node->{$field}[$key]['i18n_sync'] = $count;
+function i18n_sync_node_load($nodes) {
+ foreach ($nodes as $node) {
+ // Add instance count for cck fields so we can use the information later, see hook_file_references()
+ if (i18n_sync_node_check($node) && !empty($node->tnid) && ($sync_fields = i18n_sync_node_fields($node->type))) {
+ if ($translations = i18n_sync_node_translations($node)) {
+ $count = count($translations);
+ foreach ($sync_fields as $field) {
+ if (isset($content_fields[$field]) && !empty($node->$field) && is_array($node->$field)) {
+ // The node field should be an array with one or more fields
+ // Reminder: Use brackets for $node->{$field}[$key] as $node->$field[$key] won't work
+ foreach (array_keys($node->$field) as $key) {
+ if (is_array($node->{$field}[$key])) {
+ $node->{$field}[$key]['i18n_sync'] = $count;
+ }
}
}
}
}
}
- }
+ }
}
/**
@@ -209,43 +201,34 @@ function i18n_sync_node_insert($node) {
if (i18n_sync_node_check($node) && !empty($node->translation_source)) {
// Set tnid that is not set by translation module
$node->tnid = $node->translation_source->tnid ? $node->translation_source->tnid : $node->translation_source->nid;
- // If we have files, we need to save the files that have been inherited
- if (!empty($node->files) && i18n_sync_node_fields($node->type, 'files')) {
- foreach ($node->files as $fid => $file) {
- $file = (object)$file;
- if (empty($file->remove) && empty($file->new)) {
- db_query("INSERT INTO {upload} (fid, nid, vid, list, description, weight) VALUES (%d, %d, %d, %d, '%s', %d)", $file->fid, $node->nid, $node->vid, $file->list, $file->description, $file->weight);
- }
- }
- }
i18n_sync_node_update($node);
}
}
/**
+ * Get node translations if any, excluding the node itself
+ */
+function i18n_sync_node_translations($node, $exclude = FALSE) {
+ // Maybe translations are already here
+ if (!empty($node->tnid) && ($translations = translation_node_get_translations($node->tnid))) {
+ unset($translations[$node->language]);
+ return $translations;
+ }
+}
+
+/**
* Implements hook_node_update().
*/
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_translations($node, TRUE))) {
- i18n_sync(FALSE);
- $count = 0;
+ if (i18n_sync_node_check($node) && !empty($node->tnid) && ($fields = i18n_sync_node_fields($node->type)) && ($translations = i18n_sync_node_translations($node))) {
// If we have fields we need to reload them so we have the full data (fid, etc...)
if (!empty($node->files) && in_array('files', $fields)) {
$node->files = upload_load($node);
}
- // Disable language selection temporarily, enable it again later
- i18n_select(FALSE);
- foreach ($translations as $trnode) {
- if ($node->nid != $trnode->nid) {
- i18n_sync_node_translation($node, $trnode, $fields, $op);
- $count++;
- }
- }
- i18n_select(TRUE);
- i18n_sync(TRUE);
- drupal_set_message(format_plural($count, 'One node translation has been synchronized.', 'All @count node translations have been synchronized.'));
- }
+ module_load_include('node.inc', 'i18n_sync');
+ i18n_sync_node_translation($node, $translations, $fields, 'update');
+ }
}
/**
@@ -254,7 +237,7 @@ function i18n_sync_node_update($node) {
function i18n_sync_node_prepare($node) {
// If creating a translation, copy over all the fields to be synchronized.
if (empty($node->nid) && !empty($node->translation_source) && ($sync_fields = i18n_sync_node_fields($node->type))) {
- foreach ($field_list as $field) {
+ foreach ($sync_fields as $field) {
if (empty($node->translation_source->$field)) continue;
switch ($field) {
case 'taxonomy':
@@ -269,197 +252,6 @@ function i18n_sync_node_prepare($node) {
}
/**
- * Synchronizes fields for node translation.
- *
- * There's some specific handling for known fields like:
- * - files, for file attachments.
- * - iid (CCK node attachments, translations for them will be handled too).
- *
- * All the rest of the fields will be just copied over.
- * The 'revision' field will have the special effect of creating a revision too for the translation.
- *
- * @param $node
- * Source node being edited.
- * @param $translation
- * Node translation to synchronize, just needs nid property.
- * @param $fields
- * List of fields to synchronize.
- * @param $op
- * Node operation (insert|update).
- */
-function i18n_sync_node_translation($node, $translation, $fields, $op) {
- // Load full node, we need all data here.
- $translation = node_load($translation->nid, NULL, TRUE);
-
- // Collect info on any CCK fields.
- $content_fields = _i18n_sync_cck_fields($node->type);
-
- foreach ($fields as $field) {
- // Check for CCK fields first.
- if (isset($content_fields[$field]) && isset($node->$field)) {
- switch ($content_fields[$field]['type']) {
- // TODO take type specific actions.
-
- // Filefields and imagefields are syncronized equally.
- case 'filefield':
- case 'imagefield':
- i18n_sync_node_translation_filefield_field($node, $translation, $field);
- break;
-
- case 'nodereference':
- i18n_sync_node_translation_nodereference_field($node, $translation, $field);
- break;
-
- default:
- // For fields that don't need special handling.
- $translation->$field = $node->$field;
- }
- // Skip over the regular handling.
- continue;
- }
- else {
- switch ($field) {
- case 'taxonomy': // Do nothing it has already been syncd.
- i18n_sync_node_taxonomy($translation, $node);
- break;
-
- case 'parent': // Book outlines, translating parent page if exists.
- case 'iid': // Attached image nodes.
- i18n_sync_node_translation_attached_node($node, $translation, $field);
- break;
-
- case 'images':
- $translation->images = $node->images;
- // Intentional no break so 'images' synchronizes files too.
- // About images, see related patch status: http://drupal.org/node/360643
- // @todo Weird things may happen if 'images' and 'files' are both selected
- case 'files':
- // Sync existing attached files. This should work for images too
- foreach ((array)$node->files as $fid => $file) {
- if (isset($translation->files[$fid])) {
- // Just update list and weight properties, description can be different
- $translation->files[$fid]->list = $file->list;
- $translation->files[$fid]->weight = $file->weight;
- }
- else {
- // New file. Clone so we can set the new property just for this translation
- $translation->files[$fid] = clone $file;
- $translation->files[$fid]->new = TRUE;
- }
- }
- // Drop removed files.
- foreach ((array)$translation->files as $fid => $file) {
- if (!isset($node->files[$fid])) {
- $translation->files[$fid]->remove = TRUE;
- }
- }
- break;
-
- default:
- // For fields that don't need special handling.
- if (isset($node->$field)) {
- $translation->$field = $node->$field;
- }
- }
- }
- }
- node_save($translation);
-}
-
-/**
- * Synchronize taxonomy.
- *
- * Translate translatable terms, just copy over the rest.
- */
-function i18n_sync_node_taxonomy(&$node, &$source) {
- if (module_exists('i18n_taxonomy') && is_array($source->taxonomy)) {
- // Load clean source node taxonomy so we don't need to handle weird form input
- if (!isset($source->i18n_taxonomy)) {
- $source->i18n_taxonomy = i18n_taxonomy_node_get_terms($source);
- }
- $node->taxonomy = i18n_taxonomy_translate_terms($source->i18n_taxonomy, $node->language, FALSE);
- }
- else {
- // If not multilingual taxonomy enabled, just copy over.
- $node->taxonomy = $source->taxonomy;
- }
-}
-
-/**
- * Node attachments (CCK) that may have translation.
- */
-function i18n_sync_node_translation_attached_node(&$node, &$translation, $field) {
- if ($attached = node_load($node->$field)) {
- $translation->$field = i18n_sync_node_translation_reference_field($attached, $node->$field, $translation->language);
- }
-}
-
-/**
- * Translating a nodereference field (cck).
- */
-function i18n_sync_node_translation_nodereference_field(&$node, &$translation, $field) {
- $translated_references = array();
- foreach ($node->$field as $reference) {
- if ($reference_node = node_load($reference['nid'])) {
- $translated_references[] = array(
- 'nid' => i18n_sync_node_translation_reference_field($reference_node, $reference['nid'], $translation->language)
- );
- }
- }
- $translation->$field = $translated_references;
-}
-
-/**
- * Translating an filefield (cck).
- */
-function i18n_sync_node_translation_filefield_field(&$node, &$translation, $field) {
- if (is_array($node->$field)) {
- $translated_images = array();
- foreach ($node->$field as $file) {
- $found = false;
-
- // Try to find existing translations of the filefield items and reference them.
- foreach ($translation->$field as $translation_image) {
- if ($file['fid'] == $translation_image['fid']) {
- $translated_images[] = $translation_image;
- $found = true;
- }
- }
-
- // If there was no translation found for the filefield item, just copy it.
- if (!$found) {
- $translated_images[] = $file;
- }
- }
- $translation->$field = $translated_images;
- }
-}
-
-/**
- * Helper function to which translates reference field. We try to use translations for reference, otherwise fallback.
- * 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.
- */
-function i18n_sync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
- if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
- // This content type has translations, find the one.
- if (($reference_trans = translation_node_get_translations($reference_node->tnid)) && isset($reference_trans[$langcode])) {
- return $reference_trans[$langcode]->nid;
- }
- else {
- // No requested language found, just copy the field.
- return $default_value;
- }
- }
- else {
- // Content type without language, just copy the field.
- return $default_value;
- }
-}
-
-/**
* Returns list of fields to synchronize for a given content type.
*
* @param $type
@@ -468,121 +260,43 @@ function i18n_sync_node_translation_reference_field(&$reference_node, $default_v
* Optional field name to check whether it is in the list
*/
function i18n_sync_node_fields($type, $field = NULL) {
- $fields = variable_get('i18n_sync_nodeapi_'. $type, array());
+ $fields = variable_get('i18n_sync_node_type_'. $type, array());
return $field ? in_array($field, $fields) : $fields;
}
/**
* Returns list of available fields for given content type.
*
- * There are two hidden variables (without UI) that can be used to add fields
- * with the form array('field' => 'Field name')
- * - i18n_sync_fields_node
- * - i18n_sync_fields_node_$type;
- *
* Fields can also be changed using hook_i18n_sync_fields_alter($fields, $type)
*
* @param $type
* Node type.
*/
-function i18n_sync_node_available_fields($type) {
- static $cache;
-
- if (!isset($cache[$type])) {
- // Default node fields.
- $fields['node']['#title'] = t('Standard node fields.');
- $options = variable_get('i18n_sync_fields_node', array());
- $options += array(
- 'name' => t('Author'),
- 'status' => t('Status'),
- 'promote' => t('Promote'),
- 'moderate' => t('Moderate'),
- 'sticky' => t('Sticky'),
- 'revision' => t('Revision (Create also new revision for translations)'),
- 'parent' => t('Book outline (with the translated parent)'),
- 'taxonomy' => t('Taxonomy terms'),
- );
- if (module_exists('comment')) {
- $options['comment'] = t('Comment settings');
- }
- if (module_exists('upload')) {
- $options['files'] = t('File attachments');
- }
- // Location module
- if (module_exists('location')) {
- $options['locations'] = t('Location settings');
- }
- // If no type defined yet, that's it.
- $fields['node']['#options'] = $options;
-
- if (!$type) {
- return $fields;
- }
-
- // Get variable for this node type.
- $fields += variable_get("i18n_sync_fields_node_$type", array());
-
- // Image and image attach.
- if (module_exists('image') && $type == 'image') {
- $image['images'] = t('Image files');
- }
- if (module_exists('image_attach') && variable_get('image_attach_'. $type, 0)) {
- $image['iid'] = t('Attached image nodes');
- }
- if (!empty($image)) {
- $fields['image']['#title'] = t('Image module');
- $fields['image']['#options'] = $image;
- }
- // Event fields.
- if (variable_get('event_nodeapi_'. $type, 'never') != 'never') {
- $fields['event']['#title'] = t('Event fields');
- $fields['event']['#options'] = array(
- 'event_start' => t('Event start'),
- 'event_end' => t('Event end'),
- 'timezone' => t('Timezone')
- );
- }
-/*
- // Get CCK fields.
- if (($contentfields = _i18n_sync_cck_fields($type))) {
- // Get context information.
- $info = module_invoke('content', 'fields', NULL, $type);
- $fields['cck']['#title'] = t('CCK fields');
- foreach ($contentfields as $name => $data) {
- $fields['cck']['#options'][$data['field_name']] = $data['widget']['label'];
- }
- }
-*/
- // Give a chance to modules to change/remove/add their own fields
- drupal_alter('i18n_sync_fields', $fields, $type);
-
- $cache[$type] = $fields;
- }
- return $cache[$type];
+function i18n_sync_node_options($type) {
+ return i18n_sync_options('node', $type);
}
/**
- * Helper function to get list of cck fields
- */
-function _i18n_sync_cck_fields($type) {
- if (($content = module_invoke('content', 'types', $type)) && !empty($content['fields'])) {
- return $content['fields'];
- }
-}
-
-/**
- * Get node translations if any, optionally excluding this node
+ * Returns list of available fields for given entity / bundle.
*
- * Translations will be stored in the node itself so we have them cached
+ * Fields can also be changed using hook_i18n_sync_options_alter($fields, $type)
+ *
+ * @param $entity_type
+ * Entity type.
+ * @param
*/
-function _i18n_sync_node_translations($node, $exclude = FALSE) {
- // Maybe translations are already here
- if (!empty($node->tnid) && ($translations = translation_node_get_translations($node->tnid))) {
- if ($exclude && $node->language) {
- unset($translations[$node->language]);
- }
- return $translations;
+function i18n_sync_options($entity_type, $bundle_name) {
+ $cache = &drupal_static(__FUNCTION__);
+
+ if (!isset($cache[$entity_type][$bundle_name])) {
+ module_load_include('modules.inc', 'i18n_sync');
+ $fields = module_invoke_all('i18n_sync_options', $entity_type, $bundle_name);
+ // Give a chance to modules to change/remove/add their own fields
+ drupal_alter('i18n_sync_options', $fields, $entity_type, $bundle_name);
+ $cache[$entity_type][$bundle_name] = $fields;
}
+
+ return $cache[$entity_type][$bundle_name];
}
/**
@@ -594,22 +308,3 @@ function i18n_sync_file_references($file) {
// We have marked the field previously on nodeapi load
return !empty($file->i18n_sync);
}
-
-/*
- * Sample CCK field definition for Drupal 5.
-'field_text' =>
- array
- 'field_name' => string 'field_text' (length=10)
- 'type' => string 'text' (length=4)
- 'required' => string '0' (length=1)
- 'multiple' => string '1' (length=1)
- 'db_storage' => string '0' (length=1)
- 'text_processing' => string '0' (length=1)
- 'max_length' => string '' (length=0)
- 'allowed_values' => string '' (length=0)
- 'allowed_values_php' => string '' (length=0)
- 'widget' =>
- array
- ...
- 'type_name' => string 'test' (length=4)
- */
diff --git a/i18n_sync/i18n_sync.modules.inc b/i18n_sync/i18n_sync.modules.inc
new file mode 100644
index 0000000..190e82e
--- /dev/null
+++ b/i18n_sync/i18n_sync.modules.inc
@@ -0,0 +1,66 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Internationalization (i18n) package. Synchronization of translations
+ *
+ * Implements hook_i18n_sync_node_fields() for several core and contrib modules.
+ */
+
+/**
+ * Book module. Implements hook_i18n_sync_options().
+ */
+function book_i18n_sync_options($entity_type, $bundle_name) {
+ if ($entity_type == 'node') {
+ return array(
+ 'parent' => array('title' =>t('Book outline'), 'description' => t('Set the translated parent for each translation if possible.')),
+ );
+ }
+}
+
+/**
+ * Comment module. Implements hook_i18n_sync_options().
+ */
+function comment_i18n_sync_options($entity_type, $bundle_name) {
+ if ($entity_type == 'node') {
+ $fields['comment'] = array('title' => t('Comment settings'));
+ return $fields;
+ }
+}
+
+/**
+ * Field module. Implements hook_i18n_sync_options().
+ */
+function field_i18n_sync_options($entity_type, $bundle_name) {
+ $sync_fields = array();
+ if ($bundle_name) {
+ $instances = field_info_instances($entity_type, $bundle_name);
+ foreach ($instances as $name => $instance) {
+ $field = field_info_field($instance['field_name']);
+ $sync_fields[$name] = array(
+ 'title' => $instance['label'],
+ 'description' => $instance['description'],
+ 'field' => $field,
+ 'group' => 'fields',
+ );
+ }
+ }
+ return $sync_fields;
+}
+
+/**
+ * Node module. Implements hook_i18n_sync_options().
+ */
+function node_i18n_sync_options($entity_type, $bundle_name) {
+ if ($entity_type == 'node') {
+ return array(
+ 'name' => array('title' => t('Author')),
+ 'status' => array('title' => t('Status')),
+ 'promote' => array('title' => t('Promote')),
+ 'moderate' => array('title' => t('Moderate')),
+ 'sticky' => array('title' => t('Sticky')),
+ 'revision' => array('title' => t('Revision'), 'description' => t('Create also new revision for translations')),
+ );
+ }
+}
diff --git a/i18n_sync/i18n_sync.node.inc b/i18n_sync/i18n_sync.node.inc
new file mode 100644
index 0000000..75a443d
--- /dev/null
+++ b/i18n_sync/i18n_sync.node.inc
@@ -0,0 +1,214 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Internationalization (i18n) package. Synchronization of translations
+ *
+ * Node synchronization.
+ */
+
+/**
+ * Synchronizes fields for node translation.
+ *
+ * There's some specific handling for known fields like:
+ * - files, for file attachments.
+ * - iid (CCK node attachments, translations for them will be handled too).
+ *
+ * All the rest of the fields will be just copied over.
+ * The 'revision' field will have the special effect of creating a revision too for the translation.
+ *
+ * @param $node
+ * Source node being edited.
+ * @param $translations
+ * Node translations to synchronize, just needs nid property.
+ * @param $fields
+ * List of fields to synchronize.
+ * @param $op
+ * Node operation (insert|update).
+ */
+function i18n_sync_node_translation($node, $translations, $fields, $op) {
+ $count = 0;
+ // Disable language selection and synchronization temporarily, enable it again later
+ i18n_select(FALSE);
+ i18n_sync(FALSE);
+ foreach ($translations as $translation) {
+ // If translation is the same node, we cannot synchronize with itself
+ if ($node->nid == $translation->nid) {
+ continue;
+ }
+ // Load full node, we need all data here.
+ $translation = node_load($translation->nid, NULL, TRUE);
+ $field_info = i18n_sync_node_options($node->type);
+ // Invoke callback for each field, the default is just copy over
+ foreach ($fields as $field) {
+ if (!empty($field_info[$field]['callback'])) {
+ call_user_func($field_info[$field]['callback'], $node, $translation, $field);
+ }
+ elseif (!empty($field_info[$field]['field'])) {
+ i18n_sync_node_translation_field_field($node, $translation, $field, $field_info[$field]['field']);
+ }
+ }
+ // Give a chance to other modules for additional sync
+ module_invoke_all('i18n_sync_node_translation', $node, $translation, $fields);
+ node_save($translation);
+ $count++;
+ }
+ i18n_sync(TRUE);
+ i18n_select(TRUE);
+ drupal_set_message(format_plural($count, 'One node translation has been synchronized.', 'All @count node translations have been synchronized.'));
+}
+
+/**
+ * Synchronize default field
+ */
+function i18n_sync_node_translation_default_field($node, $translation, $field) {
+ switch ($field) {
+ case 'parent': // Book outlines, translating parent page if exists.
+ case 'iid': // Attached image nodes.
+ i18n_sync_node_translation_attached_node($node, $translation, $field);
+ break;
+
+ case 'files':
+ // Sync existing attached files. This should work for images too
+ foreach ((array)$node->files as $fid => $file) {
+ if (isset($translation->files[$fid])) {
+ // Just update list and weight properties, description can be different
+ $translation->files[$fid]->list = $file->list;
+ $translation->files[$fid]->weight = $file->weight;
+ }
+ else {
+ // New file. Clone so we can set the new property just for this translation
+ $translation->files[$fid] = clone $file;
+ $translation->files[$fid]->new = TRUE;
+ }
+ }
+ // Drop removed files.
+ foreach ((array)$translation->files as $fid => $file) {
+ if (!isset($node->files[$fid])) {
+ $translation->files[$fid]->remove = TRUE;
+ }
+ }
+ break;
+
+ default:
+ // For fields that don't need special handling.
+ if (isset($node->$field)) {
+ $translation->$field = $node->$field;
+ }
+ }
+}
+
+
+/**
+ * Synchronize taxonomy.
+ *
+ * Translate translatable terms, just copy over the rest.
+ */
+function i18n_sync_node_translation_taxonomy(&$node, &$source) {
+ if (module_exists('i18n_taxonomy') && is_array($source->taxonomy)) {
+ // Load clean source node taxonomy so we don't need to handle weird form input
+ if (!isset($source->i18n_taxonomy)) {
+ $source->i18n_taxonomy = i18n_taxonomy_node_get_terms($source);
+ }
+ $node->taxonomy = i18n_taxonomy_translate_terms($source->i18n_taxonomy, $node->language, FALSE);
+ }
+ else {
+ // If not multilingual taxonomy enabled, just copy over.
+ $node->taxonomy = $source->taxonomy;
+ }
+}
+
+/**
+ * Node attachments (CCK) that may have translation.
+ */
+function i18n_sync_node_translation_attached_node(&$node, &$translation, $field) {
+ if ($attached = node_load($node->$field)) {
+ $translation->$field = i18n_sync_node_translation_reference_field($attached, $node->$field, $translation->language);
+ }
+}
+
+/**
+ * Translating a nodereference field (cck).
+ */
+function i18n_sync_node_translation_nodereference_field(&$node, &$translation, $field) {
+ $translated_references = array();
+ foreach ($node->$field as $reference) {
+ if ($reference_node = node_load($reference['nid'])) {
+ $translated_references[] = array(
+ 'nid' => i18n_sync_node_translation_reference_field($reference_node, $reference['nid'], $translation->language)
+ );
+ }
+ }
+ $translation->$field = $translated_references;
+}
+
+/**
+ * Translating an filefield (cck).
+ */
+function i18n_sync_node_translation_filefield_field(&$node, &$translation, $field) {
+ if (is_array($node->$field)) {
+ $translated_images = array();
+ foreach ($node->$field as $file) {
+ $found = false;
+
+ // Try to find existing translations of the filefield items and reference them.
+ foreach ($translation->$field as $translation_image) {
+ if ($file['fid'] == $translation_image['fid']) {
+ $translated_images[] = $translation_image;
+ $found = true;
+ }
+ }
+
+ // If there was no translation found for the filefield item, just copy it.
+ if (!$found) {
+ $translated_images[] = $file;
+ }
+ }
+ $translation->$field = $translated_images;
+ }
+}
+
+/**
+ * Helper function to which translates reference field. We try to use translations for reference, otherwise fallback.
+ * 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.
+ */
+function i18n_sync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
+ if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
+ // This content type has translations, find the one.
+ if (($reference_trans = translation_node_get_translations($reference_node->tnid)) && isset($reference_trans[$langcode])) {
+ return $reference_trans[$langcode]->nid;
+ }
+ else {
+ // No requested language found, just copy the field.
+ return $default_value;
+ }
+ }
+ else {
+ // Content type without language, just copy the field.
+ return $default_value;
+ }
+}
+
+/**
+ * Synchronize configurable field
+ *
+ * @param $field_info
+ * Field API field information.
+ */
+function i18n_sync_node_translation_field_field($node, $translation, $field, $field_info) {
+ switch ($field_info['type']) {
+ case 'taxonomy_term_reference':
+ // Do nothing it has already been syncd.
+ // i18n_sync_node_translation_taxonomy($translation, $node);
+ break;
+ default:
+ // For fields that don't need special handling, just copy it over if defined.
+ if (isset($node->$field)) {
+ $translation->$field = $node->$field;
+ }
+ }
+} \ No newline at end of file
diff --git a/i18n_taxonomy/i18n_taxonomy.admin.inc b/i18n_taxonomy/i18n_taxonomy.admin.inc
index 31b9735..41a75df 100644
--- a/i18n_taxonomy/i18n_taxonomy.admin.inc
+++ b/i18n_taxonomy/i18n_taxonomy.admin.inc
@@ -19,11 +19,11 @@ function i18n_taxonomy_page_vocabulary($vocabulary, $op = NULL, $tid = NULL) {
switch ($op) {
case 'edit':
drupal_set_title(t('Edit term translations'));
- $output = drupal_get_form('i18n_taxonomy_translation_term_form', $vocabulary->vid, $tid);
+ $output = drupal_get_form('i18n_taxonomy_translation_term_form', $vocabulary, $tid);
break;
default:
- $output = i18n_taxonomy_translation_overview($vocabulary->vid);
+ $output = i18n_taxonomy_translation_overview($vocabulary);
}
return $output;
}
@@ -31,41 +31,40 @@ function i18n_taxonomy_page_vocabulary($vocabulary, $op = NULL, $tid = NULL) {
/**
* Produces a vocabulary translation form.
*/
-function i18n_taxonomy_translation_term_form($form_state, $vid, $trid = NULL, $edit = array()) {
- $languages = i18n_language_list();
-
- if ($trid == 'new') {
- $translations = array();
- $form['trid'] = array(
- '#type' => 'hidden',
- '#value' => 0
- );
+function i18n_taxonomy_translation_term_form($form, $form_state, $vocabulary, $translation_set = NULL) {
+ if ($translation_set) {
+ $trid = $translation_set['trid'];
+ $translations = $translation_set['translations'];
}
else {
- $form['trid'] = array(
- '#type' => 'hidden',
- '#value' => $trid
- );
- $translations = i18n_taxonomy_term_get_translations(array('trid' => $trid));
+ $trid = 0;
+ $translations = array();
}
- $vocabulary = taxonomy_vocabulary_load($vid);
-
+ $form['vocabulary'] = array('#type' => 'value', '#value' => $vocabulary);
+ $form['trid'] = array(
+ '#type' => 'hidden',
+ '#value' => $trid
+ );
+ $form['translations'] = array('#tree' => TRUE);
// List of terms for languages.
- foreach ($languages as $lang => $langname) {
- $current = isset($translations[$lang]) ? $translations[$lang]->tid : '';
- $tree = i18n_taxonomy_get_tree($vid, $lang);
- $form[$lang] = array('#type' => 'fieldset', '#tree' => TRUE);
- $form[$lang]['tid'] = _i18n_taxonomy_term_select($langname, $current, $tree);
- $form[$lang]['old'] = array(
- '#type' => 'hidden',
- '#value' => $current
+ foreach (i18n_language_list() as $lang => $langname) {
+ $form['translations'][$lang] = array(
+ '#title' => $langname,
+ '#type' => 'textfield',
+ '#default_value' => isset($translations[$lang]) ? $translations[$lang]->name : '',
+ '#autocomplete_path' => 'i18n/taxonomy/autocomplete/' . $vocabulary->machine_name . '/' . $lang,
+ '#langcode' => $lang,
+ '#size' => 80,
+ '#maxlength' => 1024,
+ '#element_validate' => array('i18n_taxonomy_autocomplete_validate'),
);
+
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save')
);
- if ($trid != 'new') {
+ if ($trid) {
$form['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete')
@@ -79,19 +78,64 @@ function i18n_taxonomy_translation_term_form($form_state, $vid, $trid = NULL, $e
}
/**
+ * Form element validate handler for taxonomy term autocomplete element.
+ */
+function i18n_taxonomy_autocomplete_validate($element, &$form_state) {
+ // Autocomplete widgets do not send their tids in the form, so we must detect
+ // them here and process them independently.
+ $value = array();
+ if ($tags = $element['#value']) {
+ // Collect candidate vocabularies.
+ $vocabulary = $form_state['values']['vocabulary'];
+ $vocabularies[$vocabulary->vid] = $vocabulary;
+
+ // Translate term names into actual terms.
+ $typed_terms = drupal_explode_tags($tags);
+ foreach ($typed_terms as $typed_term) {
+ // See if the term exists in the chosen vocabulary and return the tid;
+ // otherwise, create a new 'autocreate' term for insert/update.
+ if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => $vocabulary->vid, 'language' => $element['#langcode']))) {
+ $term = array_pop($possibilities);
+ }
+ else {
+ $vocabulary = reset($vocabularies);
+ $term = array(
+ 'tid' => 'autocreate',
+ 'vid' => $vocabulary->vid,
+ 'name' => $typed_term,
+ 'vocabulary_machine_name' => $vocabulary->machine_name,
+ 'language' => $element['#langcode'],
+ );
+ }
+ $value[] = (array)$term;
+ }
+ }
+
+ form_set_value($element, $value, $form_state);
+}
+
+/**
* Form callback: Process vocabulary translation form.
*/
function i18n_taxonomy_translation_term_form_submit($form, &$form_state) {
switch ($form_state['values']['op']) {
case t('Save'):
- i18n_taxonomy_translation_save($form_state['values'], $form_state['values']['trid']);
- drupal_set_message(t('Term translations have been updated.'));
+ if (i18n_taxonomy_translation_save($form_state['values']['translations'], $form_state['values']['trid'])) {
+ drupal_set_message(t('Term translations have been updated.'));
+ }
+ else {
+ drupal_set_message(t('Cannot update term translations'), 'error');
+ }
break;
case t('Delete'):
// Delete old translations for this trid.
- db_query("UPDATE {term_data} SET trid = 0 WHERE trid = %d", $form_state['values']['trid']);
+ db_update('taxonomy_term_data')
+ ->fields(array('trid' => 0))
+ ->condition('trid', $form_state['values']['trid'])
+ ->execute();
drupal_set_message(t('The term translation has been deleted.'));
+ $form_state['redirect'] = 'admin/structure/taxonomy/' . $form_state['values']['vocabulary']->machine_name . '/translation';
break;
}
}
@@ -105,25 +149,45 @@ function i18n_taxonomy_translation_term_form_submit($form, &$form_state) {
* Optional translation set id.
*/
function i18n_taxonomy_translation_save($terms, $trid = 0) {
- // Delete old translations for this trid.
- if ($trid) {
- db_query("UPDATE {term_data} SET trid = 0 WHERE trid = %d", $trid);
- }
- // Now pick up all the tids in an array.
- $translations = array();
- foreach (i18n_language_list() as $lang => $name) {
- if (isset($terms[$lang]) && ($term = (array)$terms[$lang]) && $tid = $term['tid']) {
- $translations[$lang] = $tid;
- }
- }
// Now set a translation set with all these terms. We need some table locking to avoid race conditions.
// when other translations created simulaneously. @TODO Find a better way.
- if (count($translations)) {
- db_lock_table('term_data');
- $trid = (is_numeric($trid) && $trid) ? $trid : i18n_taxonomy_next_trid();
- $params = array_merge(array($trid), $translations);
- db_query('UPDATE {term_data} SET trid = %d WHERE tid IN('. db_placeholders($translations) .')', $params);
- db_unlock_tables();
+ if (lock_acquire('i18n_taxonomy')) {
+ // Delete old translations for this trid.
+ if ($trid) {
+ db_update('taxonomy_term_data')
+ ->fields(array('trid' => 0))
+ ->condition('trid', $trid)
+ ->execute();
+ }
+ // Now pick up all the tids in an array.
+ $translations = array();
+ foreach (i18n_language_list() as $lang => $name) {
+ if (!empty($terms[$lang]) && ($item = reset($terms[$lang]))) {
+ if ($item['tid'] == 'autocreate') {
+ $term = (object) $item;
+ unset($term->tid);
+ taxonomy_term_save($term);
+ }
+ else {
+ $term = (object) $item;
+ }
+ $translations[$lang] = $term->tid;
+ }
+ }
+
+ if (count($translations)) {
+ $trid = (is_numeric($trid) && $trid) ? $trid : i18n_taxonomy_next_trid();
+ $params = array_merge(array($trid), $translations);
+ db_update('taxonomy_term_data')
+ ->fields(array('trid' => $trid))
+ ->condition('tid', $translations)
+ ->execute();
+ }
+ lock_release('i18n_taxonomy');
+ return $trid;
+ }
+ else {
+ return FALSE;
}
}
@@ -131,15 +195,13 @@ function i18n_taxonomy_translation_save($terms, $trid = 0) {
* Get next free trid.
*/
function i18n_taxonomy_next_trid() {
- $current = (int)db_result(db_query('SELECT max(trid) FROM {term_data}'));
- return $current + 1;
+ return 1 + (int)db_query('SELECT MAX(trid) FROM {taxonomy_term_data}')->fetchField();
}
/**
* Generate a tabular listing of translations for vocabularies.
*/
-function i18n_taxonomy_translation_overview($vid) {
- $vocabulary = taxonomy_vocabulary_load($vid);
+function i18n_taxonomy_translation_overview($vocabulary) {
drupal_set_title(check_plain($vocabulary->name));
$output = '';
@@ -148,9 +210,9 @@ function i18n_taxonomy_translation_overview($vid) {
$links = array();
$types = array();
// Get terms/translations for this vocab.
- $result = db_query('SELECT * FROM {term_data} t WHERE vid = %d', $vocabulary->vid);
+ $result = db_query('SELECT * FROM {taxonomy_term_data} t WHERE vid = :vid', array(':vid' => $vocabulary->vid));
$terms = $messages = array();
- while ($term = db_fetch_object($result)) {
+ foreach($result as $term) {
if ($term->trid && $term->language) {
$terms[$term->trid][$term->language] = $term;
}
@@ -167,16 +229,18 @@ function i18n_taxonomy_translation_overview($vid) {
$thisrow[] = '--';
}
}
- $thisrow[] = l(t('edit'), "admin/content/taxonomy/$vid/translation/edit/$trid");
+ $thisrow[] = l(t('edit'), "admin/structure/taxonomy/$vocabulary->machine_name/translation/edit/$trid");
$rows[] = $thisrow;
}
if ($rows) {
- $output .= theme('table', $header, $rows);
+ $build['translations'] = array(
+ '#theme' => 'table',
+ '#header' => $header,
+ '#rows' => $rows
+ );
}
else {
- $messages[] = t('No translations defined for this vocabulary.');
+ $build['message']['#markup'] = t('No translations defined for this vocabulary.');
}
- $messages[]= l(t('Create new translation'), "admin/content/taxonomy/$vid/translation/edit/new");
- $output .= theme('item_list', $messages);
- return $output;
+ return $build;
}
diff --git a/i18n_taxonomy/i18n_taxonomy.install b/i18n_taxonomy/i18n_taxonomy.install
index 0d18039..2582c66 100644
--- a/i18n_taxonomy/i18n_taxonomy.install
+++ b/i18n_taxonomy/i18n_taxonomy.install
@@ -17,8 +17,10 @@ function i18n_taxonomy_install() {
i18n_install_create_fields('taxonomy_term_data', array('language', 'trid'));
// 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'");
- // Set vocabulary mode
- i18n_taxonomy_update_7000();
+ // Set vocabulary mode if updating from D6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_taxonomy_update_7000();
+ }
}
/**
@@ -36,9 +38,9 @@ function i18n_taxonomy_uninstall() {
* Implements hook_schema_alter().
*/
function i18n_taxonomy_schema_alter(&$schema) {
- $schema['taxonomy_vocabulary']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '');
+ $schema['taxonomy_vocabulary']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
$schema['taxonomy_vocabulary']['fields']['i18n_mode'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
- $schema['taxonomy_term_data']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '');
+ $schema['taxonomy_term_data']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
$schema['taxonomy_term_data']['fields']['trid'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
}
diff --git a/i18n_taxonomy/i18n_taxonomy.module b/i18n_taxonomy/i18n_taxonomy.module
index 93f0525..cd5ef3a 100644
--- a/i18n_taxonomy/i18n_taxonomy.module
+++ b/i18n_taxonomy/i18n_taxonomy.module
@@ -70,16 +70,44 @@ function _i18n_taxonomy_vocabulary_options() {
* Implements hook_menu().
*/
function i18n_taxonomy_menu() {
- $items['admin/content/taxonomy/%taxonomy_vocabulary/translation'] = array(
+ $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/translation'] = array(
'title' => 'Translation',
- 'page callback' => 'i18n_taxonomy_page_vocabulary',
- 'page arguments' => array(3, 5, 6),
+ 'page callback' => 'i18n_taxonomy_translation_overview',
+ 'page arguments' => array(3),
'access callback' => '_i18n_taxonomy_translation_tab',
'access arguments' => array(3),
'type' => MENU_LOCAL_TASK,
- 'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+ //'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
'file' => 'i18n_taxonomy.admin.inc',
);
+ $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/translation/add'] = array(
+ 'title' => 'Create new translation',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_taxonomy_translation_term_form', 3),
+ 'access callback' => '_i18n_taxonomy_translation_tab',
+ 'access arguments' => array(3),
+ 'type' => MENU_LOCAL_ACTION,
+ //'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+ 'file' => 'i18n_taxonomy.admin.inc',
+ );
+ $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/translation/edit/%i18n_taxonomy_translation'] = array(
+ 'title' => 'Edit translation',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_taxonomy_translation_term_form', 3, 6),
+ 'access callback' => '_i18n_taxonomy_translation_tab',
+ 'access arguments' => array(3),
+ 'type' => MENU_CALLBACK,
+ //'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+ 'file' => 'i18n_taxonomy.admin.inc',
+ );
+ $items['i18n/taxonomy/autocomplete/%taxonomy_vocabulary_machine_name'] = array(
+ 'title' => 'Autocomplete taxonomy',
+ 'page callback' => 'i18n_taxonomy_autocomplete',
+ 'page arguments' => array(3),
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'i18n_taxonomy.pages.inc',
+ );
return $items;
}
@@ -107,7 +135,7 @@ function i18n_taxonomy_menu_alter(&$items) {
* Menu access callback. Show tab only for full multilingual vocabularies.
*/
function _i18n_taxonomy_translation_tab($vocabulary) {
- return i18n_taxonomy_vocabulary_mode($vocabulary->vid) & I18N_MODE_TRANSLATE;
+ return user_access('administer taxonomy') && i18n_taxonomy_vocabulary_mode($vocabulary->vid) & I18N_MODE_TRANSLATE;
}
/**
@@ -132,7 +160,7 @@ function i18n_taxonomy_i18n_string_refresh($group) {
foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
if (empty($vocabulary->language)) {
i18n_string_update("taxonomy:vocabulary:$vid:name", $vocabulary->name);
- if ($vocabulary->help) {
+ if (!empty($vocabulary->help)) {
i18n_string_update("taxonomy:vocabulary:$vid:description", $vocabulary->help);
}
}
@@ -150,18 +178,18 @@ function i18n_taxonomy_i18n_string_refresh($group) {
}
/**
- * Implements hook_alter_translation_link().
- *
+ * Implements hook_language_switch_links_alter().
+ *
* Replaces links with pointers to translated versions of the content.
*/
-function i18n_taxonomy_translation_link_alter(&$links, $path) {
+function i18n_taxonomy_language_switch_links_alter(array &$links, $type, $path) {
if (preg_match("/^(taxonomy\/term\/)([^\/]*)(.*)$/", $path, $matches)) { //or at a taxonomy-listing?
foreach ($links as $langcode => $link) {
if ($str_tids = i18n_taxonomy_translation_tids($matches[2], $langcode)) {
$links[$langcode]['href'] = "taxonomy/term/$str_tids". $matches[3];
}
}
- }
+ }
}
/**
@@ -292,7 +320,7 @@ function i18n_taxonomy_taxonomy_vocabulary_udpate($vocabulary) {
$update['language'] = $vocabulary->language;
break;
case I18N_MODE_NONE:
- $update['language'] = '';
+ $update['language'] = LANGUAGE_NONE;
break;
}
if (isset($update)) {
@@ -303,7 +331,7 @@ function i18n_taxonomy_taxonomy_vocabulary_udpate($vocabulary) {
drupal_set_message(t('Reset language for all terms.'));
}
// Update strings, always add translation if no language
- if (empty($vocabulary->language)) {
+ if (!i18n_object_langcode($vocabulary)) {
i18n_string_update(array('taxonomy', 'vocabulary', $vocabulary->vid), array(
'name' => $vocabulary->name,
'description' => $vocabulary->description
@@ -400,7 +428,7 @@ function i18n_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) {
'#type' => 'value',
'#value' => $vocabulary->language
);
- $form['identification']['language_info'] = array('#value' => t('All terms in this vocabulary have a fixed language: %language', array('%language' => i18n_language_property($vocabulary->language, 'name'))));
+ $form['identification']['language_info'] = array('#value' => t('All terms in this vocabulary have a fixed language: %language', array('%language' => i18n_language_name($vocabulary->language))));
break;
case I18N_MODE_LOCALIZE:
@@ -434,8 +462,8 @@ function i18n_taxonomy_form_alter(&$form, $form_state, $form_id) {
case 'taxonomy_overview_vocabularies':
$vocabularies = taxonomy_get_vocabularies();
foreach ($vocabularies as $vocabulary) {
- if ($vocabulary->language) {
- $form[$vocabulary->vid]['types']['#value'] .= '&nbsp;('. i18n_language_name($vocabulary->language) .')';
+ if (i18n_object_langcode($vocabulary)) {
+ $form[$vocabulary->vid]['name']['#markup'] .= '&nbsp;('. i18n_language_name($vocabulary->language) .')';
}
}
break;
@@ -443,8 +471,8 @@ function i18n_taxonomy_form_alter(&$form, $form_state, $form_id) {
case 'taxonomy_overview_terms':
if (i18n_taxonomy_vocabulary_mode($form['#vocabulary']->vid) & I18N_MODE_TRANSLATE) {
foreach (element_children($form) as $key) {
- if (isset($form[$key]['#term']) && ($lang = $form[$key]['#term']->language)) {
- $form[$key]['view']['#value'] .= '&nbsp;('. i18n_language_name($lang) .')';
+ if (isset($form[$key]['#term']) && ($lang = i18n_object_langcode($form[$key]['#term']))) {
+ $form[$key]['view']['#suffix'] = ' ('. i18n_language_name($lang) .')';
}
}
}
@@ -465,8 +493,11 @@ function i18n_taxonomy_form_alter(&$form, $form_state, $form_id) {
}
}
+/**
+ * Validate multilingual options for vocabulary form
+ */
function i18n_taxonomy_form_vocabulary_validate($form, &$form_state) {
- $language = !empty($form_state['values']['language']) ? $form_state['values']['language'] : '';
+ $language = i18n_object_langcode($form_state['values']['language']);
$mode = $form_state['values']['i18n_mode'];
if ($mode != I18N_MODE_LANGUAGE && $language) {
form_set_error('language', t('Setting a vocabulary language only makes sense in the "Set language to vocabulary" translation mode. Either change to this mode or do not select a language.'));
@@ -650,17 +681,20 @@ function i18n_taxonomy_term_get_translations($params, $getall = TRUE) {
}
/**
- * Like nat_get_terms() but without caching.
+ * Load translation set
*/
-function i18n_taxonomy_nat_get_terms($nid) {
- $return = array();
-
- $result = db_query("SELECT td.* FROM {nat} n INNER JOIN {term_data} td USING (tid) WHERE n.nid = %d", $nid);
- while ($term = db_fetch_object($result)) {
- $return[$term->tid] = $term;
+function i18n_taxonomy_translation_load($trid) {
+ $translations = db_select('taxonomy_term_data', 't')
+ ->fields('t')
+ ->condition('trid', $trid)
+ ->execute()
+ ->fetchAllAssoc('language');
+ if ($translations) {
+ return array(
+ 'trid' => $trid,
+ 'translations' => $translations,
+ );
}
-
- return $return;
}
/**
@@ -880,39 +914,96 @@ function i18n_taxonomy_vocabulary_get_terms($vid, $lang, $status = 'all') {
* @param $parent
* Parent term id for the tree
*/
-function i18n_taxonomy_get_tree($vid, $lang, $parent = 0, $depth = -1, $max_depth = NULL) {
- static $children, $parents, $terms;
-
- $depth++;
+function i18n_taxonomy_get_tree($vid, $langcode, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
+ $children = &drupal_static(__FUNCTION__, array());
+ $parents = &drupal_static(__FUNCTION__ . ':parents', array());
+ $terms = &drupal_static(__FUNCTION__ . ':terms', array());
// We cache trees, so it's not CPU-intensive to call get_tree() on a term
// and its children, too.
- if (!isset($children[$vid][$lang])) {
- $children[$vid][$lang] = array();
-
- $result = db_query(db_rewrite_sql("SELECT t.tid, t.*, parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d AND t.language = '%s' ORDER BY weight, name", 't', 'tid'), $vid, $lang);
- while ($term = db_fetch_object($result)) {
- $children[$vid][$lang][$term->parent][] = $term->tid;
- $parents[$vid][$lang][$term->tid][] = $term->parent;
- $terms[$vid][$term->tid] = $term;
+ if (!isset($children[$vid][$langcode])) {
+ $children[$vid][$langcode] = array();
+ $parents[$vid][$langcode] = array();
+ $terms[$vid][$langcode] = array();
+
+ $query = db_select('taxonomy_term_data', 't');
+ $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+ $result = $query
+ ->addTag('translatable')
+ ->addTag('term_access')
+ ->fields('t')
+ ->fields('h', array('parent'))
+ ->condition('t.vid', $vid)
+ ->condition('t.language', $langcode)
+ ->orderBy('t.weight')
+ ->orderBy('t.name')
+ ->execute();
+
+ foreach ($result as $term) {
+ $children[$vid][$langcode][$term->parent][] = $term->tid;
+ $parents[$vid][$langcode][$term->tid][] = $term->parent;
+ $terms[$vid][$langcode][$term->tid] = $term;
}
}
- $max_depth = (is_null($max_depth)) ? count($children[$vid][$lang]) : $max_depth;
+ // Load full entities, if necessary. The entity controller statically
+ // caches the results.
+ if ($load_entities) {
+ $term_entities = taxonomy_term_load_multiple(array_keys($terms[$vid][$langcode]));
+ }
+
+ $max_depth = (!isset($max_depth)) ? count($children[$vid][$langcode]) : $max_depth;
$tree = array();
- if (!empty($children[$vid][$lang][$parent])) {
- foreach ($children[$vid][$lang][$parent] as $child) {
- if ($max_depth > $depth) {
- $term = drupal_clone($terms[$vid][$child]);
+
+ // Keeps track of the parents we have to process, the last entry is used
+ // for the next processing step.
+ $process_parents = array();
+ $process_parents[] = $parent;
+
+ // Loops over the parent terms and adds its children to the tree array.
+ // Uses a loop instead of a recursion, because it's more efficient.
+ while (count($process_parents)) {
+ $parent = array_pop($process_parents);
+ // The number of parents determines the current depth.
+ $depth = count($process_parents);
+ if ($max_depth > $depth && !empty($children[$vid][$langcode][$parent])) {
+ $has_children = FALSE;
+ $child = current($children[$vid][$langcode][$parent]);
+ do {
+ if (empty($child)) {
+ break;
+ }
+ $term = $load_entities ? $term_entities[$child] : $terms[$vid][$langcode][$child];
+ if (count($parents[$vid][$langcode][$term->tid]) > 1) {
+ // We have a term with multi parents here. Clone the term,
+ // so that the depth attribute remains correct.
+ $term = clone $term;
+ }
$term->depth = $depth;
- // The "parent" attribute is not useful, as it would show one parent only.
unset($term->parent);
- $term->parents = $parents[$vid][$lang][$child];
+ $term->parents = $parents[$vid][$langcode][$term->tid];
$tree[] = $term;
-
- if (!empty($children[$vid][$lang][$child])) {
- $tree = array_merge($tree, i18n_taxonomy_get_tree($vid, $lang, $child, $depth, $max_depth));
+ if (!empty($children[$vid][$term->tid])) {
+ $has_children = TRUE;
+
+ // We have to continue with this parent later.
+ $process_parents[] = $parent;
+ // Use the current term as parent for the next iteration.
+ $process_parents[] = $term->tid;
+
+ // Reset pointers for child lists because we step in there more often
+ // with multi parents.
+ reset($children[$vid][$langcode][$term->tid]);
+ // Move pointer so that we get the correct term the next time.
+ next($children[$vid][$langcode][$parent]);
+ break;
}
+ } while ($child = next($children[$vid][$langcode][$parent]));
+
+ if (!$has_children) {
+ // We processed all terms in this hierarchy-level, reset pointer
+ // so that this function works the next time it gets called.
+ reset($children[$vid][$langcode][$parent]);
}
}
}
diff --git a/i18n_taxonomy/i18n_taxonomy.pages.inc b/i18n_taxonomy/i18n_taxonomy.pages.inc
index 6655928..5491ab8 100644
--- a/i18n_taxonomy/i18n_taxonomy.pages.inc
+++ b/i18n_taxonomy/i18n_taxonomy.pages.inc
@@ -102,29 +102,24 @@ function theme_i18n_taxonomy_term_page($tids, $result) {
/**
* Helper function for autocompletion
*/
-function i18n_taxonomy_autocomplete($field_name, $tags_typed = '') {
- $field = field_info_field($field_name);
+function i18n_taxonomy_autocomplete($vocabulary, $langcode, $tags_typed = '') {
// 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));
$matches = array();
- if ($tag_last != '') {
+ if ($vocabulary && $langcode && $tag_last != '') {
// Part of the criteria for the query come from the field's own settings.
- $vids = array();
- $vocabularies = taxonomy_vocabulary_get_names();
- foreach ($field['settings']['allowed_values'] as $tree) {
- $vids[] = $vocabularies[$tree['vocabulary']]->vid;
- }
+ $vids = array($vocabulary->vid);
$query = db_select('taxonomy_term_data', 't');
$query->addTag('translatable');
$query->addTag('term_access');
// Add language condition
- $query->condition('t.language', array(i18n_langcode(), ''));
+ $query->condition('t.language', i18n_langcode($langcode));
// Do not select already entered terms.
if (!empty($tags_typed)) {
diff --git a/i18n_variable/i18n_variable.admin.inc b/i18n_variable/i18n_variable.admin.inc
new file mode 100644
index 0000000..9dd2148
--- /dev/null
+++ b/i18n_variable/i18n_variable.admin.inc
@@ -0,0 +1,54 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Administration pages for multilingual variables. Internationalization (i18n) package.
+ */
+
+/**
+ * Select multilingual variables
+ *
+ * There are two lists of variables here.
+ * - i18n_variable_conf, list with high level variable names, some of which are multiple variables
+ * - i18n_variable_list, translated list into actual variable names
+ */
+function i18n_variable_admin_settings($form, $form_state) {
+ $translatable = array();
+ $current = variable_get('i18n_variable_conf', array());
+ foreach (variable_get_info() as $name => $variable) {
+ if (!empty($variable['localize'])) {
+ $translatable[] = $name;
+ }
+ }
+ // The final list will be the sum of both lists. We may have unknown variables we want to preserve.
+ $list = array_unique(array_merge($translatable, $current));
+ $list[] = 'unknown_variable';
+ $form['variables'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Select variables to be translated'),
+ '#theme' => 'variable_table_select',
+ '#tree' => TRUE,
+ );
+ foreach ($list as $name) {
+ $form['variables'][$name] = array(
+ '#type' => 'checkbox',
+ '#default_value' => in_array($name, $current),
+ );
+ }
+ $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
+ return $form;
+}
+
+/**
+ * Handle form submission.
+ */
+function i18n_variable_admin_settings_submit($form, $form_state) {
+ // Get main variable names
+ $variables = $form_state['values']['variables'];
+ $variables = array_keys(array_filter($variables));
+ variable_set('i18n_variable_conf', $variables);
+ // Spawn multiple variables and translate into actual variables
+ variable_set('i18n_variable_list', variable_children($variables));
+ // Clear list from cache
+ cache_clear_all('*', I18N_VARIABLE_CACHE, TRUE);
+} \ No newline at end of file
diff --git a/i18n_variable/i18n_variable.info b/i18n_variable/i18n_variable.info
index e484680..e75ce10 100644
--- a/i18n_variable/i18n_variable.info
+++ b/i18n_variable/i18n_variable.info
@@ -1,7 +1,7 @@
; $Id$
name = Multilingual variables
-description = API module for multilingual variables
-dependencies[] = locale
+description = Multilingual variables that switch language depending on page language.
dependencies[] = i18n
+dependencies[] = variable
package = Multilanguage
core = 7.x
diff --git a/i18n_variable/i18n_variable.install b/i18n_variable/i18n_variable.install
index f559089..d2c59b8 100644
--- a/i18n_variable/i18n_variable.install
+++ b/i18n_variable/i18n_variable.install
@@ -42,5 +42,17 @@ function i18n_variable_schema() {
function i18n_variable_install() {
// Set module weight for it to run after core modules
db_query("UPDATE {system} SET weight = -1000 WHERE name = 'i18n_variable' AND type = 'module'");
+ // Update from d6, module changed name
+ if (variable_get('i18n_drupal6_update')) {
+ i18n_variable_update_7000();
+ }
}
+/**
+ * Update multilingual variables variable name
+ */
+function i18n_variable_update_7000() {
+ variable_set('i18n_variable_list', variable_get('i18n_variables', array()));
+ variable_set('i18n_variable_conf', variable_get('i18n_variables', array()));
+ variable_del('i18n_variables');
+} \ No newline at end of file
diff --git a/i18n_variable/i18n_variable.module b/i18n_variable/i18n_variable.module
index 6aa2165..a72c2f3 100644
--- a/i18n_variable/i18n_variable.module
+++ b/i18n_variable/i18n_variable.module
@@ -1,6 +1,5 @@
<?php
// $Id$
-
/**
* @file
* Internationalization (i18n) package. Multilingual variables API.
@@ -19,6 +18,22 @@ function i18n_variable_boot() {
}
/**
+ * Implements hook_menu()
+ */
+function i18n_variable_menu() {
+ $items['admin/config/regional/i18n/variable'] = array(
+ 'title' => 'Variables',
+ 'description' => 'Configure multilingual variables.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_variable_admin_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n_variable.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ );
+ return $items;
+}
+
+/**
* Implements hook_language_init()
*/
function i18n_variable_language_init() {
@@ -85,10 +100,22 @@ function i18n_variable_global($name = NULL, $default = NULL) {
*
* This is the place to add language fields to all forms.
*/
-function i18n_variable_form_alter(&$form, $form_state, $form_id) {
+function i18n_variable_form_alter(&$form, &$form_state, $form_id) {
// Multilingual variables in settings form.
if (isset($form['#theme']) && $form['#theme'] == 'system_settings_form' && $variables = i18n_variable_list()) {
- if ($i18n_variables = i18n_variable_form_alter_settings($form, $variables)) {
+ if ($form_id == 'system_theme_settings') {
+ // Special treatment for theme settings
+ if (!empty($form['var']) && !empty($form['var']['#value']) && in_array($form['var']['#value'], $variables)) {
+ $variables = element_children($form['theme_settings']);
+ $variables = array_merge($variables, array('default_logo', 'logo_path', 'default_favicon', 'favicon_path'));
+ i18n_variable_form_alter_settings($form, $variables);
+ // Replace variable (theme) name so we use a temporary storage variable
+ $form['#i18n_variable'] = $form['var']['#value'];
+ $form['var']['#value'] = 'i18n_variable_theme_settings';
+ $form['#submit'][] = 'i18n_variable_theme_form_submit';
+ }
+ }
+ elseif ($i18n_variables = i18n_variable_form_alter_settings($form, $variables)) {
array_unshift($form['#submit'], 'i18n_variable_form_submit');
$form['#i18n_variables'] = $i18n_variables;
}
@@ -99,7 +126,11 @@ function i18n_variable_form_alter(&$form, $form_state, $form_id) {
* Get list of multilingual variables or check whether a variable is multilingual
*/
function i18n_variable_list($name = NULL) {
- $variables = variable_get('i18n_variables', array());
+ static $variables;
+ if (!isset($variables)) {
+ // Note that variables being a global static, any other module can initialize or alter it.
+ $variables = &drupal_static(__FUNCTION__, variable_get('i18n_variable_list', array()));
+ }
return $name ? in_array($name, $variables) : $variables;
}
@@ -109,6 +140,7 @@ function i18n_variable_list($name = NULL) {
function i18n_variable_form_alter_settings(&$form, $variables) {
$result = array();
foreach (element_children($form) as $field) {
+ $output = '';
if (count(element_children($form[$field])) && empty($form[$field]['#tree'])) {
$result += i18n_variable_form_alter_settings($form[$field], $variables);
}
@@ -116,7 +148,17 @@ function i18n_variable_form_alter_settings(&$form, $variables) {
// Add form field class (i18n-variable) and description text.
$form[$field]['#attributes']['class'][] = 'i18n-variable';
$form[$field]['#description'] = !empty($form[$field]['#description']) ? $form[$field]['#description'] : '';
- $form[$field]['#description'] .= ' <strong>'. t('This is a multilingual variable.') .'</strong>';
+ $path = $_GET['q'];
+ $output .= t('Change this variable for other language:') . ' ';
+ foreach (i18n_language_list() as $l => $lang) {
+ if (i18n_langcode() == $l) {
+ $output .= $lang . ' ';
+ }
+ else {
+ $output .= l($lang, $path, array('language' => i18n_language($l))) . ' ';
+ }
+ }
+ $form[$field]['#description'] .= ' <strong>' . t('This is a multilingual variable.') . '</strong> ' . $output;
// Addd field => name to result
$result[$field] = !empty($form[$field]['#title']) ? $form[$field]['#title'] : $field;
}
@@ -134,7 +176,7 @@ function i18n_variable_form_submit($form, &$form_state) {
foreach ($variables as $name) {
if (isset($form_state['values'][$name])) {
if ($op == t('Reset to defaults')) {
- i18n_variable_del($name, $language);
+ i18n_variable_del($name, $language->language);
}
else {
$value = $form_state['values'][$name];
@@ -143,8 +185,8 @@ function i18n_variable_form_submit($form, &$form_state) {
}
i18n_variable_set($name, $value, $language->language);
}
- // If current is default language, we allow global (without language) variables to be set too
- if ($language->language === language_default('language')) {
+ // If current is not default language, we don't set any global variable (without language)
+ if ($language->language != language_default('language')) {
unset($form_state['values'][$name]);
}
}
@@ -153,6 +195,23 @@ function i18n_variable_form_submit($form, &$form_state) {
}
/**
+ * Save multilingual variables and remove them from form.
+ *
+ * Note the theme variable has already been set into 'i18n_variable_theme_settings'
+ */
+function i18n_variable_theme_form_submit($form, &$form_state) {
+ $language = i18n_variable_language();
+ $settings = variable_get('i18n_variable_theme_settings');
+ $name = $form['#i18n_variable'];
+ i18n_variable_set($name, $settings, $language->language);
+ // If current is default language, we allow global (without language) variables to be set too
+ if ($language->language == language_default('language')) {
+ variable_set($name, $settings);
+ }
+ variable_del('i18n_variable_theme_settings');
+}
+
+/**
* Set a persistent language dependent variable.
*
* @param $name
@@ -169,7 +228,7 @@ function i18n_variable_set($name, $value, $langcode) {
db_merge('i18n_variable')->key(array('name' => $name, 'language' => $langcode))->fields(array('value' => serialize($value)))->execute();
- cache_clear_all('variables:'. $langcode, I18N_VARIABLE_CACHE);
+ cache_clear_all('variables:' . $langcode, I18N_VARIABLE_CACHE);
$conf[$name] = $value;
$i18n_conf[$langcode][$name] = $value;
@@ -204,10 +263,10 @@ function i18n_variable_del($name, $langcode) {
->condition('language', $langcode)
->execute();
- cache_clear_all('variables:'. $langcode, I18N_VARIABLE_CACHE);
+ cache_clear_all('variables:' . $langcode, I18N_VARIABLE_CACHE);
unset($conf[$name]);
- unset($i18n_conf[$name]);
+ unset($i18n_conf[$langcode][$name]);
}
/**
@@ -216,7 +275,7 @@ function i18n_variable_del($name, $langcode) {
function i18n_variable_load($langcode) {
$i18n_conf = &drupal_static('i18n_variable');
if (!isset($i18n_conf[$langcode])) {
- $cacheid = 'variables:'. $langcode;
+ $cacheid = 'variables:' . $langcode;
if ($cached = cache_get($cacheid, I18N_VARIABLE_CACHE)) {
$i18n_conf[$langcode] = $cached->data;
}
@@ -227,7 +286,9 @@ function i18n_variable_load($langcode) {
->execute();
$i18n_conf[$langcode] = array();
foreach ($result as $variable) {
- $i18n_conf[$langcode][$variable->name] = unserialize($variable->value);
+ if (i18n_variable_list($variable->name)) {
+ $i18n_conf[$langcode][$variable->name] = unserialize($variable->value);
+ }
}
cache_set($cacheid, $i18n_conf[$langcode], I18N_VARIABLE_CACHE);
}
@@ -254,7 +315,7 @@ function _i18n_variable_exit() {
}
}
if ($refresh) {
- cache_set('variables:'. $langcode, $i18n_conf[$langcode], I18N_VARIABLE_CACHE);
+ cache_set('variables:' . $langcode, $i18n_conf[$langcode], I18N_VARIABLE_CACHE);
}
}
}
diff --git a/i18n_variable/i18n_variable.variable.inc b/i18n_variable/i18n_variable.variable.inc
new file mode 100644
index 0000000..0b31292
--- /dev/null
+++ b/i18n_variable/i18n_variable.variable.inc
@@ -0,0 +1,22 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_info()
+ */
+function i18n_variable_variable_info($options = array()) {
+ $variables['i18n_variable_conf'] = array(
+ 'title' => t('Multilingual variables, main variable names.', array(), $options),
+ 'type' => 'array',
+ 'group' => 'i18n',
+ );
+ $variables['i18n_variable_list'] = array(
+ 'title' => t('Multilingual variables, real variable names.', array(), $options),
+ 'type' => 'array',
+ 'group' => 'i18n',
+ );
+ return $variables;
+} \ No newline at end of file