Commit 38587bde authored by webchick's avatar webchick

Issue #2240459 by Gábor Hojtsy, penyaskito | LoMo: Fixed Applying default...

Issue #2240459 by Gábor Hojtsy, penyaskito | LoMo: Fixed Applying default language fallback rules to interface translation has impressively bad results.
parent 7ad79095
......@@ -773,8 +773,8 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N
// Retrieve language fallback candidates to perform the entity language
// negotiation.
$context['data'] = $entity;
$context += array('operation' => 'entity_view');
$candidates = $this->languageManager->getFallbackCandidates($langcode, $context);
$context += array('operation' => 'entity_view', 'langcode' => $langcode);
$candidates = $this->languageManager->getFallbackCandidates($context);
// Ensure the default language has the proper language code.
$default_language = $entity->getUntranslated()->language();
......
......@@ -194,7 +194,7 @@ public function isLanguageLocked($langcode) {
/**
* {@inheritdoc}
*/
public function getFallbackCandidates($langcode = NULL, array $context = array()) {
public function getFallbackCandidates(array $context = array()) {
return array(LanguageInterface::LANGCODE_DEFAULT);
}
......
......@@ -132,22 +132,27 @@ public function isLanguageLocked($langcode);
/**
* Returns the language fallback candidates for a given context.
*
* @param string $langcode
* (optional) The language of the current context. Defaults to NULL.
* @param array $context
* (optional) An associative array of data that can be useful to determine
* the fallback sequence. The following keys are used in core:
* - langcode: The desired language.
* - langcode: Language code of the desired language.
* - operation: The name of the operation indicating the context where
* language fallback is being applied, e.g. 'entity_view'.
* - data: An arbitrary data structure that makes sense in the provided
* context, e.g. an entity.
* language fallback is being applied. The following operations are
* defined in core, but more may be defined in contributed modules:
* - entity_view: Invoked when an entity is about to be displayed.
* The data key contains the loaded entity.
* - views_query: Invoked when a field based views query is performed.
* The data key contains a reference to the field object.
* - locale_lookup: Invoked when a string translation was not found.
* The data key contains the source string.
* - data: A data structure that makes sense in the provided
* context, see above.
*
* @return array
* An array of language codes sorted by priority: first values should be
* tried first.
*/
public function getFallbackCandidates($langcode = NULL, array $context = array());
public function getFallbackCandidates(array $context = array());
/**
* Returns the language switch links for the given language type.
......
......@@ -305,7 +305,7 @@ public function query($use_groupby = FALSE) {
$this->view->display_handler->options['field_langcode']
);
$placeholder = $this->placeholder();
$langcode_fallback_candidates = $this->languageManager->getFallbackCandidates($langcode, array('operation' => 'views_query', 'data' => $this));
$langcode_fallback_candidates = $this->languageManager->getFallbackCandidates(array('langcode' => $langcode, 'operation' => 'views_query', 'data' => $this));
$this->query->addWhereExpression(0, "$column IN($placeholder) OR $column IS NULL", array($placeholder => $langcode_fallback_candidates));
}
}
......
......@@ -326,17 +326,24 @@ public function updateLockedLanguageWeights() {
/**
* {@inheritdoc}
*/
public function getFallbackCandidates($langcode = NULL, array $context = array()) {
public function getFallbackCandidates(array $context = array()) {
if ($this->isMultilingual()) {
// Get languages ordered by weight, add BaseLanguageInterface::LANGCODE_NOT_SPECIFIED
// at the end.
$candidates = array_keys($this->getLanguages());
$candidates[] = BaseLanguageInterface::LANGCODE_NOT_SPECIFIED;
$candidates = array_combine($candidates, $candidates);
// The first candidate should always be the desired language if specified.
if (!empty($langcode)) {
$candidates = array($langcode => $langcode) + $candidates;
$candidates = array();
if (empty($context['operation']) || $context['operation'] != 'locale_lookup') {
// If the fallback context is not locale_lookup, initialize the
// candidates with languages ordered by weight and add
// BaseLanguageInterface::LANGCODE_NOT_SPECIFIED at the end. Interface
// translation fallback should only be based on explicit configuration
// gathered via the alter hooks below.
$candidates = array_keys($this->getLanguages());
$candidates[] = BaseLanguageInterface::LANGCODE_NOT_SPECIFIED;
$candidates = array_combine($candidates, $candidates);
// The first candidate should always be the desired language if
// specified.
if (!empty($context['langcode'])) {
$candidates = array($context['langcode'] => $context['langcode']) + $candidates;
}
}
// Let other modules hook in and add/change candidates.
......@@ -349,7 +356,7 @@ public function getFallbackCandidates($langcode = NULL, array $context = array()
$this->moduleHandler->alter($types, $candidates, $context);
}
else {
$candidates = parent::getFallbackCandidates($langcode, $context);
$candidates = parent::getFallbackCandidates($context);
}
return $candidates;
......
......@@ -56,7 +56,7 @@ public function testCandidates() {
$this->state->set('language_test.fallback_operation_alter.candidates', TRUE);
$expected[] = LanguageInterface::LANGCODE_NOT_SPECIFIED;
$expected[] = LanguageInterface::LANGCODE_NOT_APPLICABLE;
$candidates = $this->languageManager->getFallbackCandidates(NULL, array('operation' => 'test'));
$candidates = $this->languageManager->getFallbackCandidates(array('operation' => 'test'));
$this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are alterable for specific operations.');
// Check that when the site is monolingual no language fallback is applied.
......
......@@ -127,7 +127,7 @@ protected function resolveCacheMiss($offset) {
// If there is no translation available for the current language then use
// language fallback to try other translations.
if ($value === TRUE) {
$fallbacks = $this->languageManager->getFallbackCandidates($this->langcode, array('operation' => 'locale_lookup', 'data' => $offset));
$fallbacks = $this->languageManager->getFallbackCandidates(array('langcode' => $this->langcode, 'operation' => 'locale_lookup', 'data' => $offset));
if (!empty($fallbacks)) {
foreach ($fallbacks as $langcode) {
$translation = $this->stringStorage->findTranslation(array(
......
......@@ -11,7 +11,7 @@
use Drupal\simpletest\WebTestBase;
/**
* Tests that LocaleLookup does not cause circular references.
* Tests LocaleLookup.
*
* @group locale
*/
......@@ -22,12 +22,14 @@ class LocaleLocaleLookupTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('locale');
public static $modules = array('locale', 'locale_test');
/**
* Tests hasTranslation().
* {@inheritdoc}
*/
public function testCircularDependency() {
public function setUp() {
parent::setUp();
// Change the language default object to different values.
$new_language_default = new Language(array(
'id' => 'fr',
......@@ -39,9 +41,29 @@ public function testCircularDependency() {
));
language_save($new_language_default);
$this->drupalLogin($this->root_user);
}
/**
* Tests that there are no circular dependencies.
*/
public function testCircularDependency() {
// Ensure that we can enable early_translation_test on a non-english site.
$this->drupalPostForm('admin/modules', array('modules[Testing][early_translation_test][enable]' => TRUE), t('Save configuration'));
$this->assertResponse(200);
}
/**
* Test language fallback defaults.
*/
public function testLanguageFallbackDefaults() {
$this->drupalGet('');
// Ensure state of fallback languages persisted by
// locale_test_language_fallback_candidates_locale_lookup_alter() is empty.
$this->assertEqual(\Drupal::state()->get('locale.test_language_fallback_candidates_locale_lookup_alter_candidates'), array());
// Make sure there is enough information provided for alter hooks.
$context = \Drupal::state()->get('locale.test_language_fallback_candidates_locale_lookup_alter_context');
$this->assertEqual($context['langcode'], 'fr');
$this->assertEqual($context['operation'], 'locale_lookup');
}
}
......@@ -129,3 +129,11 @@ function locale_test_locale_translation_projects_alter(&$projects) {
);
}
}
/**
* Implements hook_language_fallback_candidates_OPERATION_alter().
*/
function locale_test_language_fallback_candidates_locale_lookup_alter(array &$candidates, array $context) {
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_candidates', $candidates);
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_context', $context);
}
......@@ -158,8 +158,8 @@ public function testResolveCacheMissWithFallback($langcode, $string, $context, $
$this->languageManager->expects($this->any())
->method('getFallbackCandidates')
->will($this->returnCallback(function ($langcode) {
switch ($langcode) {
->will($this->returnCallback(function (array $context = array()) {
switch ($context['langcode']) {
case 'pl':
return array('cs', 'en');
......
......@@ -925,10 +925,10 @@ public function testGetTranslationFromContext() {
$this->languageManager->expects($this->exactly(2))
->method('getFallbackCandidates')
->will($this->returnCallback(function ($langcode = NULL, array $context = array()) {
->will($this->returnCallback(function (array $context = array()) {
$candidates = array();
if ($langcode) {
$candidates[$langcode] = $langcode;
if (!empty($context['langcode'])) {
$candidates[$context['langcode']] = $context['langcode'];
}
return $candidates;
}));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment