Problem/Motivation

We have amazing tools already and still the translator has NO idea about how it will look once the translation is published..

Job items have a reference url and thus the translator might get it and can see how it looked before.
However the translated content can not be previewed.

As of translator feedback, the lack of preview leads to most complains with jobs: difference in length for certain languages lead to broken design.

Proposed resolution

-implement route to preview translation content using entity view builder.
-give reviewers access to preview, display link on the review form if preview is supported.
-include hash key in url to provide external access check.

Comments

Wartus’s picture

Yes, i have the same request from our translator.

the first question he asked after i explained the setup was: "where can i preview my work before submitting?"

miro_dietiker’s picture

We have drafted this feature and while it would be (truly) amazing, we expect even the most basic functionality is more than a week of work. Also there would still be limitations and followups that needs to be fixed later.

So while it is broadly requested, no one funded it (and thus no one works on it as far as i know).
We also have different things of higher priority (smaller pieces, more quick progress).

miro_dietiker’s picture

Version: 7.x-1.x-dev » 8.x-1.x-dev
Issue summary: View changes
Issue tags: +job workflow

With 8.x this is way better achievable... :-)

miro_dietiker’s picture

We have some kind of preview through the WYSIWYG support.
#2042727: Support for CKEditor instead of normal textarea field
This is not enough though. A common question is to proof if the text is properly wrapped in the original output. Sometimes translations just don't fit into the space intended.

Each source could offer a method to tell us if it supports inline preview.

If a source item supports inline preview, we can display a button for preview.
The button would open a separate tab or overlay and display the text entered in context of the source.
For entities, we can create a temporary item and render it.

miro_dietiker’s picture

This has been discussed with a translation services provider. Would be nice if we can work on this kind of page preview.

We even discussed to expose preview through the translator plugin so the translators on the online platform can trigger preview rendering. :-)

miro_dietiker’s picture

I have been discussing this intensely with a customer that needs also preview rendering and offering the result to off site translators.
For this, we need to be able to persist preliminary translations that are not supposed to be accepted ever. Most importantly because we need to separately pull data and render the preview in a separate request...

The best fit for this would be to add a PRELIMINARY state that indicates a translation is there, we can preview it already, possibly even provide feedback, but we can not accept it yet due to remote workflow constraints. Most translators will not use this state at all.

There are also other approaches, but this seemed to be the best fit for TMGMT.
Any other opinions?

berdir’s picture

Priority: Normal » Major
Issue tags: +Needs issue summary update

So, lets start with the basics, before we get to remote translator previews, which are about 5 steps away.

Note that not each step is a separate issue, but it makes sense to start small to get into the whole concept.

Step 0:

In the content source plugin/module, implement a route like /tmgmt/content/preview/{job_item}. For access control, use the accept job permission.

In there, load the entity from the job item, populate it with the target translation of the job item and view it using an entity view builder.

You can find most of this code in the content source plugin, which does all those things already (and then saves, instead of displaying). It's important that we do *not* save here. you can try to re-use existing code there but note that you must not change existing, public API's unless there really is no other way.

Step 1:

The next step is giving reviewers access to this preview. For that, implement a new interface, SourcePreviewInterface or so, that has a getPreviewUrl() method, which returns a a Url object if preview is supported. On the review form, check if the source implements this and if so, display it as a link.

Step 2: Access control. Similar to a CSRF hash, the built URl should include a hash, for example based on the job item data, like ?key=segk4gs3t3kdsfefr19.... Provide an access check that verifies that (by generating that key again from the job item data). That will allow external access .

Step 1 and two is probably a good first step for a first child issue. I'll add more explanations later. Of course, everything should be covered with tests.

CTaPByK’s picture

Issue summary: View changes
CTaPByK’s picture

Assigned: Unassigned » CTaPByK
Issue summary: View changes

I start working on first step (step 0) to implement route for preview.

CTaPByK’s picture

Status: Active » Needs review
StatusFileSize
new10.09 KB

We make that step 0 for this preview, for now we have just implemented controller and route for preview.
After that we will continue with step 1 and create tests.

