[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/_test/tests/inc/ -> lang.php (source)

   1  <?php
   2  
   3  /**
   4   * Language file tests inspired by the script by schplurtz
   5   * @link https://www.dokuwiki.org/teams:i18n:translation-check
   6   */
   7  class lang_test extends DokuWikiTest
   8  {
   9      /**
  10       * returen all languages except english
  11       *
  12       * @return string[]
  13       */
  14      protected function findLanguages()
  15      {
  16          $languages = glob(DOKU_INC . 'inc/lang/*', GLOB_ONLYDIR);
  17          $languages = array_map('basename', $languages);
  18          $languages = array_filter($languages, function ($in) {
  19              return $in !== 'en';
  20          });
  21          return $languages;
  22      }
  23  
  24      /**
  25       * Get all installed plugins
  26       *
  27       * This finds all things that might be a plugin and does not care for enabled or not.
  28       *
  29       * @return string[]
  30       */
  31      protected function findPlugins()
  32      {
  33          $plugins = glob(DOKU_INC . 'lib/plugins/*', GLOB_ONLYDIR);
  34          return $plugins;
  35      }
  36  
  37      /**
  38       * Get all installed templates
  39       *
  40       * This finds all things that might be a template and does not care for enabled or not.
  41       *
  42       * @return string[]
  43       */
  44      protected function findTemplates()
  45      {
  46          $templates = glob(DOKU_INC . 'lib/tpl/*', GLOB_ONLYDIR);
  47          return $templates;
  48      }
  49  
  50      /**
  51       * Load the strings for the given language
  52       *
  53       * @param string $lang
  54       * @return array
  55       */
  56      protected function loadLanguage($file)
  57      {
  58          $lang = [];
  59          if (file_exists($file)) {
  60              include $file;
  61          }
  62          return $lang;
  63      }
  64  
  65      /**
  66       * Provide all the language files to compare
  67       *
  68       * @return Generator
  69       */
  70      public function provideLanguageFiles()
  71      {
  72          $bases = array_merge(
  73              [DOKU_INC . 'inc'],
  74              $this->findPlugins(),
  75              $this->findTemplates()
  76          );
  77  
  78          foreach ($this->findLanguages() as $code) {
  79              foreach ($bases as $base) {
  80                  foreach (['lang.php', 'settings.php'] as $file) {
  81                      $englishFile = "$base/lang/en/$file";
  82                      $foreignFile = "$base/lang/$code/$file";
  83                      $name = substr($foreignFile, strlen(DOKU_INC));
  84                      $name = '…'.substr($name, -35);
  85  
  86                      if (file_exists($foreignFile)) {
  87                          yield ([
  88                              $this->loadLanguage($englishFile),
  89                              $this->loadLanguage($foreignFile),
  90                              $code,
  91                              $name,
  92                          ]);
  93                      }
  94                  }
  95              }
  96          }
  97      }
  98  
  99      /**
 100       * Check for obsolete language strings
 101       *
 102       * @param array $english key/value language pairs for English
 103       * @param array $foreign key/value language pairs for the foreign language
 104       * @param string $code language code of the foreign file
 105       * @param string $file the base file name the foreign keys came from
 106       * @param string $prefix sub key that is currently checked (used in recursion)
 107       * @dataProvider provideLanguageFiles
 108       */
 109      public function testObsolete($english, $foreign, $code, $file, $prefix = '')
 110      {
 111          $this->assertGreaterThan(0, count($foreign), "$file exists but has no translations");
 112  
 113          foreach ($foreign as $key => $value) {
 114              $name = $prefix ? $prefix . $key : $key;
 115              $this->assertArrayHasKey($key, $english, "$file: obsolete/unknown key '$name'");
 116  
 117              // sub arrays as for the js translations:
 118              if (is_array($value) && is_array($english[$key])) {
 119                  $this->testObsolete($english[$key], $value, $code, $file, $key);
 120              }
 121          }
 122      }
 123  
 124      /**
 125       * Check for sprintf format placeholder equality
 126       *
 127       * @param array $english key/value language pairs for English
 128       * @param array $foreign key/value language pairs for the foreign language
 129       * @param string $code language code of the foreign file
 130       * @param string $file the base file name the foreign keys came from
 131       * @param string $prefix sub key that is currently checked (used in recursion)
 132       * @dataProvider provideLanguageFiles
 133       */
 134      public function testPlaceholders($english, $foreign, $code, $file, $prefix = '')
 135      {
 136          $this->assertGreaterThan(0, count($foreign), "$file exists but has no translations");
 137  
 138          foreach ($foreign as $key => $value) {
 139              // non existing in english is skipped here, that what testObsolete checks
 140              if (!isset($english[$key])) continue;
 141  
 142              // sub arrays as for the js translations:
 143              if (is_array($value) && is_array($english[$key])) {
 144                  $this->testPlaceholders($english[$key], $value, $code, $file, $key);
 145                  return;
 146              }
 147  
 148              $name = $prefix ? $prefix . $key : $key;
 149  
 150              $englishPlaceholders = $this->parsePlaceholders($english[$key]);
 151              $foreignPlaceholders = $this->parsePlaceholders($value);
 152              $countEnglish = count($englishPlaceholders);
 153              $countForeign = count($foreignPlaceholders);
 154  
 155              $this->assertEquals($countEnglish, $countForeign,
 156                  join("\n",
 157                      [
 158                          "$file: unequal amount of sprintf format placeholders in '$name'",
 159                          "en: '" . $english[$key] . "'",
 160                          "$code: '$value'",
 161                      ]
 162                  )
 163              );
 164  
 165              $this->assertEquals($englishPlaceholders, $foreignPlaceholders,
 166                  join("\n",
 167                      [
 168                          "$file: sprintf format mismatch in '$name'",
 169                          "en: '" . $english[$key] . "'",
 170                          "$code: '$value'",
 171                      ]
 172                  )
 173              );
 174          }
 175      }
 176  
 177      /**
 178       * Parses the placeholders from a string and brings them in the correct order
 179       *
 180       * This has its own test below.
 181       *
 182       * @param string $string
 183       */
 184      protected function parsePlaceholders($string)
 185      {
 186          if (!preg_match_all('/%(?:([0-9]+)\$)?([-.0-9hl]*?[%dufsc])/', $string, $matches, PREG_SET_ORDER)) {
 187              return [];
 188          }
 189  
 190          // Given this string : 'schproutch %2$s with %1$04d in %-20s plouf'
 191          // we have this in $matches:
 192          // [
 193          //     0 => ['%2$s', 2, 's'],
 194          //     1 => ['%1$04d', 1, '04d'],
 195          //     2 => ['%-20s', '', '-20s'],
 196          // ]
 197  
 198          // sort by the given sorting in key 1
 199          usort($matches, function ($a, $b) {
 200              if ($a[1] === $b[1]) return 0; // keep as is
 201  
 202              // sort empties towards the back
 203              if ($a[1] === '') $a[1] = 9999;
 204              if ($b[1] === '') $b[1] = 9999;
 205  
 206              // compare sort numbers
 207              if ((int)$a[1] < (int)$b[1]) return -1;
 208              if ((int)$a[1] > (int)$b[1]) return 1;
 209              return 0;
 210          });
 211  
 212          // return values in key 2
 213          return array_column($matches, 2);
 214      }
 215  
 216      /**
 217       * Dataprovider for the parsePlaceholder test
 218       * @return array[]
 219       */
 220      public function providePlaceholders()
 221      {
 222          return [
 223              ['schproutch %2$s with %1$04d in %-20s plouf', ['04d', 's', '-20s']],
 224          ];
 225      }
 226  
 227      /**
 228       * Test the parsePlaceholder utility function above
 229       *
 230       * @param string $input
 231       * @param array $expected
 232       * @dataProvider providePlaceholders
 233       */
 234      public function testParsePlaceholders($input, $expected)
 235      {
 236          $this->assertEquals($expected, $this->parsePlaceholders($input));
 237      }
 238  }