123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- <?php
- namespace Drupal\locale;
- use Drupal\Component\Gettext\PoHeader;
- use Drupal\Component\Gettext\PoItem;
- use Drupal\Component\Gettext\PoReaderInterface;
- use Drupal\Component\Gettext\PoWriterInterface;
- /**
- * Gettext PO writer working with the locale module database.
- */
- class PoDatabaseWriter implements PoWriterInterface {
- /**
- * An associative array indicating what data should be overwritten, if any.
- *
- * Elements of the array:
- * - override_options
- * - not_customized: boolean indicating that not customized strings should
- * be overwritten.
- * - customized: boolean indicating that customized strings should be
- * overwritten.
- * - customized: the strings being imported should be saved as customized.
- * One of LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
- *
- * @var array
- */
- private $options;
- /**
- * Language code of the language being written to the database.
- *
- * @var string
- */
- private $langcode;
- /**
- * Header of the po file written to the database.
- *
- * @var \Drupal\Component\Gettext\PoHeader
- */
- private $header;
- /**
- * Associative array summarizing the number of changes done.
- *
- * Keys for the array:
- * - additions: number of source strings newly added
- * - updates: number of translations updated
- * - deletes: number of translations deleted
- * - skips: number of strings skipped due to disallowed HTML
- *
- * @var array
- */
- private $report;
- /**
- * Constructor, initialize reporting array.
- */
- public function __construct() {
- $this->setReport();
- }
- /**
- * {@inheritdoc}
- */
- public function getLangcode() {
- return $this->langcode;
- }
- /**
- * {@inheritdoc}
- */
- public function setLangcode($langcode) {
- $this->langcode = $langcode;
- }
- /**
- * Get the report of the write operations.
- */
- public function getReport() {
- return $this->report;
- }
- /**
- * Set the report array of write operations.
- *
- * @param array $report
- * Associative array with result information.
- */
- public function setReport($report = []) {
- $report += [
- 'additions' => 0,
- 'updates' => 0,
- 'deletes' => 0,
- 'skips' => 0,
- 'strings' => [],
- ];
- $this->report = $report;
- }
- /**
- * Get the options used by the writer.
- */
- public function getOptions() {
- return $this->options;
- }
- /**
- * Set the options for the current writer.
- */
- public function setOptions(array $options) {
- if (!isset($options['overwrite_options'])) {
- $options['overwrite_options'] = [];
- }
- $options['overwrite_options'] += [
- 'not_customized' => FALSE,
- 'customized' => FALSE,
- ];
- $options += [
- 'customized' => LOCALE_NOT_CUSTOMIZED,
- ];
- $this->options = $options;
- }
- /**
- * {@inheritdoc}
- */
- public function getHeader() {
- return $this->header;
- }
- /**
- * Implements Drupal\Component\Gettext\PoMetadataInterface::setHeader().
- *
- * Sets the header and configure Drupal accordingly.
- *
- * Before being able to process the given header we need to know in what
- * context this database write is done. For this the options must be set.
- *
- * A langcode is required to set the current header's PluralForm.
- *
- * @param \Drupal\Component\Gettext\PoHeader $header
- * Header metadata.
- *
- * @throws Exception
- */
- public function setHeader(PoHeader $header) {
- $this->header = $header;
- $locale_plurals = \Drupal::state()->get('locale.translation.plurals') ?: [];
- // Check for options.
- $options = $this->getOptions();
- if (empty($options)) {
- throw new \Exception('Options should be set before assigning a PoHeader.');
- }
- $overwrite_options = $options['overwrite_options'];
- // Check for langcode.
- $langcode = $this->langcode;
- if (empty($langcode)) {
- throw new \Exception('Langcode should be set before assigning a PoHeader.');
- }
- if (array_sum($overwrite_options) || empty($locale_plurals[$langcode]['plurals'])) {
- // Get and store the plural formula if available.
- $plural = $header->getPluralForms();
- if (isset($plural) && $p = $header->parsePluralForms($plural)) {
- list($nplurals, $formula) = $p;
- \Drupal::service('locale.plural.formula')->setPluralFormula($langcode, $nplurals, $formula);
- }
- }
- }
- /**
- * {@inheritdoc}
- */
- public function writeItem(PoItem $item) {
- if ($item->isPlural()) {
- $item->setSource(implode(LOCALE_PLURAL_DELIMITER, $item->getSource()));
- $item->setTranslation(implode(LOCALE_PLURAL_DELIMITER, $item->getTranslation()));
- }
- $this->importString($item);
- }
- /**
- * {@inheritdoc}
- */
- public function writeItems(PoReaderInterface $reader, $count = -1) {
- $forever = $count == -1;
- while (($count-- > 0 || $forever) && ($item = $reader->readItem())) {
- $this->writeItem($item);
- }
- }
- /**
- * Imports one string into the database.
- *
- * @param \Drupal\Component\Gettext\PoItem $item
- * The item being imported.
- *
- * @return int
- * The string ID of the existing string modified or the new string added.
- */
- private function importString(PoItem $item) {
- // Initialize overwrite options if not set.
- $this->options['overwrite_options'] += [
- 'not_customized' => FALSE,
- 'customized' => FALSE,
- ];
- $overwrite_options = $this->options['overwrite_options'];
- $customized = $this->options['customized'];
- $context = $item->getContext();
- $source = $item->getSource();
- $translation = $item->getTranslation();
- // Look up the source string and any existing translation.
- $strings = \Drupal::service('locale.storage')->getTranslations([
- 'language' => $this->langcode,
- 'source' => $source,
- 'context' => $context,
- ]);
- $string = reset($strings);
- if (!empty($translation)) {
- // Skip this string unless it passes a check for dangerous code.
- if (!locale_string_is_safe($translation)) {
- \Drupal::logger('locale')->error('Import of string "%string" was skipped because of disallowed or malformed HTML.', ['%string' => $translation]);
- $this->report['skips']++;
- return 0;
- }
- elseif ($string) {
- $string->setString($translation);
- if ($string->isNew()) {
- // No translation in this language.
- $string->setValues([
- 'language' => $this->langcode,
- 'customized' => $customized,
- ]);
- $string->save();
- $this->report['additions']++;
- }
- elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
- // Translation exists, only overwrite if instructed.
- $string->customized = $customized;
- $string->save();
- $this->report['updates']++;
- }
- $this->report['strings'][] = $string->getId();
- return $string->lid;
- }
- else {
- // No such source string in the database yet.
- $string = \Drupal::service('locale.storage')->createString(['source' => $source, 'context' => $context])
- ->save();
- \Drupal::service('locale.storage')->createTranslation([
- 'lid' => $string->getId(),
- 'language' => $this->langcode,
- 'translation' => $translation,
- 'customized' => $customized,
- ])->save();
- $this->report['additions']++;
- $this->report['strings'][] = $string->getId();
- return $string->lid;
- }
- }
- elseif ($string && !$string->isNew() && $overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
- // Empty translation, remove existing if instructed.
- $string->delete();
- $this->report['deletes']++;
- $this->report['strings'][] = $string->lid;
- return $string->lid;
- }
- }
- }
|