|
12 | 12 | namespace Symfony\Component\Translation\Bridge\Lokalise; |
13 | 13 |
|
14 | 14 | use Psr\Log\LoggerInterface; |
| 15 | +use Symfony\Component\Translation\Exception\LogicException; |
15 | 16 | use Symfony\Component\Translation\Exception\ProviderException; |
16 | 17 | use Symfony\Component\Translation\Loader\LoaderInterface; |
17 | 18 | use Symfony\Component\Translation\MessageCatalogueInterface; |
@@ -96,7 +97,11 @@ public function write(TranslatorBagInterface $translatorBag): void |
96 | 97 | public function read(array $domains, array $locales): TranslatorBag |
97 | 98 | { |
98 | 99 | $translatorBag = new TranslatorBag(); |
99 | | - $translations = $this->exportFiles($locales, $domains); |
| 100 | + if (\extension_loaded('zip')) { |
| 101 | + $translations = $this->exportFilesAsync($locales, $domains); |
| 102 | + } else { |
| 103 | + $translations = $this->exportFiles($locales, $domains); |
| 104 | + } |
100 | 105 |
|
101 | 106 | foreach ($translations as $locale => $files) { |
102 | 107 | foreach ($files as $filename => $content) { |
@@ -176,6 +181,109 @@ private function exportFiles(array $locales, array $domains): array |
176 | 181 | return array_combine($reformattedLanguages, $responseContent['files']); |
177 | 182 | } |
178 | 183 |
|
| 184 | + /** |
| 185 | + * @see https://app.lokalise.com/api2docs/curl/#transition-download-files-post |
| 186 | + */ |
| 187 | + private function exportFilesAsync(array $locales, array $domains): array |
| 188 | + { |
| 189 | + $response = $this->client->request('POST', 'files/async-download', [ |
| 190 | + 'json' => [ |
| 191 | + 'format' => 'symfony_xliff', |
| 192 | + 'original_filenames' => true, |
| 193 | + 'filter_langs' => array_values($locales), |
| 194 | + 'filter_filenames' => array_map($this->getLokaliseFilenameFromDomain(...), $domains), |
| 195 | + 'export_empty_as' => 'skip', |
| 196 | + 'replace_breaks' => false, |
| 197 | + ], |
| 198 | + ]); |
| 199 | + |
| 200 | + if (200 !== $response->getStatusCode()) { |
| 201 | + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); |
| 202 | + } |
| 203 | + |
| 204 | + $processId = $response->toArray()['process_id']; |
| 205 | + $attempt = 1; |
| 206 | + while (true) { |
| 207 | + usleep(500000 * $attempt); |
| 208 | + $response = $this->client->request('GET', \sprintf('processes/%s', $processId)); |
| 209 | + $process = $response->toArray()['process']; |
| 210 | + if ('failed' === $process['status']) { |
| 211 | + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); |
| 212 | + } |
| 213 | + if ('finished' === $process['status']) { |
| 214 | + $downloadUrl = $process['details']['download_url']; |
| 215 | + break; |
| 216 | + } |
| 217 | + ++$attempt; |
| 218 | + } |
| 219 | + |
| 220 | + try { |
| 221 | + $newfile = \sprintf('/tmp/%s.zip', uniqid()); |
| 222 | + $extractPath = \sprintf('/tmp/%s', uniqid()); |
| 223 | + |
| 224 | + if (!copy($downloadUrl, $newfile)) { |
| 225 | + throw new LogicException(\sprintf('failed to copy "%s".', $downloadUrl)); |
| 226 | + } |
| 227 | + |
| 228 | + $zip = new \ZipArchive(); |
| 229 | + if (!$zip->open($newfile, \ZipArchive::CREATE)) { |
| 230 | + throw new LogicException(\sprintf('failed to open zip file "%s".', $newfile)); |
| 231 | + } |
| 232 | + |
| 233 | + try { |
| 234 | + if (!$zip->extractTo($extractPath)) { |
| 235 | + throw new LogicException(\sprintf('failed to extract content from zip file "%s".', $newfile)); |
| 236 | + } |
| 237 | + } finally { |
| 238 | + $zip->close(); |
| 239 | + } |
| 240 | + |
| 241 | + $fileContents = $this->getFileContents($extractPath); |
| 242 | + |
| 243 | + // Lokalise returns languages with "-" separator, we need to reformat them to "_" separator. |
| 244 | + $reformattedLanguages = array_map(function ($language) { |
| 245 | + return str_replace('-', '_', $language); |
| 246 | + }, array_keys($fileContents)); |
| 247 | + |
| 248 | + return array_combine($reformattedLanguages, $fileContents); |
| 249 | + } finally { |
| 250 | + unlink($newfile); |
| 251 | + $this->removeDir($extractPath); |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + private function getFileContents(string $dirName, string $baseDirName = ''): array |
| 256 | + { |
| 257 | + $fileContents = []; |
| 258 | + foreach (scandir($dirName) as $filename) { |
| 259 | + if (\in_array($filename, ['.', '..'])) { |
| 260 | + continue; |
| 261 | + } |
| 262 | + $path = \sprintf('%s/%s', $dirName, $filename); |
| 263 | + if (is_dir($path)) { |
| 264 | + $fileContents = array_merge($fileContents, $this->getFileContents($path, $filename)); |
| 265 | + continue; |
| 266 | + } |
| 267 | + $fileContents[$baseDirName][$filename]['content'] = file_get_contents($path); |
| 268 | + } |
| 269 | + |
| 270 | + return $fileContents; |
| 271 | + } |
| 272 | + |
| 273 | + private function removeDir(string $dir): void |
| 274 | + { |
| 275 | + $it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS); |
| 276 | + $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST); |
| 277 | + foreach ($files as $file) { |
| 278 | + if ($file->isDir()) { |
| 279 | + rmdir($file->getPathname()); |
| 280 | + } else { |
| 281 | + unlink($file->getPathname()); |
| 282 | + } |
| 283 | + } |
| 284 | + rmdir($dir); |
| 285 | + } |
| 286 | + |
179 | 287 | private function createKeys(array $keys, string $domain): array |
180 | 288 | { |
181 | 289 | $keysToCreate = []; |
|
0 commit comments