class LocaleFileManager

Provide Locale File helper methods.

Hierarchy

Expanded class hierarchy of LocaleFileManager

6 files declare their use of LocaleFileManager
install.core.inc in core/includes/install.core.inc
API functions for installing Drupal.
locale.batch.inc in core/modules/locale/locale.batch.inc
locale.bulk.inc in core/modules/locale/locale.bulk.inc
locale.module in core/modules/locale/locale.module
LocaleHooks.php in core/modules/locale/src/Hook/LocaleHooks.php

... See full list

File

core/modules/locale/src/File/LocaleFileManager.php, line 28

Namespace

Drupal\locale\File
View source
class LocaleFileManager {
  use StringTranslationTrait;
  public function __construct(protected readonly LocaleProjectStorageInterface $localeProjectStorage, protected readonly FileSystemInterface $fileSystem, protected readonly ClientFactory $clientFactory, protected readonly ClientInterface $httpClient, protected readonly LoggerChannelFactoryInterface $loggerFactory, protected readonly MessengerInterface $messenger) {
  }
  
  /**
   * Get interface translation files present in the translations directory.
   *
   * @param array $projects
   *   (optional) Project names from which to get the translation files and
   *   history. Defaults to all projects.
   * @param array $langcodes
   *   (optional) Language codes from which to get the translation files and
   *   history. Defaults to all languages.
   *
   * @return array
   *   An array of interface translation files keyed by their URI.
   */
  public function getInterfaceTranslationFiles(array $projects = [], array $langcodes = []) : array {
    \Drupal::moduleHandler()->loadInclude('locale', 'inc', 'locale.compare');
    $files = [];
    $projects = $projects ?: array_keys($this->localeProjectStorage
      ->getProjects());
    $langcodes = $langcodes ?: array_keys(locale_translatable_language_list());
    // Scan the translations directory for files matching a name pattern
    // containing a project name and language code: {project}.{langcode}.po or
    // {project}-{version}.{langcode}.po.
    // Only files of known projects and languages will be returned.
    $directory = TranslationsStream::basePath();
    $result = [];
    if (is_dir($directory)) {
      $result = $this->fileSystem
        ->scanDirectory($directory, '![a-z_]+(\\-[0-9a-z\\.\\-\\+]+|)\\.[^\\./]+\\.po$!', [
        'recurse' => FALSE,
      ]);
    }
    foreach ($result as $file) {
      // Recreate file object, detect the project name and version from the file
      // name.
      $file = LocaleFile::createFromPath($file->filename, $file->uri);
      if (in_array($file->project, $projects) && in_array($file->langcode, $langcodes)) {
        $files[$file->uri] = $file;
      }
    }
    return $files;
  }
  
  /**
   * Deletes interface translation files and translation history records.
   *
   * @param array $projects
   *   (optional) Project names from which to delete the translation files and
   *   history. Defaults to all projects.
   * @param array $langcodes
   *   (optional) Language codes from which to delete the translation files and
   *   history. Defaults to all languages.
   *
   * @return bool
   *   TRUE if files are removed successfully. FALSE if one or more files could
   *   not be deleted.
   */
  public function deleteTranslationFiles(array $projects = [], array $langcodes = []) : bool {
    $fail = FALSE;
    locale_translation_file_history_delete($projects, $langcodes);
    // Delete all translation files from the translations directory.
    if ($files = $this->getInterfaceTranslationFiles($projects, $langcodes)) {
      foreach ($files as $file) {
        try {
          $this->fileSystem
            ->delete($file->uri);
        } catch (FileException) {
          $fail = TRUE;
        }
      }
    }
    return !$fail;
  }
  