berdir’s picture

  1. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,220 @@
    +    // Load entity from tmgmt job item.
    +    $entity = \Drupal::entityTypeManager()->getStorage($tmgmt_job_item->getItemType())->load($tmgmt_job_item->getItemId());
    

    Use injection to inject the dependencies you need.

  2. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,220 @@
    +    $lang = $tmgmt_job_item->getJob()->getTargetLangcode();
    

    Use $target_langcode instead, don't abbreviate variables (too much)

  3. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,220 @@
    +  public function getData(JobItemInterface $job_item) {
    +    $entity = \Drupal::entityTypeManager()->getStorage($job_item->getItemType())->load($job_item->getItemId());
    

    Do we really need this?

    You should be able to just call $job_item->getData(). getting data is when you create a new job (item)

CTaPByK’s picture

Here is the new patch for step 0, i remove unnecessary methods and code.

CTaPByK’s picture

StatusFileSize
new1.62 KB
new5.92 KB

Patch with test coverage for preview.

CTaPByK’s picture

StatusFileSize
new6.82 KB
new293.32 KB

Here is patch with better test, also title for preview is modified.

thenchev’s picture

StatusFileSize
new2.63 KB

The missing interdiff.

berdir’s picture

Status: Needs review » Needs work
  1. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +    $this->entityManager = $entity_manager;
    

    you should use $entity_type_manager and $this->entityTypeManager everywhere.

  2. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +   * @param string $preview_type
    +   *   The preview mode that should be used to display the entity.
    

    preview_type isn't really a singe, and below you then talk about preview mode?

    I'd just call this what it is. $view_mode.

    Also, I like adding it, but currently it's possible to actually use it.

    What you should be able to do is add another argument to the route {view_mode}, and add the default value for it in defaults:

    defaults:
      view_mode: full
    

    If that doesn't work, you can also define two routes, one with the view_mode argument, otherwise identical.

  3. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +    $page = $this->entityManager
    +      ->getViewBuilder($entity->getEntityTypeId())
    +      ->view($preview, $preview_type);
    

    This only displays the node in the right language for me when accessing the site in that language. Since we are previewing a specific language, you should force that by passing $preview->language()->getId() as third argument.

  4. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +    return t("Preview of @title for @target_langcode", [
    +      '@title' => $title,
    +      '@target_langcode' => $target_langcode,
    +    ]);
    

    We should use getTargetLanguage()->getName() instead. and call it $target_language then.

  5. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +  /**
    +   * Return translation data.
    +   *
    +   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    ...
    +   *
    +   * @return $this
    +   */
    

    This is a lie, it doesn't return translation data. It builds the entity translation for the provided translation data. The @return is also completely wrong. Should be ContentEntityInterface instead.

  6. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +
    +    $embeded_fields = \Drupal::config('tmgmt_content.settings')->get('embedded_fields');
    

    Use $this->config()

  7. +++ b/sources/content/src/Controller/ContentTranslationPreviewController.php
    @@ -0,0 +1,141 @@
    +            $this->makePreview($translation->get($name)->offsetGet($delta)->$property, $property_data, $target_langcode);
    

    you're not using the return value yet, so I don't think this is doing anything yet.

    To test this, install tmgmt_demo and paragraphs demo. Make sure that the paragraph field is saved as a embeddable field in the settings. Then translate a node with paragraphs and view it.

  8. +++ b/sources/content/src/Tests/ContentEntitySourceUiTest.php
    @@ -471,4 +471,28 @@ class ContentEntitySourceUiTest extends EntityTestBase {
    +    $this->assertText($node->getTitle());
    

    This tests the wrong behavior. We want to see the *translated* title. Which, with the default test translator, is "de_$title".

  9. +++ b/sources/content/tmgmt_content.routing.yml
    @@ -0,0 +1,11 @@
    +entity.tmgmt_job_item.preview:
    +  path: '/admin/tmgmt/content/preview/{tmgmt_job_item}'
    +  defaults:
    

    I didn't say anything about admin :)

    It's a preview if your content. You want to see that in the frontend theme. So just /tmgmt/content...

CTaPByK’s picture

Status: Needs work » Needs review
StatusFileSize
new7.25 KB
new7.28 KB

In this patch i try to fix all suggestions from latest review.

CTaPByK’s picture

StatusFileSize
new441.95 KB

Here is screenshot of testing preview with paragraph.

  • Berdir committed 652420f on 8.x-1.x authored by CTaPByK
    Issue #1998060 by CTaPByK, Denchev, Berdir: Provide an in-site preview...
berdir’s picture

Removed the options from the route, Drupal can figure that out automatically. Tests still pass.

Committed.

berdir’s picture

Status: Needs review » Fixed
thenchev’s picture

There is an edge case when the preview is not working. When you start creating a job (didn't save it yet or submit to translator) we get an exception when you try to go to the preview for some job item. The target language in that stage is still undefined is the reason. I can make a simple check if is still undefined in the controller preview and redirect to the job with some message that the user needs to save the job first or submit to translator to view it. Not sure how important it is...

berdir’s picture

Good catch. Lets handle that as part of the custom access check, just deny access if the item has no job (could happen if it's still in cart, that would be a fatal right now too) or the job is not active.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.