Closed (fixed)
Project:
Translation Management Tool
Version:
8.x-1.x-dev
Component:
Core
Priority:
Normal
Category:
Feature request
Assigned:
Reporter:
Created:
31 May 2016 at 07:41 UTC
Updated:
4 Aug 2016 at 15:24 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
edurenye commentedAdded those methods.
Comment #3
miro_dietikerBoth implementations look very similar. Why would we want to have two new methods?
IMHO the goal is that the source is always segmented when we add data to a job. Accessing the data thus doesn't need triggering any segmenter again.
(For past jobs we can't change the situation anyway.)
Hm, getSegmentedSourceData should IMHO return unsegmented data if the segmenter is not installed. Currently it returns pure emptyness...
Comment #4
edurenye commentedThe problem with the point 1 is that if we make getSourceData() return allways the data segmented, then we will have problems with non formatted fields like title for nodes, as then the tags will be shown. I think the best is have two methods. Another solution could be check if the fields are formatted, and just add the tags when it's formatted, but I think this is not viable as other plugins could want to use it and get the segments of a non formatted field, could be that possible?
Comment #5
edurenye commentedBy now I fixed the second point.
Comment #6
miro_dietikerI start to understand parts here. The problem is that the calls are not provided and the interfaces are not described in how they should be used.
The segmenter could be transparent.
If it only detects a single segment, it doesn't need to annotate a segment at all.
IMHO even a lengthy title could contain multiple linguistic segments.
But we could also define that we only trigger the segmenter for format aware fields.
Also, the issue should also call the segmenter and make TMGMT use the segmenter.. (uh, test dependencies...)
Including test coverage. Otherwise we are stepping in the dark.
We can't commit the methods as is.
The segmenter should not only return the string. It should also be able to attach OO data to each element.
Thus, we can later reuse the parsed data without triggering the parser again.
Comment #7
edurenye commentedI understand the problems and that we have some possible solutions, but not sure what to use. I have another idea, what if we don't add any method, but just add another property to the data item called #segmented_text for example so if it is set we can use it in the jobItemForm? Also what kind of OO do you mean?
Comment #8
miro_dietikerOK, more thoughts:
Returning data with an additional key containing segment information is a good idea for API consistency.
We can still offer some helpers to work with it, if it simplifies code.
To run a test successfully, we can provide a dummy segmenter service in the test. Make it return some variable like a mock does, so the calling text can set what the service should return.
But what we learn from this is, that the segmenter interface needs to be introduced by tmgmt, while the tmgmt_memory can only implement it. Otherwise interface annotations and decoupling doesn't work.
Hope this helps a bit to move forward and bring the segmentation back to tmgmt.
Comment #9
edurenye commentedThis patch is a WIP.
Comment #11
miro_dietikerOhh, those are different things.
Mind that on the source side, a segment originates. When the source is parsed for the first time, segments are identified.
On the translation side, the segmenter is only allowed to detect segments that are passed into translation and exist on the source side. We should only detect segments on the right side if they have a pair.
By blindly segmenting the right side, you could detect different segments and that will create huge trouble..
Comment #12
edurenye commentedI rather add validation when we save as completed than not save if there is no match. Also fixed tests.
Comment #14
edurenye commentedFixed another test, I can't fix the other one yet.
Comment #16
Bambell commentedProbably just stating the obvious, but so far, here's what I've got :
The test fails at at line 227 of
TMGMTUITest.Line 219 :
$this->drupalGet('admin/tmgmt/items/' . $item4->id());.Going to the newly created job item (line 216) and saving it (line 225).
tjiidhere is 6. Job 3 has job items 5 and 6.This redirects to the job overview page, where the job items are listed.
Line 226 :
$this->clickLink('View');.In a successful run, this clicks on job item 5. With the patch, this clicks on job item 6. So, job item 5 was last modified in a successful run and job item 6 was last modified with the new patch. What I'm not sure to understand is why this code worked in the first place, since job item 6 is created and saved after job item 5 in both cases.
Comment #17
Bambell commentedAs discussed with @edurenye, fixing tests seems most appropriate here. Other than that, I did not investigate much to see if the data segmentation was working fine :-/.
Comment #18
Bambell commentedComment #19
edurenye commentedThat looks great to me.
Comment #20
miro_dietikerI'm trying to document some thoughts about usage of the Segmenter.
Now the idea of #segmented_data is tightly coupled between tmgmt, tmgmt_memory and the segmenter.
Why are we not passing in the items completely to the segmenter and it has access to it and can attach #segmented_data on its own?
The segmenter then would do the checks above.
That's much more flexibility inside the segmenter to improve the situation.
It would be the internal choice of the segmenter to attach #segmented_data to the $field.
The key here is that $field is passed by reference.
This would translate to:
Whereas again $source_data here is then internally checked for #segmented_data and source and translation segments.
Also the segmenter can independently check health (can also improve to much more than just counts) and display proper errors.
This would make the dependency much more reduced and allow more freedom to the Segmenter.
Also TMGMT has less assumptions about how the Segmenter internally works IMHO.
Still not sure and open for discussions to improve or learn i was wrong. :-)
Hm, why getting the segmentData from $label?
Comment #21
berdirAnd here is the review finally.
Spoiler: You will not like it :)
the first call to segmenting will do it and persist it. The source text will not change anymore, there is no need to repeat this process every time we unserialize*.
So instead, we can move this call into getSourceData() and only need to call it from a single place.
* One exception: We have to possibility to read the source data again if it changed on review. We need a follow-up to figure out what needs to happen there. Basically, we will need to explicitly throw away the segmented data and do it again. But that can come later and is not important for step one.
Instead of checking for the existence of a service that is actually in our namespace, we register an empty implementation in tmgmt. That will not do anything and not return anything but it makes the code easier to read.
memory might then need to implement a ServiceProvider and replace the class (might still work to just define it if it follows later in the list of modules but that is a bit unreliable).
Need to check when this scenario happens exactly. Could be for the update case I mentioned above, but it could also be when a translation is written back. that would not be correct, see below. So this might need to go away here...
I'm fine with miros suggestion to give the segmenter the whole array (Which btw needs to be called $data_items/$data_item, *not* $fields)
However, we must never segment the translated text. Our only hope is that the translator is nice and respects and supports the segmented data. If not, there is absolutely nothing that we can do.
As an example (if you don't believe me), take a right-to-left language like russian.
Source text:
Sentence A. Sentence B. Sentence C.
The translation that will come back will basically look like this:
.C ecnetneS .B ecnetneS .A ecnetneS
Your implementation would assign "Sentence A" to "C ecnetneS".
Which clearly is wrong :)
Yes, that means that every translator that we want to support translation memory with needs to be updated to somehow work with segmented data instead of the current text.
We can make some assumptions, like if there's only one source segment and at some point, reviewers might have a tool (hi Sasa) to fix segments if they got lost/broken but we're a few steps away from that.
Yes, to actually test your memory, it means you need to have a translator that supports this. Doing that with test translator should be do-able. You could also patch google/microsoft to send out the segmented string and hope that they're nice to our segment tags.
We'll also see there we might need to do something with segmented data, but more likely the opposite of this (removing segments and put into #text when only #segmented_data is set (which I would btw name #segmented_text).
Nice variable name ;)
There isn't really such a thing as "Service interface".
I'd just simplify this to "Offers a ...". solves the >80 chars problem too.
Comment #22
edurenye commentedDone all this.
I didn't thought about the problem with the order in the languages, this does not affect this patch now but does affect tmgmt_memory, I need to think how to solve that.
Comment #24
edurenye commentedFixed the test errors.
Comment #26
edurenye commentedUps, sorry, to fast.
Comment #28
edurenye commentedLast test fix.
Comment #29
berdircould be simplifed to return $segmenter->getSegmentedData() ?: $data;. Or we could even say it must return $data, then that wouldn't be necessary.
that's also not really correct, $data item means an array with #text, #translation and so on. This is really just the text, so $translation_text or so.
But this means you're still trying to segment the translation text? As explained above, I don't think that is possible at all.
Yes, without this, it's impossible to use tmgmt_memory without Sasa's project. But I don't see another way.
You could try to just show the segments and hope they don't get broken.
See also #2757813: Provide support for masking HTML tags while editing, the idea there is that be able to switch out the text that we show, what that means is that we could show the segment tags if memory is enabled, at least as a Proof of Concept.
Base classes are usually abstract and have actual logic. Without any code, there is no benefit from extending this class.
I'd rather name this NullSegmenter or something like that.
Comment #30
edurenye commentedDone everything but the point 3.
We just have that data as temporary data for the plugins to use it, but those are not real segments and they are not displayed so it's not affecting anything, it's just because maybe some plugins may need it (maybe to know how many segments are already translated, just to say an example).
Comment #31
edurenye commentedUndid this change, otherwise in tmgmt_test is the last to change the Segmenter and then in tmgmt_memory the test is used when shouldn't be.
Comment #32
berdirOk. Discussed this part with Miro.
Agree with what he is saying in his earlier review. All of this should be a method on the segmenter, something like validateFormTranslation() or something like that. That receives argument like the form element ($form['review'][$group_key][$parent_key][$key]), the array_key, $form_state and whatever else you need.
Then you can experiment with validations in your module, and the segmenter can do validations based on what he supports and understands. Count is just one example. We might also want to validate if the segment hashes are the same, for example.
Lets mark this interface as @internal to clarify that it is not yet a public/stable API and will change in the future.
This method will then no longer be on the public inteface.
Those three methods need better documentation, some things are simply wrong (getSegmentedData() does *not* receive a string). They should also have better variable names (if it is a string, do not use $data. Use $text, $segmented_text, ..).
Where useful, examples of the expected/returned data structures should also be included.
One also mentions segments delta, should be id.
Comment #33
tduong commentedDon't forget to update it also in other modules where it is used, like in Drupal\tmgmt_memory\Segmenter ;)
Comment #34
edurenye commentedDone all those points.
About this:
Not really, this is the delta, as identifies the segment inside the data_item.
In this issue #2742525: Segmentation semantics is where I'll add a unique ID for the segment, this will be the ID, so this will mean another patch to this issue, but first I need to fix that issue.
@tduong tmgmt_memory now works with the patch 31 I'll write here when works for this patch.
Comment #35
berdirVery close now ;)
remove those lines, because while you might still do it, I'm still convinced it is wrong (and I think we're slowly convincing you as well).
Comment #36
berdirComment #37
edurenye commentedDone, also now getSegmentsOfData() will return an array of segments formated as an array containing the segment properties.
Comment #38
edurenye commentedMissed to change the test accordingly.
Comment #39
edurenye commentedDone.
Comment #40
miro_dietikerYeah this starts to look awesome! Some nits...
Is the fallback to $data needed? I think not. The segmenter should always return data.
The NullSegmenter should still consider ALL valid return values!
return $data;
Comment #41
edurenye commentedDone.
The validate form adds an error to the form, so here does nothing.
Comment #42
berdirthis should return $segmented_data?
We should also add a test to test the test segmenter test.
More seriously, most of this is pointless :) We're testing hardcoded dummy implementations in a test implementation.
You use test implementations to test the other part of your code (like that the method is being called), you don't test them directly. See below.
This one for example. You load test data, and ensure that you get hardcoded test data back. But the test implementation doesn't even look at $data.
Only provide a test implementation if you want to test something else remove this test and the implementation for it. It is not needed.
Same for testSegmentData(), testGetSegmentsData(), and that's all of this class ;)
Comment #43
edurenye commentedDone.
Comment #44
miro_dietikerYayy, committed. :-)
Doesn't look that complex anymore. And most importantly all known cases can be covered in pluggability now.
Looking forward to see real experimental implementations and learn from its usage.