Issue #2139467 by vaplas, Gábor Hojtsy, andypost, Sutharsan, alexpott, dillix: format_plural() does not handle D7 translations with a plural form after @count

8.5.x
Gabor Hojtsy 2017-11-30 23:32:06 +01:00
parent 9b874acf9f
commit 3f49bda7e7
4 changed files with 125 additions and 0 deletions

View File

@ -0,0 +1,13 @@
<?php
/**
* @file
* Post-update functions for Locale module.
*/
/**
* Clear cache to ensure plural translations are removed from it.
*/
function locale_post_update_clear_cache_for_old_translations() {
// Remove cache of translations, like '@count[2] words'.
}

View File

@ -7,6 +7,7 @@ use Drupal\Core\Cache\CacheCollector;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
/** /**
@ -172,6 +173,12 @@ class LocaleLookup extends CacheCollector {
} }
} }
if (is_string($value) && strpos($value, PluralTranslatableMarkup::DELIMITER) !== FALSE) {
// Community translations imported from localize.drupal.org as well as
// migrated translations may contain @count[number].
$value = preg_replace('!@count\[\d+\]!', '@count', $value);
}
$this->storage[$offset] = $value; $this->storage[$offset] = $value;
// Disabling the usage of string caching allows a module to watch for // Disabling the usage of string caching allows a module to watch for
// the exact list of strings used on a page. From a performance // the exact list of strings used on a page. From a performance

View File

@ -2,6 +2,7 @@
namespace Drupal\Tests\locale\Functional; namespace Drupal\Tests\locale\Functional;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\language\Entity\ConfigurableLanguage; use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
@ -55,4 +56,45 @@ class LocaleLocaleLookupTest extends BrowserTestBase {
$this->assertEqual($context['operation'], 'locale_lookup'); $this->assertEqual($context['operation'], 'locale_lookup');
} }
/**
* Test old plural style @count[number] fix.
*
* @dataProvider providerTestFixOldPluralStyle
*/
public function testFixOldPluralStyle($translation_value, $expected) {
$string_storage = \Drupal::service('locale.storage');
$string = $string_storage->findString(['source' => 'Member for', 'context' => '']);
$lid = $string->getId();
$string_storage->createTranslation([
'lid' => $lid,
'language' => 'fr',
'translation' => $translation_value,
])->save();
_locale_refresh_translations(['fr'], [$lid]);
// Check that 'count[2]' was fixed for render value.
$this->drupalGet('');
$this->assertSession()->pageTextContains($expected);
// Check that 'count[2]' was saved for source value.
$translation = $string_storage->findTranslation(['language' => 'fr', 'lid' => $lid])->translation;
$this->assertSame($translation_value, $translation, 'Source value not changed');
$this->assertNotFalse(strpos($translation, '@count[2]'), 'Source value contains @count[2]');
}
/**
* Provides data for testFixOldPluralStyle().
*
* @return array
* An array of test data:
* - translation value
* - expected result
*/
public function providerTestFixOldPluralStyle() {
return [
'non-plural translation' => ['@count[2] non-plural test', '@count[2] non-plural test'],
'plural translation' => ['@count[2] plural test' . PluralTranslatableMarkup::DELIMITER, '@count plural test'],
];
}
} }

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\locale\Unit; namespace Drupal\Tests\locale\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\locale\LocaleLookup; use Drupal\locale\LocaleLookup;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -266,4 +267,66 @@ class LocaleLookupTest extends UnitTestCase {
$this->assertTrue($locale_lookup->get('test')); $this->assertTrue($locale_lookup->get('test'));
} }
/**
* Tests locale lookups with old plural style of translations.
*
* @param array $translations
* The source with translations.
* @param string $langcode
* The language code of translation string.
* @param string $string
* The string for translation.
* @param bool $is_fix
* The flag about expected fix translation.
*
* @covers ::resolveCacheMiss
* @dataProvider providerFixOldPluralTranslationProvider
*/
public function testFixOldPluralStyleTranslations($translations, $langcode, $string, $is_fix) {
$this->storage->expects($this->any())
->method('findTranslation')
->will($this->returnCallback(function ($argument) use ($translations) {
if (isset($translations[$argument['language']][$argument['source']])) {
return (object) ['translation' => $translations[$argument['language']][$argument['source']]];
}
return TRUE;
}));
$this->languageManager->expects($this->any())
->method('getFallbackCandidates')
->will($this->returnCallback(function (array $context = []) {
switch ($context['langcode']) {
case 'by':
return ['ru'];
}
}));
$this->cache->expects($this->once())
->method('get')
->with('locale:' . $langcode . '::anonymous', FALSE);
$locale_lookup = new LocaleLookup($langcode, '', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
$this->assertSame($is_fix, strpos($locale_lookup->get($string), '@count[2]') === FALSE);
}
/**
* Provides test data for testResolveCacheMissWithFallback().
*/
public function providerFixOldPluralTranslationProvider() {
$translations = [
'by' => [
'word1' => '@count[2] word-by',
'word2' => implode(PluralTranslatableMarkup::DELIMITER, ['word-by', '@count[2] word-by']),
],
'ru' => [
'word3' => '@count[2] word-ru',
'word4' => implode(PluralTranslatableMarkup::DELIMITER, ['word-ru', '@count[2] word-ru']),
],
];
return [
'no-plural' => [$translations, 'by', 'word1', FALSE],
'no-plural from other language' => [$translations, 'by', 'word3', FALSE],
'plural' => [$translations, 'by', 'word2', TRUE],
'plural from other language' => [$translations, 'by', 'word4', TRUE],
];
}
} }