  /**
   * Check if remote file exists and when it was last updated.
   *
   * @param string $uri
   *   URI of remote file.
   *
   * @return \Drupal\locale\File\RemoteFileInfo
   *   RemoteFileInfo value object.
   */
  public function checkRemoteFileStatus(string $uri) : RemoteFileInfo {
    $logger = $this->loggerFactory
      ->get('locale');
    $remoteFileInfo = new RemoteFileInfo();
    try {
      $actual_uri = NULL;
      $response = $this->clientFactory
        ->fromOptions([
        'allow_redirects' => [
          'on_redirect' => function (RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) {
            $actual_uri = (string) $request_uri;
          },
        ],
      ])
        ->head($uri);
      // Return the effective URL if it differs from the requested.
      if ($actual_uri && $actual_uri !== $uri) {
        $remoteFileInfo->location = $actual_uri;
      }
      $remoteFileInfo->lastModified = $response->hasHeader('Last-Modified') ? strtotime($response->getHeaderLine('Last-Modified')) : 0;
      $remoteFileInfo->status = RemoteFileStatus::Success;
      return $remoteFileInfo;
    } catch (RequestException $e) {
      // Handle 4xx and 5xx http responses.
      $response = $e->getResponse();
      if ($response) {
        if ($response->getStatusCode() == 404) {
          // File not found occurs when a translation file is not yet available
          // at the translation server. But also if a custom module or custom
          // theme does not define the location of a translation file. By
          // default the file is checked at the translation server, but it will
          // not be found there.
          $logger->notice('Translation file not found: @uri.', [
            '@uri' => $uri,
          ]);
          $remoteFileInfo->status = RemoteFileStatus::Missing;
          return $remoteFileInfo;
        }
        $logger->notice('HTTP request to @url failed with error: @error.', [
          '@url' => $uri,
          '@error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase(),
        ]);
      }
    } catch (ConnectException $e) {
      $logger->notice('HTTP request to @url failed with error: @error.', [
        '@url' => $uri,
        '@error' => $e->getMessage(),
      ]);
    }
    $remoteFileInfo->status = RemoteFileStatus::Error;
    return $remoteFileInfo;
  }
  
  /**
   * Downloads a translation file from a remote server.
   *
   * @param LocaleFile $source_file
   *   Source file object with at least:
   *   - "uri": uri to download the file from.
   *   - "project": Project name.
   *   - "langcode": Translation language.
   *   - "version": Project version.
   *   - "filename": File name.
   * @param string $directory
   *   Directory where the downloaded file will be saved. Defaults to the
   *   temporary file path.
   *
   * @return LocaleFile|false
   *   File object if download was successful. FALSE on failure.
   */
  public function downloadTranslationSource(LocaleFile $source_file, string $directory = 'translations://') : LocaleFile|false {
    try {
      $data = (string) $this->httpClient
        ->request('get', $source_file->uri)
        ->getBody();
      $filename = basename($source_file->uri);
      if ($uri = $this->fileSystem
        ->saveData($data, $directory . $filename, FileExists::Replace)) {
        $hash = hash_file(LocaleSource::LOCAL_FILE_HASH_ALGO, $uri);
        $langcode = $source_file->langcode ?? LanguageInterface::LANGCODE_NOT_SPECIFIED;
        $project = $source_file->project ?? NULL;
        $version = $source_file->version ?? NULL;
        $file = new LocaleFile($filename, $uri, $hash, filemtime($uri), $langcode, $project, $version);
        $file->type = LOCALE_TRANSLATION_LOCAL;
        $file->directory = $directory;
        return $file;
      }
    } catch (ClientExceptionInterface $exception) {
      $this->messenger
        ->addError($this->t('Failed to fetch file due to error "%error"', [
        '%error' => $exception->getMessage(),
      ]));
    } catch (FileException|InvalidStreamWrapperException $e) {
      $this->messenger
        ->addError($this->t('Failed to save file due to error "%error"', [
        '%error' => $e->getMessage(),
      ]));
    }
    $this->loggerFactory
      ->get('locale')
      ->error('Unable to download translation file @uri.', [
      '@uri' => $source_file->uri,
    ]);
    return FALSE;
  }

}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.