diff --git a/core/lib/Drupal/Core/Language/LanguageInterface.php b/core/lib/Drupal/Core/Language/LanguageInterface.php index 2568cb33f303..9e9907a0cc8a 100644 --- a/core/lib/Drupal/Core/Language/LanguageInterface.php +++ b/core/lib/Drupal/Core/Language/LanguageInterface.php @@ -51,6 +51,13 @@ interface LanguageInterface { */ const LANGCODE_SITE_DEFAULT = 'site_default'; + /** + * A regex for validating language codes according to W3C specifications. + * + * @see https://www.w3.org/International/articles/language-tags/ + */ + const VALID_LANGCODE_REGEX = '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*'; + /** * The language state when referring to configurable languages. */ diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php index 327b763b184a..b4c67ed80e2a 100644 --- a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php +++ b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php @@ -5,6 +5,7 @@ namespace Drupal\Core\StringTranslation\Translator; use Drupal\Component\Gettext\PoStreamReader; use Drupal\Component\Gettext\PoMemoryWriter; use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Language\LanguageInterface; /** * File based string translation. @@ -102,7 +103,7 @@ class FileTranslation extends StaticTranslation { // The file name matches: drupal-[release version].[language code].po // When provided the $langcode is use as language code. If not provided all // language codes will match. - return '!drupal-[0-9a-z\.-]+\.' . (!empty($langcode) ? preg_quote($langcode, '!') : '[^\.]+') . '\.po$!'; + return '!drupal-[0-9]+\.[0-9]+\.([0-9]+|x)(-[a-z]+[0-9]*)?\.' . (!empty($langcode) ? preg_quote($langcode, '!') : LanguageInterface::VALID_LANGCODE_REGEX) . '\.po$!'; } /** diff --git a/core/modules/language/src/Form/LanguageFormBase.php b/core/modules/language/src/Form/LanguageFormBase.php index d30c4b692285..2c9663574788 100644 --- a/core/modules/language/src/Form/LanguageFormBase.php +++ b/core/modules/language/src/Form/LanguageFormBase.php @@ -95,7 +95,7 @@ abstract class LanguageFormBase extends EntityForm { */ public function validateCommon(array $form, FormStateInterface $form_state) { // Ensure sane field values for langcode and name. - if (!isset($form['langcode_view']) && !preg_match('@^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$@', $form_state->getValue('langcode'))) { + if (!isset($form['langcode_view']) && !preg_match('@^' . LanguageInterface::VALID_LANGCODE_REGEX . '$@', $form_state->getValue('langcode'))) { $form_state->setErrorByName('langcode', $this->t('%field must be a valid language tag as defined by the W3C.', [ '%field' => $form['langcode']['#title'], ':url' => 'http://www.w3.org/International/articles/language-tags/', diff --git a/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php b/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php index c7161494fd20..71f542839ef6 100644 --- a/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php +++ b/core/tests/Drupal/KernelTests/Core/Installer/InstallerLanguageTest.php @@ -20,8 +20,9 @@ class InstallerLanguageTest extends KernelTestBase { // Different translation files would be found depending on which language // we are looking for. $expected_translation_files = [ - NULL => ['drupal-8.0.0-beta2.hu.po', 'drupal-8.0.0.de.po'], + NULL => ['drupal-8.0.0-beta2.hu.po', 'drupal-8.0.0.de.po', 'drupal-8.0.x.fr-CA.po'], 'de' => ['drupal-8.0.0.de.po'], + 'fr-CA' => ['drupal-8.0.x.fr-CA.po'], 'hu' => ['drupal-8.0.0-beta2.hu.po'], 'it' => [], ]; diff --git a/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po b/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po new file mode 100644 index 000000000000..e2106954b8b4 --- /dev/null +++ b/core/tests/fixtures/files/translations/drupal-8.0.0.fr____CA.po @@ -0,0 +1,4 @@ +# This file exists to prove that +# \Drupal\Core\StringTranslation\Translator\FileTranslation::findTranslationFiles() +# does not find it. See +# \Drupal\KernelTests\Core\Installer\InstallerLanguageTest::testInstallerTranslationFiles() diff --git a/core/tests/fixtures/files/translations/drupal-8.0.x.fr-CA.po b/core/tests/fixtures/files/translations/drupal-8.0.x.fr-CA.po new file mode 100644 index 000000000000..e69de29bb2d1