Skip to content

Commit ebd1828

Browse files
committed
feat: Add lang:sync command
1 parent 345e1c3 commit ebd1828

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\Commands\Translation;
15+
16+
use CodeIgniter\CLI\BaseCommand;
17+
use CodeIgniter\CLI\CLI;
18+
use CodeIgniter\Helpers\Array\ArrayHelper;
19+
use Config\App;
20+
use Locale;
21+
use RecursiveDirectoryIterator;
22+
use RecursiveIteratorIterator;
23+
use SplFileInfo;
24+
25+
/**
26+
* @see \CodeIgniter\Commands\Translation\LocalizationSyncTest
27+
*/
28+
class LocalizationSync extends BaseCommand
29+
{
30+
protected $group = 'Translation';
31+
protected $name = 'lang:sync';
32+
protected $description = 'Synchronize translation files from one language to another.';
33+
protected $usage = 'lang:sync [options]';
34+
protected $arguments = [];
35+
protected $options = [
36+
'--locale' => 'The original locale (en, ru, etc.).',
37+
'--target' => 'Target locale (en, ru, etc.).',
38+
];
39+
private string $languagePath;
40+
41+
public function run(array $params)
42+
{
43+
$optionTargetLocale = '';
44+
$optionLocale = $params['locale'] ?? Locale::getDefault();
45+
$this->languagePath = APPPATH . 'Language';
46+
47+
if (isset($params['target']) && $params['target'] !== '') {
48+
$optionTargetLocale = $params['target'];
49+
}
50+
51+
if (! in_array($optionLocale, config(App::class)->supportedLocales, true)) {
52+
CLI::error(
53+
'Error: "' . $optionLocale . '" is not supported. Supported locales: '
54+
. implode(', ', config(App::class)->supportedLocales)
55+
);
56+
57+
return EXIT_USER_INPUT;
58+
}
59+
60+
if ($optionTargetLocale === '') {
61+
CLI::error(
62+
'Error: "--target" is not configured. Supported locales: '
63+
. implode(', ', config(App::class)->supportedLocales)
64+
);
65+
66+
return EXIT_USER_INPUT;
67+
}
68+
69+
if (! in_array($optionTargetLocale, config(App::class)->supportedLocales, true)) {
70+
CLI::error(
71+
'Error: "' . $optionTargetLocale . '" is not supported. Supported locales: '
72+
. implode(', ', config(App::class)->supportedLocales)
73+
);
74+
75+
return EXIT_USER_INPUT;
76+
}
77+
78+
if ($optionTargetLocale === $optionLocale) {
79+
CLI::error(
80+
'Error: You cannot have the same values "--target" and "--locale".'
81+
);
82+
83+
return EXIT_USER_INPUT;
84+
}
85+
86+
if (ENVIRONMENT === 'testing') {
87+
$this->languagePath = SUPPORTPATH . 'Language';
88+
}
89+
90+
$this->process($optionLocale, $optionTargetLocale);
91+
92+
CLI::write('All operations done!');
93+
94+
return EXIT_SUCCESS;
95+
}
96+
97+
private function process(string $originalLocale, string $targetLocale): void
98+
{
99+
$originalLocaleDir = $this->languagePath . DIRECTORY_SEPARATOR . $originalLocale;
100+
$targetLocaleDir = $this->languagePath . DIRECTORY_SEPARATOR . $targetLocale;
101+
102+
if (! is_dir($originalLocaleDir)) {
103+
CLI::error(
104+
'Error: The "' . $originalLocaleDir . '" directory was not found.'
105+
);
106+
}
107+
108+
if (! is_dir($targetLocaleDir) && ! mkdir($targetLocaleDir, 0775)) {
109+
CLI::error(
110+
'Error: The target directory "' . $targetLocaleDir . '" cannot be accessed.'
111+
);
112+
}
113+
114+
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($originalLocaleDir));
115+
116+
/**
117+
* @var list<SplFileInfo> $files
118+
*/
119+
$files = iterator_to_array($iterator, true);
120+
ksort($files);
121+
122+
foreach ($files as $originalLanguageFile) {
123+
if ($this->isIgnoredFile($originalLanguageFile)) {
124+
continue;
125+
}
126+
127+
$targetLanguageFile = $targetLocaleDir . DIRECTORY_SEPARATOR . $originalLanguageFile->getFilename();
128+
129+
$targetLanguageKeys = [];
130+
$originalLanguageKeys = include $originalLanguageFile;
131+
132+
if (is_file($targetLanguageFile)) {
133+
$targetLanguageKeys = include $targetLanguageFile;
134+
}
135+
136+
$targetLanguageKeys = $this->mergeLanguageKeys($originalLanguageKeys, $targetLanguageKeys, $originalLanguageFile->getBasename('.php'));
137+
ksort($targetLanguageKeys);
138+
139+
$content = "<?php\n\nreturn " . var_export($targetLanguageKeys, true) . ";\n";
140+
file_put_contents($targetLanguageFile, $content);
141+
}
142+
}
143+
144+
/**
145+
* @param array<string, array<string,mixed>|string|null> $originalLanguageKeys
146+
* @param array<string, array<string,mixed>|string|null> $targetLanguageKeys
147+
*
148+
* @return array<string, array<string,mixed>|string|null>
149+
*/
150+
private function mergeLanguageKeys(array $originalLanguageKeys, array $targetLanguageKeys, string $prefix = ''): array
151+
{
152+
foreach ($originalLanguageKeys as $key => $value) {
153+
$placeholderValue = $prefix === '' ? $prefix . '.' . $key : $key;
154+
155+
if (! is_array($value)) {
156+
// Keep the old value
157+
// TODO: The value type may not match the original one
158+
if (array_key_exists($key, $targetLanguageKeys)) {
159+
continue;
160+
}
161+
162+
// Set new key with placeholder
163+
$targetLanguageKeys[$key] = $placeholderValue;
164+
} else {
165+
if (! array_key_exists($key, $targetLanguageKeys)) {
166+
$targetLanguageKeys[$key] = [];
167+
}
168+
169+
$targetLanguageKeys[$key] = $this->mergeLanguageKeys($value, $targetLanguageKeys[$key], $placeholderValue);
170+
}
171+
}
172+
173+
return ArrayHelper::intersectKeyRecursive($targetLanguageKeys, $originalLanguageKeys);
174+
}
175+
176+
private function isIgnoredFile(SplFileInfo $file): bool
177+
{
178+
return $file->isDir() || $file->getFilename() === '.' || $file->getFilename() === '..' || $file->getExtension() !== 'php';
179+
}
180+
}

0 commit comments

Comments
 (0)