When the translation module is in use, several new types of views might be useful:
1. A view of a translation set, such that only one node per translation set is displayed. E.g.: Voting is set up so that votes are registered to the "source" translation (the one where nid = tnid). We want a view of top voted upon nodes, with links to the translated version where available and, where not, the source node.
2. A view that aggregates numerical data across a translation set. E.g.: we have a visitor viewing a site in French and we want to present a list of top viewed content. What we want here is a field we can sort on that has the sum of page views per translation set rather than node.
3. With inter-object references, we want to present data for all members
of a translation set. E.g.: we have event signups, which register users to nodes. We want to display all users signed up for event X. When we're viewing
an event, show us signups for all members of this node's translation set.
These are distinct problems and may require distinct solutions, but they have in common the need to expose and work with the node.tnid field.
I'm unsure of the best starting place.
How far can we get by simply adding tnid as a field to the node table?
Or do we need a new group returned by node_views_data(), "Node translation set"? Something vaguely like:
// ----------------------------------------------------------------------
// Node translations if translation module enabled
if (module_exists('translation')) {
// Define the base group of this table. Fields that don't
// have a group defined will go into this field by default.
$data['node_translation_set']['table']['group'] = t('Node translation set');
// Explain how this table joins to others.
$data['node_translation_set']['table']['join'] = array(
// Directly links to node table.
'node' => array(
'table' => 'node',
'left_field' => 'nid',
'field' => 'tnid',
),
);
}
Hints? Suggestions?
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | translation-views-followup.diff | 2.14 KB | nedjo |
| #18 | views-translation.diff | 740 bytes | nedjo |
| #14 | views-translation.diff | 8.7 KB | nedjo |
| #14 | translation.tar_.gz | 2.6 KB | nedjo |
| #4 | views-translation.tar_.gz | 1.41 KB | nedjo |
Comments
Comment #1
merlinofchaos commentedI believe 1) could be achievable through the use of a node to node relationship, where you get the "translation for this node in the selected language" and the relationship has a language selector (that includes "user's current language" as an option, but also all the languages). These would be checkboxes so that you could get all languages as well.
2) is more difficult if for no reason than because Views doesn't intrinsically do group by and that kind of math will require a sum() and a group by. You could write custom fields but they would be useful in a very limited view.
3) may be achievable with the relationship described in #1.
I wouldn't do the automatic joining that you have in your example. I would accomplish this by having translation.views.inc perform a hook_views_data_alter to add the relationship directly to the node table. (And honestly, perhaps all the 'language' stuff should be moved to translation.views.inc anyway, since it's doing a module_exists which is something I always dislike).
Comment #2
nedjoThanks, I'll put together a draft translation.views.inc and post for review.
Comment #3
merlinofchaos commentedAs I think on it, you'll definitely need a field for tnid as you will probably have to find a way to filter so that you can tell the 'primary node' from 'translated node' and I think tnid is the only way to tell.
Comment #4
nedjoHere's an initial roughed in patch. To test: after applying the patch, extract the tarball to views/modules.
Features:
1. Moves language field from node.views.inc to new translation.views.inc.
2. Adds remaining translation-related fields: tnid and translate.
3. For convenience, all fields handled are added to a new group, "Node translation".
4. For tnid, declares a relationship with the source ID. Here I was following what's done with the {comments}.pid field. I vaguely think this relationship might be useful but I don't fully understand yet just how ;)
5. Adds a view_node_translation link field. This will link by title to a translation of the node in the current language if available, and otherwise to the node.
In other words, I've started to sketch in some of the possibilities. Needs review and thinking through at this point.
There's a lot that could be done or tried but not all of it will be commonly needed. We might want to put a minimum in Views core - something not much more than what I've sketched in, though obviously revined - and leave more advanced or particular handling to e.g. a views_translation module.
Comment #5
Alice Heaton commentedThis can be done using a filter ; the Select Translation does just that. You implement your view such that it returns content in any language, the Select Translation filter will then keep only one instance per translation set. It provides several algorithms for choosing the translation you want to see :
Comment #6
merlinofchaos commentedNedjo: That patch looks pretty decent on a quick inspection. Do you have more you want to do with it?
(I want to roll another rc today and am touching some of these issues to see where they are).
Comment #7
nedjo@merlinofchaos: Looking at it again, I think the main thing that would be useful is another relationship on the node.tnid field. What I currently have is a join on node.nid to find the source translation. What we also need, I think, is a relationship to the translation in the current language. However, this would require a where condition in the join: join on node.tnid where node.language = '***CURRENT_LANGUAGE***'.
This looks complicated. I don't see support for where conditions in the views_handler_relationship class.
If this worked, though, then what I sketched in as the 'view_node_translation' field probably wouldn't be necessary, since we could get any of the translation's fields via the relationship.
So I guess we should either:
a. strip out both the relationship and the 'view_node_translation' and work them out later,
b. strip out just the 'view_node_translation' field, on the assumption that the source translation relationship is going to be useful, or
c. complete work on a 'translation in the current language' relationship before applying anything.
I think I lean towards a. It gets some basic improved support in and gives time to work through the rest. Thoughts? Shall I do up a patch sans those two?
Also, I forgot a line assigning the node.translate field to the "Node translation" group. I'll add that in.
@Anselm Heaton: thanks for the link, I'll have a look.
Comment #8
merlinofchaos commentedThat usually requires a custom relationship class that adds a clause to the JOIN. For a good example see the new content_handler_relationship in CCK that got committed a few days ago. That does relationships on nodereferences using delta, so that multiple node references can pick and choose which one they want. (Can do neat tricks there). This would be a very similar thing, the join would look like this:
It's not that hard to do that by getting the original $join that would be used and putting the extra clause in the $join using $join->extra.
So I actually lean towards the relationships.
Comment #9
nedjoNice! That example from CCK is very useful. I'm thinking now that we'll need only one relationship. It'll be a join on node.tnid (not on node.nid as I'd done earlier for finding the source translation).
Here, the options could be:
* all (no where condition, multiple rows)
* source translation (translated_node.nid = translated_node.tnid)
* current language (as above)
* for each enabled language, a version in that language
I'll get working on this, but I'm not likely to finish today.
Comment #10
merlinofchaos commentedIt strikes me that you really want both.
I have a node; and I want to know the source translation.
I have a node and I want to know what it's been translated into in my language.
Comment #11
nedjoYes. I was just thinking that we could do both of these in a single relationship. Or in other words these two are equivalent and would both create a relationship with the source node.
LEFT JOIN node translated_node ON node.tnid = translated_node.tnid AND translated_node.nid = translated_node.tnid
(which could be done as part of the single translated node relationship)
LEFT JOIN node source_node ON node.tnid = source_node.nid
(which would require a separate source translation relationship)
But even if so, it might be clearer and cleaner to do the source translation as a separate field. What do you think?
Comment #12
merlinofchaos commentedWell, Views relationships are unidirectional, you can't just turn them around, so I think you'd really need both.
Comment #13
nedjok, thanks, I'll get coding.
Comment #14
nedjoWell, I got most of the way but I'm stuck on how to modify the join.
merlinofchaos, can you have a quick look and point me in the right direction?
Changes since the last patch:
* Removed the 'view_node_translation' field and handler
* Added a DEFAULT_LANGUAGE query substitute
* Added a join on node table (node.tnid = node_node.tnid) to permit a tnid to tnid relationship
* Added a translation relationship and handler.
This last point is where I'm stuck. In the handler, I'm not finding the right way to modify the join to add a condition. See ensure_my_table in class views_handler_relationship_translation in views_handler_relationship_translation.inc in the attached tarball.
I tried the following:
Here's the relationship definition I'm working with, in case I got something wrong there:
What's the right approach here?
Thanks, Nedjo.
Comment #15
merlinofchaos commentedOk, it took me a minute, but I realized that the problem here is because the relationships are slightly different.
In CCK, the joins look like this:
[node] --> [nodereference] --> [node].
But we're directly joining node to node here.
In the CCK relationship, ensure_my_table is adding the 'nodereference' table, and then query() adds the relationship on top of that. However, in this case, we don't need the intermediate table. That means our join manipulation is part of the relationship itself...in the query() method.
I've fixed this; when playing with the relationships, I also discovered that you had it set to tnid --> tnid, when it should be nid --> tnid (and the converse, tnid --> nid), so I think I fixed that.
I went ahead and committed this, since I want to release rc4 and an imperfect commit in rc4 is better than throwing the patch around. I think, however, that the patch was in pretty good shape and it should be largely usable as is. Please review this and see if there are any errors in it that need to be cleaned up!
Comment #16
nedjoGreat, I'll have a look.
I realized what I'd missed previously is that we for can't simply join on the tnid field, because there are two cases we need to account for. The first is: the node is part of a translation set and has a tnid equal to the nid of the source translation. The second is: the node is not part of a translation set and so has a tnid of 0.
If we simply join on node.tnid, for untranslated nodes we get all other untranslated nodes joining (since they too have tnid values of 0).
To complicate things, we should support the "require this relationship" functionality. So the joins we're looking for would be something like:
1. If we do *not* require the relationship:
SELECT node.nid, node.title, node_node.nid AS node_node_nid, node_node_title AS node_node_title FROM {node} node LEFT JOIN {node}node_node ON node.tnid = node_node.tnid OR node.tnid = 02. If we do require the relationship:
SELECT node.nid, node.title, node_node.nid AS node_node_nid, node_node_title AS node_node_title FROM {node} node INNER JOIN {node}node_node ON node.tnid = node_node.tnid AND node.tnid != 0Looking at the commit, I'll report back.
Comment #17
merlinofchaos commentedI am pretty sure you don't want to relate node.tnid == node.tnid. That's more of a filter.
So perhaps we need to add some filtering capability on tnid to help with that.
Comment #18
nedjoLooks like I put the wrong handler, the relationship on source translation should use the standard views_handler_relationship, patch attached but not tested. I'll test in a minute.
Joining on nid covers this relationship: show me translations of this source translation. That's definitely something we want.
I'm thinking it misses something we may also want, sibling translations. Those are: show me translations of this piece of content (which may itself be a translation, not the source translation).
That's where I'm thinking we might want to join on tnid.
Comment #19
merlinofchaos commentedThe thing is, you're going to get multiple rows, because there are an unspecified number of siblings. That is what makes it a poor choice for a relationship.
Also in theory you can get siblings by going node -> master -> translations though that is probably a bit on the not so efficient side.
Comment #20
nedjoTrue. But we're going to get multiple rows in any case as there are multiple translations per source translation.
We already handle this by including the language field in the join. There are multiple nodes with a tnid of 23, but only one of them is in Spanish.
So by joining on tnid we get everything we get from nid (ability to show translations of a source node) plus sibling-to-sibling joins.
Attached patch includes some small needed fixes:
* Fixes my incorrect handler for the source translation.
* Adds in two bits missed from the original patch.
And:
* Uses tnid as the join field, enabling sibling-to-sibling translation relationships. The only extra this requires is ensuring tnid != 0 in the join.
I've run this through its paces and it seems to work.
But I found some additional areas I left unfinished. There's no views_handler_filtern_tnid, but it's declared in the field definition. I'll try to write that tomorrow.
Comment #21
merlinofchaos commentedHey nedjo, it seems
- 'filter' => array(
- 'handler' => 'views_handler_filter_node_language',
- ),
+ 'filter' => array(
+ 'handler' => 'views_handler_filter_node_language_current',
+ ),
But the rename didn't actually happen. I'm curious what you were planning here, or if this was just leftover. I didn't catch this, unfortunately, so woke up to a long list of "augh broke my site!" posts. =)
Comment #22
nedjoI'm afraid I have no idea what I was thinking there, apologies for the problem.
Comment #23
Anonymous (not verified) commentedAutomatically closed -- issue fixed for two weeks with no activity.