1<?php
2
3
4namespace ComboStrap;
5
6
7use ComboStrap\Meta\Api\Metadata;
8use ComboStrap\Meta\Api\MetadataText;
9use dokuwiki\Cache\Cache;
10
11class Lang extends MetadataText
12{
13
14    public const PROPERTY_NAME = "lang";
15    public const PROPERTY_DIR_NAME = "dir";
16
17
18    /**
19     * Process the lang attribute
20     * https://www.w3.org/International/questions/qa-html-language-declarations
21     * @param TagAttributes $attributes
22     *
23     * Language supported:
24     * http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
25     *
26     * Common Locale Data Repository
27     * http://cldr.unicode.org/
28     * Data:
29     *   * http://www.unicode.org/Public/cldr/
30     *   * https://github.com/unicode-cldr/
31     *   * https://github.com/unicode-org/cldr-json
32     * The ''dir'' data is known as the ''characterOrder''
33     *
34     */
35    public static function processLangAttribute(&$attributes)
36    {
37
38
39        /**
40         * Adding the lang attribute
41         * if set
42         */
43        if ($attributes->hasComponentAttribute(self::PROPERTY_NAME)) {
44            $langValue = $attributes->getValueAndRemove(self::PROPERTY_NAME);
45            $attributes->addOutputAttributeValue("lang", $langValue);
46
47            $languageDataCache = new Cache("combo_" . $langValue, ".json");
48            $cacheDataUsable = $languageDataCache->useCache();
49            if (!$cacheDataUsable) {
50
51                // Language about the data
52                $downloadUrl = "https://raw.githubusercontent.com/unicode-org/cldr-json/refs/tags/47.0.0/cldr-json/cldr-misc-full/main/$langValue/layout.json";
53
54                if (PluginUtility::isDevOrTest()) {
55                    // phpunit takes over and would catch and cache the error
56                    $filePointer = fopen($downloadUrl, 'r');
57                } else {
58                    $filePointer = @fopen($downloadUrl, 'r');
59                }
60                if ($filePointer != false) {
61
62                    $numberOfByte = @file_put_contents($languageDataCache->cache, $filePointer);
63                    if ($numberOfByte != false) {
64                        LogUtility::msg("The new language data ($langValue) was downloaded", LogUtility::LVL_MSG_INFO, self::PROPERTY_NAME);
65                        $cacheDataUsable = true;
66                    } else {
67                        LogUtility::msg("Internal error: The language data ($langValue) could no be written to ($languageDataCache->cache)", LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
68                    }
69
70                } else {
71
72                    // fopen(): Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?
73                    $error_get_last = error_get_last();
74                    $message = $error_get_last['message'];
75                    LogUtility::msg("The data for the language ($langValue) could not be downloaded at (<a href=\"$downloadUrl\">$langValue</a>). Error: " . $message, LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
76
77                }
78            }
79
80            if ($cacheDataUsable) {
81                $jsonAsArray = true;
82                $languageData = json_decode(file_get_contents($languageDataCache->cache), $jsonAsArray);
83                if ($languageData == null) {
84                    LogUtility::msg("We could not read the data from the language ($langValue). No direction was set.", LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
85                    return;
86                }
87                $characterOrder = $languageData["main"][$langValue]["layout"]["orientation"]["characterOrder"];
88                if ($characterOrder == "right-to-left") {
89                    $attributes->addOutputAttributeValue("dir", "rtl");
90                } else {
91                    $attributes->addOutputAttributeValue("dir", "ltr");
92                }
93            } else {
94                LogUtility::msg("The language direction cannot be set because no language data was found for the language ($langValue)", LogUtility::LVL_MSG_WARNING, self::PROPERTY_NAME);
95            }
96
97        }
98
99    }
100
101    public static function createForMarkup(MarkupPath $markup): Lang
102    {
103        $lang = new Lang();
104        $lang->setResource($markup);
105        return $lang;
106    }
107
108
109    /**
110     * @throws ExceptionNotFound
111     */
112    public static function createFromRequestedMarkup(): Lang
113    {
114        return self::createForMarkup(MarkupPath::createFromRequestedPage());
115    }
116
117    /**
118     * Set the direction of the text
119     * The global lang direction (not yet inside the Lang class)
120     * @param string $string
121     * @return void
122     */
123    public static function setDirection(string $string)
124    {
125        global $lang;
126        $lang["direction"] = $string;
127    }
128
129    public static function createFromValue(string $langValue): Lang
130    {
131        $lang = new Lang();
132        $lang->value = $langValue;
133        return $lang;
134    }
135
136    static public function getTab(): ?string
137    {
138        return MetaManagerForm::TAB_LANGUAGE_VALUE;
139    }
140
141    /**
142     * @return string
143     */
144    public function getValueOrDefault(): string
145    {
146        try {
147            return $this->getValue();
148        } catch (ExceptionNotFound $e) {
149            return $this->getDefaultValue();
150        }
151    }
152
153
154    /**
155     * @throws ExceptionCompile
156     */
157    public function setFromStoreValue($value): Metadata
158    {
159
160        $this->validityCheck($value);
161        return parent::setFromStoreValue($value);
162
163    }
164
165    /**
166     * @param string|null $value
167     * @return Metadata
168     * @throws ExceptionCompile
169     */
170    public function setValue($value): Metadata
171    {
172        $this->validityCheck($value);
173        return parent::setValue($value);
174    }
175
176
177    static public function getDescription(): string
178    {
179        return "The language of the page";
180    }
181
182    static public function getLabel(): string
183    {
184        return "Language";
185    }
186
187    public static function getName(): string
188    {
189        return self::PROPERTY_NAME;
190    }
191
192    static public function getPersistenceType(): string
193    {
194        return Metadata::PERSISTENT_METADATA;
195    }
196
197    static public function isMutable(): bool
198    {
199        return true;
200    }
201
202    /**
203     * @return string
204     */
205    public function getDefaultValue(): string
206    {
207        return Site::getLang();
208    }
209
210    /**
211     * @throws ExceptionCompile
212     */
213    private function validityCheck($value)
214    {
215        if ($value === "" || $value === null) {
216            return;
217        }
218        if (!StringUtility::match($value, "^[a-zA-Z]{2}$")) {
219            throw new ExceptionCompile("The lang value ($value) for the page ($this) does not have two letters", $this->getCanonical());
220        }
221    }
222
223    static public function getCanonical(): string
224    {
225        return "lang";
226    }
227
228    public function getDirection()
229    {
230        /**
231         * TODO: should be base on the page value
232         * Search PHP and CLDR
233         * https://punic.github.io/
234         * https://www.php.net/manual/en/book.intl.php
235         *
236         * Example:
237         * https://github.com/salarmehr/cosmopolitan
238         * use Salarmehr\Cosmopolitan\Cosmo;
239         *
240         * echo Cosmo::create('fa')->direction(); // rlt
241         * echo Cosmo::create('en')->direction(); // ltr
242         */
243        return Site::getLangDirection();
244    }
245
246
247    static public function isOnForm(): bool
248    {
249        return true;
250    }
251
252}
253