1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
namespace Printful\GettextCms; |
5
|
|
|
|
6
|
|
|
|
7
|
|
|
use Gettext\Extractors\Extractor; |
8
|
|
|
use Gettext\Extractors\JsCode; |
9
|
|
|
use Gettext\Extractors\PhpCode; |
10
|
|
|
use Gettext\Extractors\VueJs; |
11
|
|
|
use Gettext\Translations; |
12
|
|
|
use League\Flysystem\Adapter\Local; |
13
|
|
|
use League\Flysystem\Filesystem; |
14
|
|
|
use League\Flysystem\Plugin\ListFiles; |
15
|
|
|
use Printful\GettextCms\Exceptions\GettextCmsException; |
16
|
|
|
use Printful\GettextCms\Exceptions\InvalidPathException; |
17
|
|
|
use Printful\GettextCms\Exceptions\UnknownExtractorException; |
18
|
|
|
use Printful\GettextCms\Structures\ScanItem; |
19
|
|
|
|
20
|
|
|
class MessageExtractor |
21
|
|
|
{ |
22
|
|
|
const EXTRACTORS = [ |
23
|
|
|
'js' => JsCode::class, |
24
|
|
|
'vue' => VueJs::class, |
25
|
|
|
'php' => PhpCode::class, |
26
|
|
|
]; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @param ScanItem[] $items |
30
|
|
|
* @param bool $defaultDomain Should we scan for translations in default domain (no domain messages) |
31
|
|
|
* @param array $otherDomains List of domains we should search (excluding the default domain |
32
|
|
|
* @return Translations[] List of translation files for each domain we wanted to extract |
33
|
|
|
* @throws GettextCmsException |
34
|
|
|
*/ |
35
|
|
|
public function extract(array $items, bool $defaultDomain = true, array $otherDomains = []) |
36
|
|
|
{ |
37
|
|
|
$pathnames = $this->resolvePathnames($items); |
38
|
|
|
|
39
|
|
|
if ($defaultDomain) { |
40
|
|
|
// If we search for default domain, add an empty domain string |
41
|
|
|
$otherDomains[] = ''; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
$translations = []; |
45
|
|
|
|
46
|
|
|
foreach ($otherDomains as $domain) { |
47
|
|
|
$translations[] = $this->extractForDomain($domain, $pathnames); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
return $translations; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @param string $domain |
55
|
|
|
* @param array $pathnames |
56
|
|
|
* @return Translations |
57
|
|
|
* @throws UnknownExtractorException |
58
|
|
|
*/ |
59
|
|
|
private function extractForDomain($domain, array $pathnames) |
60
|
|
|
{ |
61
|
|
|
$translations = new Translations; |
62
|
|
|
$translations->setDomain($domain); |
63
|
|
|
|
64
|
|
|
foreach ($pathnames as $pathname) { |
65
|
|
|
$this->extractFileMessages($pathname, $translations); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
return $translations; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @param $pathname |
73
|
|
|
* @param Translations $translations |
74
|
|
|
* @throws UnknownExtractorException |
75
|
|
|
*/ |
76
|
|
|
private function extractFileMessages($pathname, Translations $translations) |
77
|
|
|
{ |
78
|
|
|
$extractor = $this->getExtractor($pathname); |
79
|
|
|
|
80
|
|
|
$extractor::fromFile($pathname, $translations); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param ScanItem[] $items |
85
|
|
|
* @return array Pathnames to matching files |
86
|
|
|
*/ |
87
|
|
|
public function resolvePathnames(array $items): array |
88
|
|
|
{ |
89
|
|
|
return array_reduce($items, function (&$carry, ScanItem $item) { |
90
|
|
|
$carry = array_merge($carry, $this->resolveSingleItemFiles($item)); |
91
|
|
|
return $carry; |
92
|
|
|
}, []); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Returns a list of files that match this item |
97
|
|
|
* |
98
|
|
|
* @param ScanItem $item |
99
|
|
|
* @return array List of matching pathnames |
100
|
|
|
* @throws InvalidPathException |
101
|
|
|
*/ |
102
|
|
|
private function resolveSingleItemFiles(ScanItem $item): array |
103
|
|
|
{ |
104
|
|
|
if (is_file($item->path)) { |
105
|
|
|
return [$item->path]; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
if (!is_dir($item->path)) { |
109
|
|
|
throw new InvalidPathException('Path "' . $item->path . '" does not exist'); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
return $this->resolveDirectoryFiles($item); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @param ScanItem $item |
117
|
|
|
* @return string[] List of pathnames to files |
118
|
|
|
*/ |
119
|
|
|
private function resolveDirectoryFiles(ScanItem $item) |
120
|
|
|
{ |
121
|
|
|
$dir = realpath($item->path); |
122
|
|
|
|
123
|
|
|
$adapter = new Local($dir); |
124
|
|
|
$filesystem = new Filesystem($adapter); |
125
|
|
|
$filesystem->addPlugin(new ListFiles); |
126
|
|
|
|
127
|
|
|
$files = $filesystem->listFiles('', $item->recursive); |
128
|
|
|
|
129
|
|
|
// If extensions are set, filter other files out |
130
|
|
|
if ($item->extensions) { |
|
|
|
|
131
|
|
|
$files = array_filter($files, function ($file) use ($item) { |
132
|
|
|
return in_array($file['extension'], $item->extensions); |
133
|
|
|
}); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return array_map(function ($file) use ($dir) { |
137
|
|
|
return $dir . DIRECTORY_SEPARATOR . $file['path']; |
138
|
|
|
}, $files); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @param string $pathname Full path to file |
143
|
|
|
* @return Extractor |
144
|
|
|
* @throws UnknownExtractorException |
145
|
|
|
*/ |
146
|
|
|
private function getExtractor($pathname) |
147
|
|
|
{ |
148
|
|
|
$extension = pathinfo($pathname, PATHINFO_EXTENSION); |
149
|
|
|
|
150
|
|
|
if (isset(self::EXTRACTORS[$extension])) { |
151
|
|
|
return self::EXTRACTORS[$extension]; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
throw new UnknownExtractorException('Extractor is not know for file extension "' . $extension . '"'); |
155
|
|
|
} |
156
|
|
|
} |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.