The Problem
When using localized (translated) terms with views only the default language is ever shown. This includes the display of the terms in both fields and filters. A number of posts have been brought up on this topic but often it is kicked back and forth between being a views issue or being an i18n issue.
Digging deeper into the views module I noticed that all the methods used to get the terms of a particular node or vocabulary rely on direct queries baked right into the views handlers instead of using Drupal api calls. The initial cause of this is that the returned values from the DB are outputted with a simple 'check_plain' instead of being passed through the translation system. Hence why translated taxonomy text doesn't work with Views.
Our Solution
We absolutely needed this fixed, but didn't want to 'hack' views to get it done. So instead I created module which creates views handlers that extend the existing taxonomy handlers and then override the handler to use mine instead. If that sounds a little confusing hopefully it'll make sense as I dive into the process.
Implementation
I created a module called i18ntaxonomy_views which I will go through to outlined the process of overriding the default views handler
i18ntaxonomy_views.module
The only thing in this module is the implementation of hook_views_api which is required when doing anything with views. This method basically tells views what api version we are supporting and where it can find the files it needs
function i18ntaxonomy_views_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'i18ntaxonomy_views'),
);
}
i18ntaxonomy_views.views.inc
The module.views.inc file is responsible for all the major declaration of what your module will be adding to views. This includes the table declaration for the DB queries and handlers for fields, filters and sorts.
In this file I did two things.
- Alter the default handlers specified up by taxonomy.views.inc
- Registered my custom handlers that I'm going to use instead
/**
* Implementation of hook_views_data().
*/
function i18ntaxonomy_views_views_data_alter(&$data) {
// tid field
$data['term_node']['tid']['field']['handler'] = 'views_handler_field_term_node_tid_i18n';
$data['term_node']['tid']['filter']['handler'] = 'views_handler_filter_term_node_tid_i18n';
return $data;
}
/**
* Implementation of hook_views_handlers() to register all of the basic handlers
* views uses.
*/
function i18ntaxonomy_views_views_handlers() {
return array(
'info' => array(
'path' => drupal_get_path('module', 'i18ntaxonomy_views'),
),
'handlers' => array(
'views_handler_field_term_node_tid_i18n' => array(
'parent' => 'views_handler_field_term_node_tid',
),
'views_handler_filter_term_node_tid_i18n' => array(
'parent' => 'views_handler_filter_term_node_tid',
),
),
);
}
i18ntaxonomy_views_views_data_alter is an implementation of hook_views_data_alter which lets me change existing declaration of the views $data object. In this case I am changing what taxonomy.views.inc declared for it's handlers for tid field and tid filter
Then in i18ntaxonomy_views_views_handlers I registered my custom handlers views_handler_field_term_node_tid_i18n and views_handler_filter_term_node_tid_i18n. What is extremely important here is that I specified views_handler_field_term_node_tid and views_handler_filter_term_node_tid respectively as the parents of my hanlders. This is where the Object Oriented nature of development comes into play. I don't want to re-invent the wheel with my handler so instead I extend the existing handler that comes with views.
Basically my handler extends the taxonomy.views.inc handler which extends a base handler that views uses.
views_handler_field_term_node_tid_i18n -> views_handler_field_term_node_tid -> views_handler_field_prerender_list
What this means is that in my handler I only have to focus on the part I want to change as the rest of the stuff is taken from the original taxonomy handler. If you're confused or lost I highly recommend reading the views API documentation available when you install the 'Advanced Help' module, as well as looking through the code for the existing 'modules' within views. Start with taxonomy.views.inc and work your way from there.
Next we create our handlers.
views_handler_field_term_node_tid_i18n.inc
/**
* Field handler for terms.
*/
class views_handler_field_term_node_tid_i18n extends views_handler_field_term_node_tid {
function pre_render($values) {
$this->field_alias = $this->aliases['vid'];
$vids = array();
foreach ($values as $result) {
if (!empty($result->{$this->aliases['vid']})) {
$vids[] = $result->{$this->aliases['vid']};
}
}
if ($vids) {
$voc = '';
if (!empty($this->options['limit']) && !empty($this->options['vids'])) {
$voc = " AND td.vid IN (" . implode(', ', array_keys(array_filter($this->options['vids']))) . ")";
}
$result = db_query("SELECT tn.vid AS node_vid, td.*, v.name as vocabulary FROM {term_data} td INNER JOIN {term_node} tn ON td.tid = tn.tid INNER JOIN {vocabulary} v ON v.vid = td.vid WHERE tn.vid IN (" . implode(', ', $vids) . ")$voc ORDER BY td.weight, td.name");
while ($term = db_fetch_object($result)) {
$this->items[$term->node_vid][$term->tid]['name'] = i18nstrings("taxonomy:term:$term->tid:name", $term->name);
$this->items[$term->node_vid][$term->tid]['tid'] = $term->tid;
$this->items[$term->node_vid][$term->tid]['vid'] = $term->vid;
$this->items[$term->node_vid][$term->tid]['vocabulary'] = check_plain($term->vocabulary);
if (!empty($this->options['link_to_taxonomy'])) {
$this->items[$term->node_vid][$term->tid]['make_link'] = TRUE;
$this->items[$term->node_vid][$term->tid]['path'] = taxonomy_term_path($term);
}
}
}
}
}
This first handler is responsible for displaying taxonomy terms associated with a node. The first thing to note is that again I have to declare the class for the handler views_handler_field_term_node_tid_i18n and what class it extends. In this case views_handler_field_term_node_tid. If you look at the original views_handler_field_term_node_tid.inc file there are a lot of functions in that file, but in ours I only copied one. This is because we have extended the original class so I only declare the methods I want to either change or create. In this case I focused on changing the pre_render function as that is responsible for getting the taxonomy terms.
I change the line
$this->items[$term->node_vid][$term->tid]['name'] = check_plain($term->name);
to this
$this->items[$term->node_vid][$term->tid]['name'] = i18nstrings("taxonomy:term:$term->tid:name", $term->name);
I won't go into the other handler since the concept is basically the same. Find the method you want to override and make changes to it. It may seem like a lot of work, but really it's a lot of copy and paste work with some minor modifications.
The code for this module is on github for anyone to grab and take a look at. https://github.com/freeform/i18ntaxonomy_views
Currently this is the only solution I could think of that doesn't hack any modules directly. If anyone has any questions or suggestions lets talk about it here.
Comments
Improvement: Term depth filter
Just what I was looking for, thanks ! Only thing missing is the same functionality with term (with depth) filter.
Great
Great work and idea!
I don't seem to get it to work though. I activated it, cleared all caches, refreshed all translation, tried to translate new terms.
No effect on the block where the terms are listed through views.
I use i18n v1.9 and Views v2.12, if that helps somehow.
Best regards
Jan
I have the same problem. Did
I have the same problem.
Did you get it to work?
Views 6.x-2.16
i18n 6.x-1.10
Taxonomy: Localize terms
Very helpful, thank you!
Works like charm for me. (Views 6.x-2.16, Internationalization Views 6.x-2.0, Internationalization 6.x-1.10)
Does work only on the node views, not the term views unfortunately.
Meanwhile, there's an official module for that
Nowadays you can use the i18nviews module.