1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Core\Translator\Adapter;
6:
7: use Atk4\Core\ConfigTrait;
8: use Atk4\Core\Translator\ITranslatorAdapter;
9:
10: class Generic implements ITranslatorAdapter
11: {
12: use ConfigTrait {
13: getConfig as protected;
14: readConfig as protected;
15: setConfig as protected;
16: }
17:
18: /** @var array<string, array<string, array<string, non-empty-array<string, string>>>> */
19: protected array $definitions = [];
20:
21: #[\Override]
22: public function _(string $message, array $parameters = [], string $domain = null, string $locale = null): string
23: {
24: $definition = $this->getDefinition($message, $domain ?? 'atk', $locale ?? 'en');
25:
26: if ($definition === null) {
27: return $message;
28: }
29:
30: $count = $parameters['count'] ?? 1;
31:
32: return $this->processMessagePlural($definition, $parameters, $count);
33: }
34:
35: /**
36: * Return translated string. If parameters is not empty will replace tokens.
37: *
38: * @param array<string, mixed> $parameters
39: */
40: protected function processMessage(string $definition, array $parameters = []): string
41: {
42: foreach ($parameters as $key => $val) {
43: $definition = str_replace('{{' . $key . '}}', (string) $val, $definition);
44: }
45:
46: return $definition;
47: }
48:
49: /**
50: * @param non-empty-array<string, string> $definition
51: * @param array<string, mixed> $parameters
52: * @param int $count Requested plural form
53: */
54: protected function processMessagePlural(array $definition, array $parameters = [], int $count = 1): string
55: {
56: $foundDefinition = null;
57: switch ($count) {
58: case 0:
59: $foundDefinition = $definition['zero'] ?? end($definition);
60:
61: break;
62: case 1:
63: $foundDefinition = $definition['one'] ?? null;
64:
65: break;
66: default:
67: $foundDefinition = $definition['other'] ?? null;
68:
69: break;
70: }
71:
72: // if no definition found get the first from array
73: if ($foundDefinition === null) {
74: $foundDefinition = reset($definition);
75: }
76:
77: return $this->processMessage($foundDefinition, $parameters);
78: }
79:
80: /**
81: * @return non-empty-array<string, string>|null
82: */
83: protected function getDefinition(string $message, string $domain, ?string $locale): ?array
84: {
85: return $this->definitions[$locale][$domain][$message] ?? null;
86: }
87:
88: /**
89: * @param array<string, string|non-empty-array<string, string>> $data
90: */
91: public function addDefinitionFromArray(array $data, string $locale, string $domain): void
92: {
93: foreach ($data as $k => $v) {
94: $this->setDefinitionSingle($k, $v, $locale, $domain);
95: }
96: }
97:
98: /**
99: * Set or Replace a single definition within a domain.
100: *
101: * @param string|non-empty-array<string, string> $definition
102: *
103: * @return $this
104: */
105: public function setDefinitionSingle(string $key, $definition, string $locale = 'en', string $domain = 'atk')
106: {
107: if (is_string($definition)) {
108: $definition = ['one' => $definition];
109: }
110:
111: $this->definitions[$locale][$domain][$key] = $definition;
112:
113: return $this;
114: }
115: }
116: