With multilingual content now in core, the question of how to handle votes on nodes in different languages comes to a head.
By default, votes on members of a translation set are handled separately. E.g., if there are French, English, and Spanish versions of a node, each version's votes are tallied separately.
In many cases, site admins will want to aggregate data across a translation set, such that, e.g., a vote for any member of the translation set is reflected for all members.
How to achieve this however?
Different strategies are emerging--see this handbook page: http://drupal.org/node/304047#aggregating-content
For Voting API, our choices seem to include:
1. Don't do anything. Leave any special handling to other modules using Voting API. If they want to e.g. send a $node->tnid where available in place of a $node->nid when registering or fetching voting data, they can do so.
2. Introduce at least an optional handling in Voting API.
2a. Redirect both vote registration and vote fetching for nodes to the source translation if it exists. (This is basically the approach taken by the i18npoll module in the i18n package--it registers votes to the source translation.)
2b. Save votes as is currently done but, when loading results for a given node, aggregate votes across the translation set.
3. Provide a separate helper module to provide multilingual handling.
A problem with option 1 is that it could lead to a confusing mix of results, depending on the module using Voting API.
2a has the downside of losing data. It might be useful in some cases to retain information on which language a vote was cast in.
2b or 3 seem like the best starting points. For 3, it might be possible to use hook_votingapi_results_alter().
I'll see if I can sketch in something for review. Ideas meantime would be great.
| Comment | File | Size | Author |
|---|---|---|---|
| #7 | votingapi-translation.diff | 3.11 KB | nedjo |
| #2 | votingapi-translation.diff | 1.86 KB | nedjo |
Comments
Comment #1
nedjoI spent most of yesterday wrestling with various possible versions of approaches 2 and 3. The short conclusion: approach 1 is looking mighty attractive!
Trying to change the way that data are stored or retrieved in Voting API itself raised a host of issues.
Voting API is nicely abstracted from any content_type (e.g., node, user). Dealing with nodes' translation ids directly would compromise this.
Various strategies I tried and their problems:
1. Implement
hook_votingapi_results_alter()to add in the data for a whole translation set when a node is being handled.While useful for adding new data, is not suited to altering existing data since it gives summary statistics (e.g., percent), not the data used to calculate those statistics.
Also, vote data are loaded as well through other methods, and my testing ended up with two sets of data, one aggregated and one not.
2. Redirect all handling of node data to use the tnid if available instead of the nid.
This proved very messy. It would need some sort of _substitute function that, fed a content type and content id, would enable modules to substitute one for another. Beyond that, there is no single set of data handling functions--data are loaded, saved, deleted in multiple places. So redirecting would require messy hacking in many places.
3. Redirect only node vote loading to reference the combined data of a translation set.
Specifically I looked at enabling modules to specify an array of content_id values to be used in place of a single one in
_votingapi_get_standard_results().But IIRC this wasn't the only place that voting results could be loaded from.
I hit various problems with potential collision between what is loaded and what is saved.
Beyond this, there were issues with determining voting rights. If all nodes in a translation set count, we have the potential for a given user to bias statistics by voting separately on each member of a translation set, and hence a need for preventing a user from voting on a node if s/he has voted already on a member of its translation set.
Then I returned to the original approach 1, "Leave any special handling to other modules using Voting API." I used fivestar as the test case. And in a few minutes coding things were looking peachy.
So what I'm thinking now is; put just enough in Voting API to provide some consistency in the handling of voting modules. Like a common settings form element that they can use. And, possibly, some translation-related Views exposure option. Then leave well enough alone.
Patches to Voting API and fivestar coming up....
Comment #2
nedjoHere is a small patch to add a configuration option that voting modules can add to the voting api settings form.
Comment #3
nedjoAnd the accompanying patch on Fivestar: #307207: Multilingual voting: option to tally votes by translation set.
Comment #4
nedjoA bit more explanation about the patch.
It creates a configuration option that doesn't appear by default on the Voting API settings form but can be added by any module wanting to include options for handling of multilingual content votes.
The two options are (a) 'default' (no change, votes are separate on each node in a translation set) and (b) 'source'--votes for any piece of translated content should be registered to and then loaded from the "source" translation.
In the fivestar patch, this translates into: Use tnid instead of nid when registering and fetching voting data.
One issue we should at least think through is the implications of changing this setting.
What's the expected outcome if there are existing translated content and a site admin changes the setting from 'default' to 'source'? Should data be updated so that any existing votes are summed and assigned to the source translation? Then there's the other direction. If an admin turns 'source' to 'default', there is no effective way to determine which of the votes were assigned to which version of the node. The best that could be done would be e.g. dividing the existing votes between members of the translation set.
Probably not a critical problem, but one to think through. Maybe some warning text is enough.
Comment #5
Scott Reynolds commentedI am afraid Im missing the point on all this crazyness. Wouldn't we want votingapi to calculate (if multilingual site) results on each individual translation and then aggregate those together? This way a voting widget like Fivestar could do what they want? So basically, for a two language site (english, french), you would have three scores per node
1.) English (function = 'english count', 'english total', 'english average')
2.) French (function = 'french count', 'french total', 'french average')
3.) total (function = 'count', 'total', 'average')
when a vote is cast on a node Votingapi determines what translation and then adds it to that translation and recalcs for the total as well.
I am assuming that given a nid you can determine what language and what the source node is. But this seems too simple I am missing something. What is it?
Perhaps even better, the voting widget like FiveStar, for each vote cast, sends in an array of vote objects, each with a different content_type (so in the above example it would content_types 'english_node', and 'node'). Therefore, if you change the setting it still will work (from default to source). The net result of this is that every vote is recorded twice and counted twice (once for language and once for source) whereas if votingapi can determine from the nid what the tnid is, it results in only votingapi counting twice (by counting I mean in the votingapi_cache table and recording i mean the votingapi_vote table).
Gosh, I hope this all make sense. I really feel like there is a better solution then this.
Comment #6
nedjoThanks for your comments Scott.
Yes, that's one of the approaches I tried. Probably it merits a fuller attempt before we opt for a quick fix. Setting this back to active. I'll post a version of one of the patches I tried so you can take a look. It was messy, but maybe with some review we could clean it up.
Comment #7
nedjoHere's a roughed in patch along the lines Scott discussed, hackish, untested, intended as a proof of concept. I'll return to this next week.
Comment #8
eaton commentednedjo, thanks for putting energy into this! I'll take a close look at it to see how it works with the existing code. You mentioned at a couple of points that information retrieval from the VotingAPI tables happens in a variety of locations -- as much as possible I've tried to consolidate the functions, and an internal query builder is used for everything but the inserts. Do you think there are better ways this could be handled? I tried to preserve the ability for people to pull up random slices of voting data while still workign with centralized functions, and hopefully we can leverage that.
The biggest frustration with internationalized content is that we've effectively abandoned the idea of a nid as a useful unique identifier. For *untranslated* content, we want to vote on 'nid', but for translated content we really want to vote on the tnid. Does that make sense? if core always populated the tnid, I'd say we should just use it for safety...
The impact on modules that do their own custom vote aggregation is tricky.
Comment #9
nedjo@Eaton
Thanks for your thought and explanation.
I'm not thinking that changes are needed in the general ways Voting API handles data. I just don't particularly relish the idea of having to introduce overrides wherever data is handled to ask "is it the nid or the tnid that you're talking about?"
We're hitting a general problem that probably calls for some community-wide consensus. The problem is: under what circumstances is the nid the best primary identifier? and under which is the tnid best? Or, put differently, under what cases do we want to track each translation separately (by nid) and then, as needed, aggregate data vs. under what cases do we want to actually track by translation set (the content per se, independent of language)?
The answer I suspect comes down to the question: is the particular translation being operated on a significant piece of information for the purposes of this datum?
Take page view statistics. In that case, I think there is a strong use case for the use of nid. When we want to know what pages were viewed, we might often interested in what language was seen. When we want to, we can always aggregate across the translation set to generate statistics. So, probably, these data are best tracked by nid.
With voting, I'm not so sure. When we're voting, is it important that we're voting on the French translation of a piece of content? My initial feeling is, probably not--unless we're asking "what's the best translation? ;) A vote is on the content per se, not some translation of it. So, it seems to me, we're not losing significant data if we store and load from the nid/tnid rather than the nid.
(In practical terms, we'll often want to expose voting data to views. Here, aggregation is not necessarily straightforward since Views doesn't by default support GROUP BY operations. See merlinofchaos's comments in this related issue on Views: #307032: Support for displaying translation sets.)
We're having pretty much the same conversation about flag module over here: #307810: Multilingual support for flagging. It looks like we need some community consensus before we can move forward. When is a piece of content a node and when is it a translation set member?
Comment #10
eaton commentedYeah, that's exactly the problem that I've been chewing on. It would be possible for a multilingual voting module to cast its votes on tnid, mind you -- and if it provided a custom relationship handler for views, it could tie into all the votingapi data based on tnid rather than nid. I'm just not sure that there is any way to automatically do it.
Actually, I'd even be willing to make a language-aware version of the join handler that operates on the tnid, for individuals that want/need international voting. It would still be up to them to tweak the individual modules that CAST votes, but the views integration would work.
Would that help at all, or would it only shift the problem to another area?
Comment #11
nedjo@Eaton
My hunch is, it would help, and would help promote better solutions elsewhere. We're almost always better off choosing what's correct and relying on other solutions to correct themselves, rather than trying to compensate for what's incorrect elsewhere. Further comments here: http://drupal.org/node/307810#comment-1018545
Could you have a quick look at the patch I did for Fivestar in #307207: Multilingual voting: option to tally votes by translation set? It basically comes down to this line:
When registering or fetching voting data, if set to use the 'source' (evil stormtroopers of the empire, beware!), send the tnid if it's set, or otherwise the nid.
I'd be tempted to change it to:
In other words, enforce voting by source id, and avoid problems of what do do when switching back and forth.
In this case, at least, it's a pretty simple change and may accomplish the aim without muddying Voting API. But it only makes sense if we accept the "use the tnid if present, otherwise the nid" conclusion.
Comment #12
milksamsa commentedsubscribing
Comment #13
nedjoEaton:
For the views part, the new code in modules/translation.views.inc in views module may be useful for reference.
Comment #14
quicksketchI've been trying to summarize what would be necessary from the "VotingAPI integration module" standpoint (such as Fivestar). It sounds like Fivestar would be responsible for registering the votes on the tnid instead of nid, providing the option to the administrator to vote on either the nid or tnid (probably be node-type), and then implementing the hook to VotingAPI telling it when to use the tnid or nid when it comes to Views.
So, a breakdown of these requirements:
1. An option the node type form (in the Fivestar configuration area):
This would variable_set() an option such as
[node type]_fivestar_target2. Code the display and rating widgets to use the setting for the node type when displaying ratings in nodes and displaying the rating widget.
3. Implement the hook for VotingAPI's views integration, which would basically do a
variable_get('[node type]_fivestar_target')for each node type being retrieved.This last point gets a little fuzzy, as what happens when you try to retrieve a set of average results for some nodes that are supposed to use tnid and some that are supposed to use nid?
Comment #15
nedjoFor the views part, it may benefit from the recent translation set relationship feature that went into Views, see translation.views.inc. Or from some improvement on it.
Say what we want is a list of highest ranked nodes in the current language. With a views relationship, we can display nodes or the Spanish versions of source nodes.
So we could:
1. Create a view, filtered to show nodes that are either (a) untranslated nodes in the current language or (b) source translations.
2. Add voting data.
3. Add a relationship, so that we see the current language version of source translations.
4. Display fields from the relationship node.
Hmm, this approach assumes a given node is receiving votes either as a translation or as a translation set--not as both. Which, I guess, would work if we designated the behaviour by node type.
But I'm not fully convinced that node types is the right way to go here. I suspect that whether a vote should be applied to a translation set or an individual translation is going to depend not so much on what kind of content we're voting on but rather on the quality we're voting on.
Maybe today fivestar is limited to a single parameter. But we can imagine voting on multiple parameters. Say you have a 'song' content type. People might vote on multiple parameters: fan response (by way of comments left), quality of lyrics, instrumentation, recording quality, danceability, or whatever. In that case, maybe fan response and lyrics would be by the individual translation--they're different because they're translated into different languages. Everything else is identical throughout the translation set (it's the same recording heard in each case) so apply to the translation set.
Do we have current voting modules that provide multiple parameters/criteria/axes?
Comment #16
quicksketchFivestar recently gained the ability to vote on multiple criteria, though there is no UI for configuring it yet. I'm not sure that the axis is really the area where translation makes a difference. In your example, you'd probably turn on translation-aware voting for the entire "song" type, not for some axis but not others.
Comment #17
nedjoWell....
With flagging, we're deciding, it's not the content we're flagging that determines if a flag is better per translation or per translation set--it's why we're flagging it.
Isn't it the same with voting? Isn't it the particular quality or qualities a vote is evaluating that can have per-translation implications--not the content itself?
If all the axes in my song example should be treated per-translation - even though many are clearly translation-independent - then it feels like we're back where we started, with only per translation voting and no voting by translation set. Any node type that can exist in multiple languages by definition has some properties that vary by translation, and so could potentially be evaluated per translation.
As with flagging (and, likely, nodequeue, and other similar functionality), it seems to make more sense to determine the tnid/nid distinction by voting axis rather than node type.
Comment #18
quicksketchI think axes are a different situation than flags. Especially considering that all voting modules default to using the "vote" axis for all rating on all types. This axis tends to be applied to all types of content, while other axes would not be. For example, a "song" would have three axes, one of which would be "vote" for the grand total. Meanwhile blog posts would only use the "vote" axis, and no others. In the case of rating, the node type determines whether or not ratings should be saved against the tnid.
Maybe I'm just thinking of this in the terms that Fivestar rates on things. My basic feeling is that if you have multilingual content, Fivestar is going to want to rate that content based on the tnid. If the content is not multilingual, it'll use the nid.
Comment #19
tiago.gmarques commentedI just wanted to give you the opinion of a totally Drupal newbie (well, maybe not that newbie but pretty much still struggling in the learning curve).
For my case, I want the voting to be common to all translations. Like someone earlier said, I want the content to be voted and not the translation itself (though I recognize that for some people this might be useful). Imagine the content is a movie. The rating the movie gets should be independent of the language the page is presented.
I am also following this same issue for flags and their approach seems to be similar to yours. They are flagging the source translation. I know this might be the easiest solution, but for me is clumsy. Particular because of views. Since usually only content in the chosen language is shown, views of the flags (and this case votes) will be flawed.
So, for me, the best solution is when a vote is cast it also casts the vote on all translations of that content. The biggest issue I am seeing with this is adding a translation of the content after the vote is cast (because this is will be "unvoted").
Comment #20
halfabrain commentedHey all
I tried applying the fivestar patch but couldn't get it to work - first off, I received an error in terminal saying that it couldn't find the file, so I manually altered the filename to the latest version of fivestar. However, then I received an error that 2 out of 2 hunks had failed (for more info see drupal.org/node/307207).
Please can you tell me if I have applied these patched incorrectly or if they need to be updated for the latest version?
Thanks
hab
Comment #21
halfabrain commentedDear all
Please can someone reply to my previous comment #20? Even if just to say that there is nothing they can do, or perhaps that they are working on it?
Thanks
H
Comment #22
myxelf commentedSubscribing...
Comment #23
amrlima commentedAny news on this issue?
Comment #24
ZyanKLee commentedHey there - I would really like to see this feature implemented.
Are you still working on implementing this? Or is this somehow solved (and I missed it)?
Comment #25
ergunk commentedIs there a solution for 7.x?
Comment #26
dgastudio commentedany update for d7?
Comment #27
italya commentedhello
first excuse me for my english i speak french..
i have drupal 7 and this module voting api with fivestar.
And the same problem with my video translation (en/fr)
i dont have the same result for vote.
Do you have a fix for d7 with this?
thanks a lot
Comment #28
adelka commentedsubscribe - D7
Comment #29
rockNroll00q commentedIs there a solution for 7.x?
Comment #30
torotil commentedIn D7 things changed a bit since votingapi now supports all kinds of entities. To handle that we'd need something like http://drupal.org/project/i18n_translation . This seems quite complex ATM but I'll keep it in mind for 7.x-3.x. For most use-cases it's a lot simpler to simply use solution 1 from the original report.
For the moment I'd say this is:
* 7.x-2.x -> won't fix
* 7.x-3.x -> postponed