Verified Commit 7bd4aaca authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #2575945 by Sutharsan, mstrelan, tobiasb, ranjith_kumar_k_u, andypost,...

Issue #2575945 by Sutharsan, mstrelan, tobiasb, ranjith_kumar_k_u, andypost, ericdsd, smustgrave, Ammaletu: A new module version is not recognized by interface translation update

(cherry picked from commit 9e290d8c)
parent 734e8cd9
Loading
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -23,6 +23,43 @@
// Follow-up issue: https://www.drupal.org/node/1834298.
require_once __DIR__ . '/locale.translation.inc';

/**
 * Implements callback_batch_operation().
 *
 * Checks for changed project versions, and cleans-up data from the old version.
 * For example when a module is updated. This will make the translation import
 * system use translations that match the current version.
 *
 * @param string $project
 *   Machine name of the project for which to check the translation status.
 * @param string $langcode
 *   Language code of the language for which to check the translation.
 * @param array|\ArrayAccess $context
 *   The batch context.
 */
function locale_translation_batch_version_check(string $project, string $langcode, array|\ArrayAccess &$context) {
  $locale_project = \Drupal::service('locale.project')->get($project);

  if (empty($locale_project)) {
    return;
  }

  $status = \Drupal::keyValue('locale.translation_status')->get($project);
  if (!isset($status[$langcode])) {
    return;
  }

  if ($locale_project['version'] == $status[$langcode]->version) {
    return;
  }

  \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
  locale_translation_status_delete_projects([$project]);
  locale_translate_delete_translation_files([$project]);

  $context['message'] = t('Checked version of %project.', ['%project' => $project]);
}

/**
 * Implements callback_batch_operation().
 *
+2 −1
Original line number Diff line number Diff line
@@ -267,7 +267,8 @@ function _locale_translation_batch_status_operations($projects, $langcodes, $opt

  foreach ($projects as $project) {
    foreach ($langcodes as $langcode) {
      // Check status of local and remote translation sources.
      // Check version and status translation sources.
      $operations[] = ['locale_translation_batch_version_check', [$project, $langcode]];
      $operations[] = ['locale_translation_batch_status_check', [$project, $langcode, $options]];
    }
  }
+86 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\locale\Functional;

use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\language\Entity\ConfigurableLanguage;

/**
 * Tests how translations are handled when a project gets updated.
 *
 * @group locale
 */
class LocaleTranslationChangeProjectVersionTest extends LocaleUpdateBase {

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    \Drupal::moduleHandler()->loadInclude('locale', 'inc', 'locale.batch');
    ConfigurableLanguage::createFromLangcode('de')->save();

    \Drupal::state()->set('locale.test_projects_alter', TRUE);
    \Drupal::state()->set('locale.remove_core_project', TRUE);

    // Setup the environment.
    $config = $this->config('locale.settings');
    $public_path = PublicStream::basePath();
    $this->setTranslationsDirectory($public_path . '/local');
    $config
      ->set('translation.default_filename', '%project-%version.%language._po')
      ->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
      ->save();

    // This test uses .po files for the old translation file instead of the ._po
    // files because locale_translate_get_interface_translation_files() (used to
    // delete old translation files) only works with .po files.
    // The new translation file uses _po.
    // Old version: 8.x-1.0; New version: 8.x-1.1.
    $this->makePoFile('remote/all/contrib_module_one', 'contrib_module_one-8.x-1.0.de.po', $this->timestampOld, []);
    $this->makePoFile('remote/all/contrib_module_one', 'contrib_module_one-8.x-1.1.de._po', $this->timestampNew, []);
    $this->makePoFile('local', 'contrib_module_one-8.x-1.0.de.po', $this->timestampOld, []);

    // Initialize the projects status and change the project version to the old
    // version. This makes the code update the module translation to the new
    // version when the (batch) update script is triggered.
    $status = locale_translation_get_status();
    $status['contrib_module_one']['de']->version = '8.x-1.0';
    \Drupal::keyValue('locale.translation_status')->setMultiple($status);
  }

  /**
   * Tests update translations when project version changes.
   */
  public function testUpdateImportSourceRemote() {

    // Verify that the project status has the old version.
    $status = locale_translation_get_status(['contrib_module_one']);
    $this->assertEquals('8.x-1.0', $status['contrib_module_one']['de']->version);

    // Verify that the old translation file exists and the new does not exist.
    $this->assertFileExists('translations://contrib_module_one-8.x-1.0.de.po');
    $this->assertFileDoesNotExist('translations://contrib_module_one-8.x-1.1.de._po');

    // Run batch tasks.
    $context = [];
    locale_translation_batch_version_check('contrib_module_one', 'de', $context);
    locale_translation_batch_status_check('contrib_module_one', 'de', [], $context);
    locale_translation_batch_fetch_download('contrib_module_one', 'de', $context);

    // Verify that the project status has the new version.
    $status = locale_translation_get_status(['contrib_module_one']);
    $this->assertEquals('8.x-1.1', $status['contrib_module_one']['de']->version);

    // Verify that the old translation file was removed and the new was
    // downloaded.
    $this->assertFileDoesNotExist('translations://contrib_module_one-8.x-1.0.de.po');
    $this->assertFileExists('translations://contrib_module_one-8.x-1.1.de._po');
  }

}
+5 −1
Original line number Diff line number Diff line
@@ -135,10 +135,11 @@ protected function makePoFile($path, $filename, $timestamp = NULL, array $transl
    }

    \Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY);
    $fileUri = $path . '/' . $filename;
    $file = File::create([
      'uid' => 1,
      'filename' => $filename,
      'uri' => $path . '/' . $filename,
      'uri' => $fileUri,
      'filemime' => 'text/x-gettext-translation',
      'timestamp' => $timestamp,
    ]);
@@ -146,6 +147,9 @@ protected function makePoFile($path, $filename, $timestamp = NULL, array $transl
    file_put_contents($file->getFileUri(), $po_header . $text);
    touch(\Drupal::service('file_system')->realpath($file->getFileUri()), $timestamp);
    $file->save();

    $this->assertTrue(file_exists($fileUri));
    $this->assertEquals($timestamp, filemtime($fileUri));
  }

  /**
+6 −2
Original line number Diff line number Diff line
@@ -98,8 +98,12 @@ public function testUpdateCron() {
    locale_cron();

    // Check whether tasks are added to the queue.
    // Expected tasks:
    // - locale_translation_batch_version_check
    // - locale_translation_batch_status_check
    // - locale_translation_batch_status_finished.
    $queue = \Drupal::queue('locale_translation', TRUE);
    $this->assertEquals(2, $queue->numberOfItems(), 'Queue holds tasks for one project.');
    $this->assertEquals(3, $queue->numberOfItems(), 'Queue holds tasks for one project.');
    $item = $queue->claimItem();
    $queue->releaseItem($item);
    $this->assertEquals('contrib_module_two', $item->data[1][0], 'Queue holds tasks for contrib module one.');
@@ -110,7 +114,7 @@ public function testUpdateCron() {

    // Check whether no more tasks are added to the queue.
    $queue = \Drupal::queue('locale_translation', TRUE);
    $this->assertEquals(2, $queue->numberOfItems(), 'Queue holds tasks for one project.');
    $this->assertEquals(3, $queue->numberOfItems(), 'Queue holds tasks for one project.');

    // Ensure last checked is updated to a greater time than the initial value.
    sleep(1);