Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.57% covered (warning)
78.57%
22 / 28
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
I18n
78.57% covered (warning)
78.57%
22 / 28
25.00% covered (danger)
25.00%
1 / 4
13.42
0.00% covered (danger)
0.00%
0 / 1
 initialize
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getFlatMessage
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 getMessage
66.67% covered (warning)
66.67%
8 / 12
0.00% covered (danger)
0.00%
0 / 1
5.93
 message
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core;
6
7use PeServer\Core\Code;
8use PeServer\Core\Collection\Arr;
9use PeServer\Core\InitializeChecker;
10use PeServer\Core\Text;
11
12abstract class I18n
13{
14    #region define
15
16    // これはライブラリ側で持つ文字列リソース
17    public const COMMON_ERROR_TITLE = '@core/common/error_title';
18    public const ERROR_EMPTY = '@core/error/empty';
19    public const ERROR_WHITE_SPACE = '@core/error/white_space';
20    public const ERROR_LENGTH = '@core/error/length';
21    public const ERROR_RANGE = '@core/error/range';
22    public const ERROR_MATCH = '@core/error/match';
23    public const ERROR_EMAIL = '@core/error/email';
24    public const ERROR_WEBSITE = '@core/error/website';
25    // ここまでライブラリ側で持つ文字列リソース
26
27    private const INVARIANT_LOCALE = '*';
28
29    #endregion
30
31    #region variable
32
33    /**
34     * 初期化チェック
35     */
36    private static InitializeChecker|null $initializeChecker = null;
37
38    /**
39     * 設定。
40     *
41     * @var array<string,string|array<string,mixed>>
42     */
43    private static array $i18nConfiguration;
44
45    #endregion
46
47    #region function
48
49    /**
50     * 初期化。
51     *
52     * @param array<string,string|array<string,mixed>> $i18nConfiguration
53     * @return void
54     */
55    public static function initialize(array $i18nConfiguration): void
56    {
57        self::$initializeChecker ??= new InitializeChecker();
58        self::$initializeChecker->initialize();
59
60        self::$i18nConfiguration = $i18nConfiguration;
61    }
62
63    /**
64     * 平坦化。
65     *
66     * @param array<string,string|mixed> $array
67     * @param string $locale
68     * @return string|null
69     */
70    private static function getFlatMessage(array $array, string $locale): string|null
71    {
72        if (isset($array[$locale])) {
73            /** @var string */
74            return $array[$locale];
75        }
76
77        if ($locale === self::INVARIANT_LOCALE) {
78            return null;
79        }
80
81        return self::getFlatMessage($array, self::INVARIANT_LOCALE);
82    }
83
84    private static function getMessage(string $key, string $locale): string|null
85    {
86        if (isset(self::$i18nConfiguration[$key])) {
87            // @phpstan-ignore-next-line array<string,string|array<string,mixed>>
88            return self::getFlatMessage(self::$i18nConfiguration[$key], $locale);
89        }
90
91        /** @var array<string,string|array<string,mixed>> */
92        $leaf = self::$i18nConfiguration;
93        $tree = Text::split($key, '/');
94        foreach ($tree as $node) {
95            if (Arr::tryGet($leaf, $node, $result)) {
96                $leaf = $result;
97            } else {
98                $leaf = null;
99                break;
100            }
101        }
102
103        if ($leaf !== null) {
104            return self::getFlatMessage($leaf, $locale) ?? Text::EMPTY;
105        }
106
107
108        return null;
109    }
110
111    /**
112     * ローカライズ文字列を取得。
113     *
114     * 初期化時に渡された文字列リソースから対象文言を取得する。
115     *
116     * @param string $key 文字列リソースのキー。/が存在する場合にキーから見つからない場合は階層構造として扱う。
117     * @param array<int|string,int|string> $parameters
118     * @return string
119     */
120    public static function message(string $key, array $parameters = []): string
121    {
122        InitializeChecker::throwIfNotInitialize(self::$initializeChecker);
123
124        /** @var array<string,string> */
125        $params = [];
126        foreach ($parameters as $k => $v) {
127            $params[strval($k)] = strval($v);
128        }
129
130        $message = self::getMessage($key, 'ja');
131        if ($message === null) {
132            $message =  $key;
133        }
134
135        return Text::replaceMap(Code::toLiteralString($message), $params);
136    }
137
138    #endregion
139}