Skip to content

Commit 37cfe4a

Browse files
Merge pull request #468 from LinkStackOrg/translation
Made translation possible
2 parents 4ba8b47 + 26235b2 commit 37cfe4a

File tree

93 files changed

+2529
-2293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+2529
-2293
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
LOCALE=en
2+
13
#Email verification=Changes if users have to verify their email after registration.
24
#=REGISTER_AUTH either auth or verified. If auth is selected, no verification is required. Default is verified.
35
REGISTER_AUTH=auth

app/Console/Commands/Translate.php

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
use Illuminate\Console\Command;
5+
use Illuminate\Support\Facades\File;
6+
use GuzzleHttp\Client;
7+
use GuzzleHttp\Exception\GuzzleException;
8+
use JsonException;
9+
use Throwable;
10+
11+
interface TokenProviderInterface
12+
{
13+
14+
public function generateToken(string $source, string $target, string $text): string;
15+
}
16+
17+
class GoogleTokenGenerator implements TokenProviderInterface
18+
{
19+
20+
public function generateToken(string $source, string $target, string $text): string
21+
{
22+
$tkk = ['406398', 2087938574];
23+
24+
for ($d = [], $e = 0, $f = 0; $f < $this->length($text); $f++) {
25+
$g = $this->charCodeAt($text, $f);
26+
if ($g < 128) {
27+
$d[$e++] = $g;
28+
} else {
29+
if ($g < 2048) {
30+
$d[$e++] = $g >> 6 | 192;
31+
} else {
32+
if ($g & 64512 === 55296 && $f + 1 < $this->length($text) && ($this->charCodeAt($text, $f + 1) & 64512) === 56320) {
33+
$g = 65536 + (($g & 1023) << 10) + ($this->charCodeAt($text, ++$f) & 1023);
34+
$d[$e++] = $g >> 18 | 240;
35+
$d[$e++] = $g >> 12 & 63 | 128;
36+
} else {
37+
$d[$e++] = $g >> 12 | 224;
38+
}
39+
$d[$e++] = $g >> 6 & 63 | 128;
40+
}
41+
$d[$e++] = $g & 63 | 128;
42+
}
43+
}
44+
45+
$a = $tkk[0];
46+
foreach ($d as $value) {
47+
$a += $value;
48+
$a = $this->rl($a, '+-a^+6');
49+
}
50+
$a = $this->rl($a, '+-3^+b+-f');
51+
$a ^= $tkk[1];
52+
if ($a < 0) {
53+
$a = ($a & 2147483647) + 2147483648;
54+
}
55+
$a = fmod($a, 1000000);
56+
57+
return $a . '.' . ($a ^ $tkk[0]);
58+
}
59+
60+
private function rl(int $a, string $b): int
61+
{
62+
for ($c = 0; $c < strlen($b) - 2; $c += 3) {
63+
$d = $b[$c + 2];
64+
$d = $d >= 'a' ? ord($d[0]) - 87 : (int) $d;
65+
$d = $b[$c + 1] === '+' ? $this->unsignedRightShift($a, $d) : $a << $d;
66+
$a = $b[$c] === '+' ? ($a + $d & 4294967295) : $a ^ $d;
67+
}
68+
69+
return $a;
70+
}
71+
72+
private function unsignedRightShift(int $a, int $b): int
73+
{
74+
if ($b >= 32 || $b < -32) {
75+
$m = (int) ($b / 32);
76+
$b -= ($m * 32);
77+
}
78+
79+
if ($b < 0) {
80+
$b += 32;
81+
}
82+
83+
if ($b === 0) {
84+
return (($a >> 1) & 0x7fffffff) * 2 + (($a >> $b) & 1);
85+
}
86+
87+
if ($a < 0) {
88+
$a >>= 1;
89+
$a &= 2147483647;
90+
$a |= 0x40000000;
91+
$a >>= ($b - 1);
92+
} else {
93+
$a >>= $b;
94+
}
95+
96+
return $a;
97+
}
98+
99+
private function charCodeAt(string $string, int $index): int
100+
{
101+
return mb_ord(mb_substr($string, $index, 1));
102+
}
103+
104+
private function length(string $string): int
105+
{
106+
return mb_strlen($string);
107+
}
108+
}
109+
110+
class GoogleTranslate
111+
{
112+
113+
protected Client $client;
114+
115+
protected ?string $source;
116+
117+
protected ?string $target;
118+
119+
protected ?string $lastDetectedSource;
120+
121+
protected string $url = 'https://translate.google.com/translate_a/single';
122+
123+
protected array $options = [];
124+
125+
protected array $urlParams = [
126+
'client' => 'gtx',
127+
'hl' => 'en',
128+
'dt' => [
129+
't',
130+
'bd',
131+
'at',
132+
'ex',
133+
'ld',
134+
'md',
135+
'qca',
136+
'rw',
137+
'rm',
138+
'ss'
139+
],
140+
'sl' => null,
141+
'tl' => null,
142+
'q' => null,
143+
'ie' => 'UTF-8',
144+
'oe' => 'UTF-8',
145+
'multires' => 1,
146+
'otf' => 0,
147+
'pc' => 1,
148+
'trs' => 1,
149+
'ssel' => 0,
150+
'tsel' => 0,
151+
'kc' => 1,
152+
'tk' => null,
153+
];
154+
155+
protected array $resultRegexes = [
156+
'/,+/' => ',',
157+
'/\[,/' => '[',
158+
];
159+
160+
protected TokenProviderInterface $tokenProvider;
161+
162+
public function __construct(string $target = 'en', string $source = null, array $options = [], TokenProviderInterface $tokenProvider = null)
163+
{
164+
$this->client = new Client();
165+
$this->setTokenProvider($tokenProvider ?? new GoogleTokenGenerator)
166+
->setOptions($options)
167+
->setSource($source)
168+
->setTarget($target);
169+
}
170+
171+
public function setTarget(string $target): self
172+
{
173+
$this->target = $target;
174+
return $this;
175+
}
176+
177+
public function setSource(string $source = null): self
178+
{
179+
$this->source = $source ?? 'auto';
180+
return $this;
181+
}
182+
183+
public function setUrl(string $url): self
184+
{
185+
$this->url = $url;
186+
return $this;
187+
}
188+
189+
public function setClient(string $client): self
190+
{
191+
$this->urlParams['client'] = $client;
192+
return $this;
193+
}
194+
195+
public function setOptions(array $options = []): self
196+
{
197+
$this->options = $options;
198+
return $this;
199+
}
200+
201+
public function setTokenProvider(TokenProviderInterface $tokenProvider): self
202+
{
203+
$this->tokenProvider = $tokenProvider;
204+
return $this;
205+
}
206+
207+
public function getLastDetectedSource(): ?string
208+
{
209+
return $this->lastDetectedSource;
210+
}
211+
212+
public static function trans(string $string, string $target = 'en', string $source = null, array $options = [], TokenProviderInterface $tokenProvider = null): ?string
213+
{
214+
return (new self)
215+
->setTokenProvider($tokenProvider ?? new GoogleTokenGenerator)
216+
->setOptions($options)
217+
->setSource($source)
218+
->setTarget($target)
219+
->translate($string);
220+
}
221+
222+
public function translate(string $string): ?string
223+
{
224+
225+
if ($this->source === $this->target) {
226+
return $string;
227+
}
228+
229+
$responseArray = $this->getResponse($string);
230+
231+
if (empty($responseArray[0])) {
232+
return null;
233+
}
234+
235+
$detectedLanguages = [];
236+
237+
foreach ($responseArray as $item) {
238+
if (is_string($item)) {
239+
$detectedLanguages[] = $item;
240+
}
241+
}
242+
243+
if (isset($responseArray[count($responseArray) - 2][0][0])) {
244+
$detectedLanguages[] = $responseArray[count($responseArray) - 2][0][0];
245+
}
246+
247+
$this->lastDetectedSource = null;
248+
249+
foreach ($detectedLanguages as $lang) {
250+
if ($this->isValidLocale($lang)) {
251+
$this->lastDetectedSource = $lang;
252+
break;
253+
}
254+
}
255+
256+
if (is_string($responseArray)) {
257+
return $responseArray;
258+
}
259+
260+
if (is_array($responseArray[0])) {
261+
return (string) array_reduce($responseArray[0], static function ($carry, $item) {
262+
$carry .= $item[0];
263+
return $carry;
264+
});
265+
}
266+
267+
return (string) $responseArray[0];
268+
}
269+
270+
public function getResponse(string $string): array
271+
{
272+
$queryArray = array_merge($this->urlParams, [
273+
'sl' => $this->source,
274+
'tl' => $this->target,
275+
'tk' => $this->tokenProvider->generateToken($this->source, $this->target, $string),
276+
'q' => $string
277+
]);
278+
279+
$queryUrl = preg_replace('/%5B\d+%5D=/', '=', http_build_query($queryArray));
280+
281+
try {
282+
$response = $this->client->get($this->url, [
283+
'query' => $queryUrl,
284+
] + $this->options);
285+
} catch (GuzzleException $e) {
286+
match ($e->getCode()) {
287+
429, 503 => throw new RateLimitException($e->getMessage(), $e->getCode()),
288+
413 => throw new LargeTextException($e->getMessage(), $e->getCode()),
289+
default => throw new TranslationRequestException($e->getMessage(), $e->getCode()),
290+
};
291+
} catch (Throwable $e) {
292+
throw new TranslationRequestException($e->getMessage(), $e->getCode());
293+
}
294+
295+
$body = $response->getBody();
296+
297+
$bodyJson = preg_replace(array_keys($this->resultRegexes), array_values($this->resultRegexes), $body);
298+
299+
try {
300+
$bodyArray = json_decode($bodyJson, true, flags: JSON_THROW_ON_ERROR);
301+
} catch (JsonException) {
302+
throw new TranslationDecodingException('Data cannot be decoded or it is deeper than the recursion limit');
303+
}
304+
305+
return $bodyArray;
306+
}
307+
308+
protected function isValidLocale(string $lang): bool
309+
{
310+
return (bool) preg_match('/^([a-z]{2,3})(-[A-Za-z]{2,4})?$/', $lang);
311+
}
312+
}
313+
314+
class Translate extends Command
315+
{
316+
protected $signature = 'translate';
317+
318+
protected $description = 'Translate language files';
319+
320+
public function handle()
321+
{
322+
$locales = config('app.supported_locales');
323+
$sourceLocale = 'en';
324+
325+
foreach ($locales as $locale) {
326+
$langPath = resource_path("lang/{$locale}");
327+
if (!File::exists($langPath)) {
328+
File::makeDirectory($langPath, 0755, true);
329+
}
330+
}
331+
332+
$sourceFile = resource_path("lang/{$sourceLocale}/messages.php");
333+
$sourceTranslations = require($sourceFile);
334+
335+
foreach ($locales as $locale) {
336+
$targetFile = resource_path("lang/{$locale}/messages.php");
337+
$targetTranslations = File::exists($targetFile) ? require($targetFile) : [];
338+
339+
$tr = new GoogleTranslate();
340+
$tr->setSource($sourceLocale);
341+
$tr->setTarget($locale);
342+
343+
foreach ($sourceTranslations as $key => $value) {
344+
if (!array_key_exists($key, $targetTranslations)) {
345+
$translatedValue = $tr->translate($value);
346+
$targetTranslations[$key] = $translatedValue;
347+
}
348+
}
349+
350+
$content = '<?php' . PHP_EOL . PHP_EOL;
351+
$content .= 'return ' . var_export($targetTranslations, true) . ';' . PHP_EOL;
352+
353+
file_put_contents($targetFile, $content);
354+
355+
$this->info("Translations for '{$locale}' created successfully.");
356+
}
357+
}
358+
}

app/Http/Controllers/InstallerController.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,17 @@ public function options(request $request)
156156
return redirect(url(''));
157157
}
158158

159+
public function editConfigInstaller(request $request)
160+
{
161+
162+
$type = $request->type;
163+
$entry = $request->entry;
164+
$value = $request->value;
165+
$value = '"' . $request->value . '"';
166+
167+
if(EnvEditor::keyExists($entry)){EnvEditor::editKey($entry, $value);}
168+
169+
return Redirect(url(''));
170+
}
171+
159172
}

0 commit comments

Comments
 (0)