1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace Nexendrie\Translation; |
5
|
|
|
|
6
|
|
|
use Nette\Utils\Arrays; |
7
|
|
|
use Nette\Localization\ITranslator; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Translator |
11
|
|
|
* |
12
|
|
|
* @author Jakub Konečný |
13
|
|
|
* @property string $lang |
14
|
|
|
* @property-read string[] $untranslated |
15
|
|
|
* @method void onUntranslated(string $message) |
16
|
|
|
*/ |
17
|
1 |
|
final class Translator implements ITranslator { |
|
|
|
|
18
|
|
|
use \Nette\SmartObject; |
19
|
|
|
|
20
|
|
|
/** @internal */ |
21
|
|
|
public const DEFAULT_DOMAIN = "messages"; |
22
|
|
|
|
23
|
|
|
private ILoader $loader; |
24
|
|
|
private IMessageSelector $messageSelector; |
25
|
|
|
/** @var string[] */ |
26
|
|
|
private array $untranslated = []; |
27
|
|
|
/** @var callable[] */ |
28
|
|
|
public array $onUntranslated = []; |
29
|
|
|
|
30
|
|
|
public function __construct(ILoader $loader, IMessageSelector $messageSelector = null) { |
31
|
1 |
|
$this->loader = $loader; |
32
|
1 |
|
$this->messageSelector = $messageSelector ?? new MessageSelector(); |
33
|
1 |
|
} |
34
|
|
|
|
35
|
|
|
public function getLang(): string { |
36
|
1 |
|
return $this->loader->getLang(); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
public function setLang(string $lang): void { |
40
|
1 |
|
$this->loader->setLang($lang); |
41
|
1 |
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @return string[] |
45
|
|
|
*/ |
46
|
|
|
public function getUntranslated(): array { |
47
|
1 |
|
return $this->untranslated; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @return string[] |
52
|
|
|
*/ |
53
|
|
|
protected function extractDomainAndMessage(string $message): array { |
54
|
1 |
|
if(!str_contains($message, ".")) { |
55
|
1 |
|
return [static::DEFAULT_DOMAIN, $message]; |
56
|
|
|
} |
57
|
1 |
|
return explode(".", $message, 2); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Translate multi-level message |
62
|
|
|
*/ |
63
|
|
|
protected function multiLevelTrans(array $message, array $texts): string { |
64
|
1 |
|
$text = $texts; |
65
|
1 |
|
foreach($message as $part) { |
66
|
1 |
|
$text = Arrays::get($text, $part, ""); |
67
|
1 |
|
if($text === "") { |
68
|
1 |
|
break; |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
/** @var string $text */ |
72
|
1 |
|
return $text; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
public function logUntranslatedMessage(string $message): void { |
76
|
1 |
|
$this->untranslated[] = $message; |
77
|
1 |
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Translate the string |
81
|
|
|
* |
82
|
|
|
* @param mixed $message |
83
|
|
|
* @param mixed ...$parameters |
84
|
|
|
* @return string |
85
|
|
|
*/ |
86
|
|
|
public function translate($message, ... $parameters): string { |
87
|
1 |
|
$message = (string) $message; |
88
|
1 |
|
if(count($parameters) === 1 && is_array($parameters[0])) { |
89
|
1 |
|
$count = $parameters[0]["count"] ?? 0; |
90
|
1 |
|
$params = $parameters[0]; |
91
|
|
|
} else { |
92
|
1 |
|
$params = $parameters[1] ?? []; |
93
|
1 |
|
$params["count"] = $count = $parameters[0] ?? 0; |
94
|
|
|
} |
95
|
1 |
|
[$domain, $m] = $this->extractDomainAndMessage($message); |
96
|
1 |
|
$texts = Arrays::get($this->loader->getTexts(), $domain, []); |
97
|
1 |
|
$parts = explode(".", $m); |
98
|
1 |
|
if(count($parts) === 1) { |
99
|
1 |
|
$parts = [$m]; |
100
|
|
|
} |
101
|
1 |
|
$text = $this->multiLevelTrans($parts, $texts); |
|
|
|
|
102
|
1 |
|
if($text === "") { |
103
|
1 |
|
$this->onUntranslated($message); |
104
|
1 |
|
return $message; |
105
|
|
|
} |
106
|
1 |
|
if($this->messageSelector->isMultiChoice($text)) { |
107
|
1 |
|
$text = $this->messageSelector->choose($text, $count); |
108
|
|
|
} |
109
|
1 |
|
foreach($params as $key => $value) { |
110
|
1 |
|
$text = str_replace("%$key%", (string) $value, $text); |
111
|
|
|
} |
112
|
1 |
|
return $text; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
?> |
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.