Index: views_ui.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/views_ui.module,v retrieving revision 1.108 diff -u -p -r1.108 views_ui.module --- views_ui.module 3 Dec 2008 02:39:45 -0000 1.108 +++ views_ui.module 20 Jan 2009 04:39:21 -0000 @@ -233,6 +233,10 @@ function views_ui_cache_load($name) { // Check to see if someone else is already editing this view. global $user; $view->locked = db_fetch_object(db_query("SELECT s.uid, v.updated FROM {views_object_cache} v INNER JOIN {sessions} s ON v.sid = s.sid WHERE s.sid != '%s' and v.name = '%s' and v.obj = 'view' ORDER BY v.updated ASC", session_id(), $view->name)); + // Set a flag to indicate that this view is being edited. + // This flag will be used e.g. to determine whether strings + // should be localized. + $view->editing = TRUE; } } Index: includes/admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/admin.inc,v retrieving revision 1.150 diff -u -p -r1.150 admin.inc --- includes/admin.inc 7 Jan 2009 21:52:00 -0000 1.150 +++ includes/admin.inc 20 Jan 2009 04:39:28 -0000 @@ -2725,6 +2725,14 @@ function views_ui_admin_tools() { '#default_value' => variable_get('views_no_javascript', FALSE), ); + $form['views_localization_plugin'] = array( + '#type' => 'radios', + '#title' => t('Localization plugin'), + '#options' => views_fetch_plugin_names('localization', NULL, array(), TRUE), + '#default_value' => variable_get('views_localization_plugin', 'core'), + '#description' => t('Select a plugin for translation of Views data like header, footer, and empty text.'), + ); + $regions = system_region_list(variable_get('theme_default', 'garland')); $form['views_devel_region'] = array( @@ -2959,11 +2967,13 @@ function views_fetch_fields($base, $type * 'summary', 'feed' or others based on the neds of the display. * @param $base * An array of possible base tables. + * @param $with_help + * If true, add help to the names. * * @return * A keyed array of in the form of 'base_table' => 'Description'. */ -function views_fetch_plugin_names($type, $key = NULL, $base = array()) { +function views_fetch_plugin_names($type, $key = NULL, $base = array(), $with_help = FALSE) { $data = views_fetch_plugin_data(); $plugins[$type] = array(); @@ -2974,7 +2984,7 @@ function views_fetch_plugin_names($type, continue; } if (empty($plugin['no ui']) && (empty($base) || empty($plugin['base']) || array_intersect($base, $plugin['base']))) { - $plugins[$type][$id] = $plugin['title']; + $plugins[$type][$id] = $plugin['title'] . ($with_help && $plugin['help'] ? ': ' . $plugin['help'] : ''); } } Index: includes/base.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/base.inc,v retrieving revision 1.2 diff -u -p -r1.2 base.inc --- includes/base.inc 6 Jun 2008 19:29:03 -0000 1.2 +++ includes/base.inc 20 Jan 2009 04:39:28 -0000 @@ -76,7 +76,7 @@ class views_object { * Unpack options over our existing defaults, drilling down into arrays * so that defaults don't get totally blown away. */ - function unpack_options(&$storage, $options, $definition = NULL) { + function unpack_options(&$storage, $options, $definition = NULL, $localization_keys = array()) { if (!is_array($options)) { return; } @@ -93,8 +93,19 @@ class views_object { $this->unpack_options($storage[$key], $value, isset($definition[$key]) ? $definition[$key] : array()); } - else if (!empty($definition[$key]['translatable']) && !empty($value)) { - $storage[$key] = t($value); + // Don't localize strings during editing. When editing, we need to work with + // the original data, not the translated version. + else if (!$this->view->editing && !empty($definition[$key]['translatable']) && !empty($value)) { + // If the view is normal or overridden, use admin string translation. + if (isset($this->view->type) && in_array($this->view->type, array('Normal', 'Overridden'))) { + // The $keys array is built from the view name, any localization keys + // sent in, and the name of the property being processed. + $storage[$key] = $this->view->localization_plugin->translate($value, array_merge(array($this->view->name), $localization_keys, array($key))); + } + // Otherwise, use t(). + else { + $storage[$key] = t($value); + } } else { $storage[$key] = $value; Index: includes/plugins.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/plugins.inc,v retrieving revision 1.152 diff -u -p -r1.152 plugins.inc --- includes/plugins.inc 7 Jan 2009 23:31:12 -0000 1.152 +++ includes/plugins.inc 20 Jan 2009 04:39:29 -0000 @@ -11,7 +11,7 @@ */ function views_views_plugins() { $path = drupal_get_path('module', 'views') . '/js'; - return array( + $plugins = array( 'module' => 'views', // This just tells our themes are elsewhere. 'display' => array( 'parent' => array( @@ -235,7 +235,31 @@ function views_views_plugins() { 'help topic' => 'access-perm', ), ), + 'localization' => array( + 'parent' => array( + 'no ui' => TRUE, + 'handler' => 'views_plugin_localization', + 'parent' => '', + ), + 'none' => array( + 'title' => t('None'), + 'help' => t('Do not pass admin strings for translation.'), + 'handler' => 'views_plugin_localization_none', + 'help topic' => 'localization-none', + ), + 'core' => array( + 'title' => t('Core'), + 'help' => t("Use Drupal core t() function. Not recommended, as it doesn't support updates to existing strings."), + 'handler' => 'views_plugin_localization_core', + 'help topic' => 'localization-core', + ), + ), ); + // Add a help message pointing to the i18nstrings module if it is not present. + if (!module_exists('i18nviews')) { + $plugins['localization']['core']['help'] .= ' ' . t('If you need to translate Views labels into other languages, consider installing the Internationalization package\'s Views translation module.', array('!path' => url('http://drupal.org/project/i18n', array('absolute' => TRUE)))); + } + return $plugins; } /** Index: includes/view.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/view.inc,v retrieving revision 1.146 diff -u -p -r1.146 view.inc --- includes/view.inc 8 Jan 2009 00:29:54 -0000 1.146 +++ includes/view.inc 20 Jan 2009 04:39:33 -0000 @@ -23,6 +23,7 @@ class view extends views_db_object { // State variables var $built = FALSE; var $executed = FALSE; + var $editing = FALSE; var $args = array(); var $build_info = array(); @@ -59,6 +60,9 @@ class view extends views_db_object { } $this->query = new stdClass(); + + // Initialize localization. + $this->init_localization(); } /** @@ -1303,6 +1307,9 @@ class view extends views_db_object { $this->_save_rows($key); } + // Save data for translation. + $this->save_locale_strings(); + cache_clear_all('views_urls', 'cache_views'); cache_clear_all(); // clear the page cache as well. } @@ -1328,6 +1335,8 @@ class view extends views_db_object { return; } + $this->delete_locale_strings(); + db_query("DELETE FROM {views_view} WHERE vid = %d", $this->vid); // Delete from all of our subtables as well. foreach ($this->db_objects() as $key) { @@ -1504,6 +1513,93 @@ class view extends views_db_object { return $errors ? $errors : TRUE; } + + /** + * Find and initialize the localizer plugin. + */ + function init_localization() { + if (isset($this->localization_plugin)) { + return is_object($this->localization_plugin); + } + + $this->localization_plugin = views_get_plugin('localization', variable_get('views_localization_plugin', 'core')); + + if (empty($this->localization_plugin)) { + return FALSE; + } + + return TRUE; + } + + /** + * Send strings for localization. + */ + function save_locale_strings() { + $this->process_locale_strings('save'); + } + + /** + * Delete localized strings. + */ + function delete_locale_strings() { + $this->process_locale_strings('delete'); + } + + /** + * Process strings for localization or deletion. + */ + function process_locale_strings($op) { + if ($this->display && is_array($this->display)) { + foreach ($this->display as $display_id => $display) { + $translatable = array(); + // Special handling for display title. + if (isset($display->display_title)) { + $translatable['display_title'] = $display->display_title; + } + $this->unpack_translatable($translatable, $display_id, $display->display_options); + foreach ($translatable as $property => $string) { + switch ($op) { + case 'delete': + $this->localization_plugin->delete($string, array($this->name, $display_id, $property)); + break; + case 'save': + $this->localization_plugin->save($string, array($this->name, $display_id, $property)); + break; + } + } + } + } + } + + /** + * Unpack translatable properties and their values. + */ + function unpack_translatable(&$translatable, $display_id, $options, $definition = NULL) { + + if (!is_array($options)) { + return; + } + + // Ensure we have displays with handlers. + $this->init_display(); + + if (!isset($definition)) { + $definition = $this->display[$display_id]->handler->option_definition(); + } + + foreach ($options as $key => $value) { + if (is_array($value)) { + $this->unpack_translatable($translatable, $display_id, $value, isset($definition[$key]) ? $definition[$key] : array()); + } + else if (!empty($definition[$key]['translatable']) && !empty($value)) { + // If the view is normal or overridden, use admin string translation. + // Otherwise t() will handle it. + if (isset($this->type) && in_array($this->type, array('Normal', 'Overridden'))) { + $translatable[$key] = $value; + } + } + } + } } /** Index: plugins/views_plugin_display.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/views_plugin_display.inc,v retrieving revision 1.18 diff -u -p -r1.18 views_plugin_display.inc --- plugins/views_plugin_display.inc 7 Jan 2009 23:31:13 -0000 1.18 +++ plugins/views_plugin_display.inc 20 Jan 2009 04:39:38 -0000 @@ -40,7 +40,9 @@ class views_plugin_display extends views unset($options['defaults']); } - $this->unpack_options($this->options, $options); + // Last argument is an array of keys to be used in identifying + // strings for translation. + $this->unpack_options($this->options, $options, NULL, array($display->id)); } function destroy() { Index: plugins/views_plugin_localization.inc =================================================================== RCS file: plugins/views_plugin_localization.inc diff -N plugins/views_plugin_localization.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/views_plugin_localization.inc 20 Jan 2009 04:39:38 -0000 @@ -0,0 +1,53 @@ +view = &$view; + } + + /** + * Translate a string. + * + * @param $string + * The string to be translated. + * @param $keys + * An array of keys to identify the string. Generally constructed from + * view name, display_id, and a property, e.g., 'header'. + */ + function translate($string, $keys = array()) { } + + /** + * Save a string for translation. + * + * @param $string + * The string to be saved. + * @param $keys + * An array of keys to identify the string. Generally constructed from + * view name, display_id, and a property, e.g., 'header'. + */ + function save($string, $keys = array()) { } + + /** + * Delete a string. + * + * @param $string + * The string to be deleted. + * @param $keys + * An array of keys to identify the string. Generally constructed from + * view name, display_id, and a property, e.g., 'header'. + */ + function delete($string, $keys = array()) { } + +} Index: plugins/views_plugin_localization_core.inc =================================================================== RCS file: plugins/views_plugin_localization_core.inc diff -N plugins/views_plugin_localization_core.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/views_plugin_localization_core.inc 20 Jan 2009 04:39:38 -0000 @@ -0,0 +1,57 @@ +