Code

< 40 %
40-60 %
> 60 %
1
<?php
2
3
namespace Koded\I18n;
4
5
use Koded\Stdlib\Configuration;
6
use Throwable;
7
use function array_key_exists;
8
use function error_log;
9
use function getcwd;
10
use function rtrim;
11
use function str_replace;
12
use function strtolower;
13
14
abstract class I18nCatalog
15
{
16
    protected string $locale;
17
    protected string $directory;
18
    protected I18nFormatter $formatter;
19
20 26
    private function __construct(
21
        I18nFormatter $formatter,
22
        string        $directory,
23
        string        $locale)
24
    {
25 26
        $this->formatter = $formatter;
26 26
        $this->directory = rtrim($directory, '/');
27 26
        $this->locale = $this->initialize($locale);
28
    }
29
30 26
    public static function new(Configuration $conf): I18nCatalog
31
    {
32 26
        $catalog = $conf->get('translation.catalog', ArrayCatalog::class);
33 26
        $formatter = $conf->get('translation.formatter', DefaultFormatter::class);
34 26
        $instance = new $catalog(
35 26
            new $formatter,
36 26
            $directory = $conf->get('translation.dir', getcwd() . '/locale'),
37 26
            $locale = self::normalizeLocale($conf->get('translation.locale', I18n::DEFAULT_LOCALE))
38 26
        );
39 26
        if ($instance->supports($locale)) {
40 17
            return $instance;
41
        }
42 16
        if ($catalog !== ArrayCatalog::class) {
43 2
            error_log(" > ($locale) gettext not supported, trying ArrayCatalog ...");
44 2
            $conf->set('translation.catalog', ArrayCatalog::class);
45 2
            return static::new($conf);
46
        }
47
        // Last resort, passthru
48 15
        return new NoCatalog(new $formatter, $directory, $locale);
49
    }
50
51 26
    public static function normalizeLocale(string $locale): string
52
    {
53 26
        $locale = explode('_', str_replace('.', '_', $locale));
54 26
        return "$locale[0]_$locale[1]";
55
    }
56
57 15
    public function translate(
58
        string $domain,
59
        string $key,
60
        array  $arguments = [],
61
        int    $n = 0): string
62
    {
63 15
        return $this->formatter->format(
64 15
            $this->message($domain, $key, $n),
65 15
            $arguments
66 15
        );
67
    }
68
69 26
    public function locale(): string
70
    {
71 26
        return $this->locale;
72
    }
73
74 25
    public function directory(): string
75
    {
76 25
        return $this->directory;
77
    }
78
79 26
    public function formatter(): I18nFormatter
80
    {
81 26
        return $this->formatter;
82
    }
83
84 2
    public function urlized(): string
85
    {
86 2
        return str_replace('_', '-', strtolower($this->locale));
87
    }
88
89
    /**
90
     * Returns the localized display name for catalog's language.
91
     * Defaults to locale value if language value is not available.
92
     *
93
     * @return string
94
     */
95
    abstract public function language(): string;
96
97
    /**
98
     * Translates the message.
99
     *
100
     * @param string $domain
101
     * @param string $string
102
     * @param int $n
103
     * @return string
104
     */
105
    abstract protected function message(
106
        string $domain,
107
        string $string,
108
        int $n): string;
109
110
    /**
111
     * Checks if the locale is supported for this catalog,
112
     * or other specific requirements.
113
     *
114
     * @param string $locale
115
     * @return bool
116
     */
117
    abstract protected function supports(string $locale): bool;
118
119
    /**
120
     * Initialize the catalog object. This method is
121
     * called before supports().
122
     *
123
     * @param string $locale Desired locale to be initialized
124
     * @return string|false Returns the set locale,
125
     *                      or FALSE if initialization fails.
126
     */
127
    abstract protected function initialize(string $locale): string|false;
128
}
129
130
class NoCatalog extends I18nCatalog
131
{
132 2
    public function language(): string
133
    {
134 2
        return $this->locale ?: '';
135
    }
136
137 10
    protected function message(string $domain, string $string, int $n): string
138
    {
139 10
        return $string;
140
    }
141
142
    // @codeCoverageIgnoreStart
143
    protected function supports(string $locale): bool
144
    {
145
        return true;
146
    }
147
    // @codeCoverageIgnoreEnd
148
149 16
    protected function initialize(string $locale): string|false
150
    {
151 16
        return $locale;
152
    }
153
}
154
155
class ArrayCatalog extends I18nCatalog
156
{
157
    private array $data = [];
158
159 2
    public function language(): string
160
    {
161 2
        return $this->data['language'] ?? $this->locale;
162
    }
163
164 4
    protected function message(string $domain, string $string, int $n): string
165
    {
166 4
        return $this->data[$domain][$string] ?? $string;
167
    }
168
169 24
    protected function supports(string $locale): bool
170
    {
171 24
        return $this->locale === $locale;
172
    }
173
174 24
    protected function initialize(string $locale): string|false
175
    {
176
        try {
177
            /** @noinspection PhpIncludeInspection */
178 24
            $this->data = require($catalog = "$this->directory/$locale.php");
179 14
            if (false === array_key_exists('messages', $this->data)) {
180 1
                error_log("ERROR : i18n catalog $catalog is missing the messages array");
181 1
                return false;
182
            }
183 14
            return $locale;
184 14
        } catch (Throwable $e) {
185 14
            error_log($e->getMessage());
186 14
            return false;
187
        }
188
    }
189
}
190