Issue #3403337 by alexpott, quietone, Akhil Babu, vakulrai, smustgrave, Gábor Hojtsy, penyaskito, longwave, catch: The order of the projects coming from locale_translation_get_projects() is not consistent
parent
1e82784398
commit
e7e4e0e776
|
@ -120,6 +120,9 @@ function hook_locale_translation_projects_alter(&$projects) {
|
||||||
'info' => [
|
'info' => [
|
||||||
'interface translation server pattern' => 'http://example.com/files/translations/%core/%project/%project-%version.%language.po',
|
'interface translation server pattern' => 'http://example.com/files/translations/%core/%project/%project-%version.%language.po',
|
||||||
],
|
],
|
||||||
|
// An optional key to change the order in which translation files are
|
||||||
|
// processed. By default, the projects are sorted alphabetically by key.
|
||||||
|
'weight' => 1,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,14 @@ class LocaleProjectStorage implements LocaleProjectStorageInterface {
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected static $all = FALSE;
|
protected bool $all = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorted status flag.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected bool $sorted = FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a State object.
|
* Constructs a State object.
|
||||||
|
@ -98,6 +105,7 @@ class LocaleProjectStorage implements LocaleProjectStorageInterface {
|
||||||
$this->cache[$key] = $value;
|
$this->cache[$key] = $value;
|
||||||
}
|
}
|
||||||
$this->keyValueStore->setMultiple($data);
|
$this->keyValueStore->setMultiple($data);
|
||||||
|
$this->sorted = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,7 +130,7 @@ class LocaleProjectStorage implements LocaleProjectStorageInterface {
|
||||||
*/
|
*/
|
||||||
public function resetCache() {
|
public function resetCache() {
|
||||||
$this->cache = [];
|
$this->cache = [];
|
||||||
static::$all = FALSE;
|
$this->sorted = $this->all = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,11 +167,26 @@ class LocaleProjectStorage implements LocaleProjectStorageInterface {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getAll() {
|
public function getAll() {
|
||||||
if (!static::$all) {
|
if (!$this->all) {
|
||||||
$this->cache = $this->keyValueStore->getAll();
|
$this->cache = $this->keyValueStore->getAll();
|
||||||
static::$all = TRUE;
|
$this->all = TRUE;
|
||||||
}
|
}
|
||||||
return $this->cache;
|
if (!$this->sorted) {
|
||||||
|
// Work around PHP 8.3.0 - 8.3.3 bug by assigning $this->cache to a local
|
||||||
|
// variable, see https://github.com/php/php-src/pull/13285.
|
||||||
|
$cache = $this->cache;
|
||||||
|
uksort($this->cache, function ($a, $b) use ($cache) {
|
||||||
|
// Sort by weight, if available, and then by key. This allows locale
|
||||||
|
// projects to set a weight, if required, and keeps the order consistent
|
||||||
|
// regardless of whether the list is built from code or retrieve from
|
||||||
|
// the database.
|
||||||
|
$sort = (int) ($cache[$a]['weight'] ?? 0) <=> (int) ($cache[$b]['weight'] ?? 0);
|
||||||
|
return $sort ?: strcmp($a, $b);
|
||||||
|
});
|
||||||
|
$this->sorted = TRUE;
|
||||||
|
}
|
||||||
|
// Remove any NULL values as these are not valid projects.
|
||||||
|
return array_filter($this->cache, fn ($value) => $value !== NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\Tests\locale\Unit;
|
||||||
|
|
||||||
|
use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
|
||||||
|
use Drupal\locale\LocaleProjectStorage;
|
||||||
|
use Drupal\Tests\UnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @coversDefaultClass \Drupal\locale\LocaleProjectStorage
|
||||||
|
* @group locale
|
||||||
|
* @runTestsInSeparateProcesses
|
||||||
|
*/
|
||||||
|
class LocaleProjectStorageTest extends UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\locale\LocaleProjectStorage
|
||||||
|
*/
|
||||||
|
private LocaleProjectStorage $projectStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\Core\KeyValueStore\KeyValueMemoryFactory
|
||||||
|
*/
|
||||||
|
private KeyValueMemoryFactory $keyValueMemoryFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
$this->keyValueMemoryFactory = new KeyValueMemoryFactory();
|
||||||
|
$this->projectStorage = new LocaleProjectStorage($this->keyValueMemoryFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that projects are sorted by weight and key.
|
||||||
|
*/
|
||||||
|
public function testSorting(): void {
|
||||||
|
// There are no projects.
|
||||||
|
$this->assertSame([], $this->projectStorage->getAll());
|
||||||
|
|
||||||
|
// Add project 'b'.
|
||||||
|
$this->projectStorage->set('b', ['name' => 'b']);
|
||||||
|
$this->assertSame(['b'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Add project 'c' and confirm alphabetical order.
|
||||||
|
$this->projectStorage->set('c', ['name' => 'c']);
|
||||||
|
$this->assertSame(['b', 'c'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Add project 'a' and confirm 'a' is first.
|
||||||
|
$this->projectStorage->set('a', ['name' => 'a']);
|
||||||
|
$this->assertSame(['a', 'b', 'c'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Add project 'd' with a negative weight and confirm 'd' is first.
|
||||||
|
$this->projectStorage->set('d', ['name' => 'd', 'weight' => -1]);
|
||||||
|
$this->assertSame(['d', 'a', 'b', 'c'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Add project 'aa' with a positive weight and confirm 'aa' is last.
|
||||||
|
$this->projectStorage->set('aa', ['name' => 'aa', 'weight' => 1]);
|
||||||
|
$this->assertSame(['d', 'a', 'b', 'c', 'aa'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Delete project 'a'.
|
||||||
|
$this->projectStorage->delete('a');
|
||||||
|
$this->assertSame(['d', 'b', 'c', 'aa'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Add project 'e' with a lower negative weight than 'd' and confirm 'e' is
|
||||||
|
// first.
|
||||||
|
$this->projectStorage->set('e', ['name' => 'e', 'weight' => -5]);
|
||||||
|
$this->assertSame(['e', 'd', 'b', 'c', 'aa'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Pretend there is a container rebuild by generating a new
|
||||||
|
// LocaleProjectStorage object with the same data.
|
||||||
|
$this->projectStorage = new LocaleProjectStorage($this->keyValueMemoryFactory);
|
||||||
|
$this->projectStorage->set('z', ['name' => 'z']);
|
||||||
|
$this->assertSame(['e', 'd', 'b', 'c', 'z', 'aa'], array_keys($this->projectStorage->getAll()));
|
||||||
|
|
||||||
|
// Now delete all projects.
|
||||||
|
$this->projectStorage->deleteAll();
|
||||||
|
$this->assertSame([], $this->projectStorage->getAll());
|
||||||
|
|
||||||
|
// Add project 'z' before project 'a' and confirm 'a' is first.
|
||||||
|
$this->projectStorage->set('z', ['name' => 'z']);
|
||||||
|
$this->projectStorage->set('a', ['name' => 'a']);
|
||||||
|
$this->assertSame(['a', 'z'], array_keys($this->projectStorage->getAll()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests deleted projects are not included in the count.
|
||||||
|
*/
|
||||||
|
public function testDelete(): void {
|
||||||
|
$this->projectStorage->set('b', ['name' => 'b']);
|
||||||
|
$this->assertSame(['name' => 'b'], $this->projectStorage->get('b'));
|
||||||
|
$this->assertSame(1, $this->projectStorage->countProjects());
|
||||||
|
$this->projectStorage->delete('b');
|
||||||
|
$this->assertNull($this->projectStorage->get('b'));
|
||||||
|
$this->assertSame(0, $this->projectStorage->countProjects());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue