This class maintans a hierarchical relation between locales. For example, a locale "en_GB" will be a child of a locale "en".
 /**
  * @test
  */
 public function returnsNullWhenNoParentLocaleCouldBeFound()
 {
     foreach ($this->locales as $locale) {
         $this->localeCollection->addLocale($locale);
     }
     $this->assertNull($this->localeCollection->getParentLocaleOf(new I18n\Locale('sv')));
     $this->assertNull($this->localeCollection->getParentLocaleOf($this->locales[0]));
 }
 /**
  * Returns best-matching Locale object based on the template Locale object
  * provided as parameter. System default locale will be returned if no
  * successful matches were done.
  *
  * @param Locale $locale The template Locale object
  * @return Locale Best-matching existing Locale instance
  * @api
  */
 public function detectLocaleFromTemplateLocale(Locale $locale)
 {
     $bestMatchingLocale = $this->localeCollection->findBestMatchingLocale($locale);
     if ($bestMatchingLocale !== null) {
         return $bestMatchingLocale;
     }
     return $this->localizationService->getConfiguration()->getDefaultLocale();
 }
 /**
  * Finds all Locale objects representing locales available in the
  * Flow installation. This is done by scanning all Private and Public
  * resource files of all active packages, in order to find localized files.
  *
  * Localized files have a locale identifier added before their extension
  * (or at the end of filename, if no extension exists). For example, a
  * localized file for foobar.png, can be foobar.en.png, fobar.en_GB.png, etc.
  *
  * Just one localized resource file causes the corresponding locale to be
  * regarded as available (installed, supported).
  *
  * Note: result of this method invocation is cached
  *
  * @return void
  */
 protected function generateAvailableLocalesCollectionByScanningFilesystem()
 {
     $whitelistPaths = array_keys(array_filter((array) $this->settings['scan']['includePaths']));
     if ($whitelistPaths === []) {
         return;
     }
     $blacklistPattern = $this->getScanBlacklistPattern();
     /** @var PackageInterface $activePackage */
     foreach ($this->packageManager->getActivePackages() as $activePackage) {
         $packageResourcesPath = Files::getNormalizedPath($activePackage->getResourcesPath());
         if (!is_dir($packageResourcesPath)) {
             continue;
         }
         $directories = [];
         foreach ($whitelistPaths as $path) {
             $scanPath = Files::concatenatePaths(array($packageResourcesPath, $path));
             if (is_dir($scanPath)) {
                 array_push($directories, Files::getNormalizedPath($scanPath));
             }
         }
         while ($directories !== []) {
             $currentDirectory = array_pop($directories);
             $relativeDirectory = '/' . str_replace($packageResourcesPath, '', $currentDirectory);
             if ($blacklistPattern !== '' && preg_match($blacklistPattern, $relativeDirectory) === 1) {
                 continue;
             }
             if ($handle = opendir($currentDirectory)) {
                 while (false !== ($filename = readdir($handle))) {
                     if ($filename[0] === '.') {
                         continue;
                     }
                     $pathAndFilename = Files::concatenatePaths([$currentDirectory, $filename]);
                     if (is_dir($pathAndFilename)) {
                         array_push($directories, Files::getNormalizedPath($pathAndFilename));
                     } else {
                         $localeIdentifier = Utility::extractLocaleTagFromFilename($filename);
                         if ($localeIdentifier !== false) {
                             $this->localeCollection->addLocale(new Locale($localeIdentifier));
                         }
                     }
                 }
                 closedir($handle);
             }
         }
     }
 }