throw new Exception("i18n syntax error: $msg (in " . ($filename ?? "unknown") . ":" . $line . ")"); $other_error = fn(string $msg, int $line): never => throw new Exception("i18n error: $msg (in " . ($filename ?? "unknown") . ":" . $line . ")"); $msgs = []; $lines = explode("\n", $contents); $currentId = ""; $currentContext = ""; $currentMessage = ""; $isInMessage = false; foreach ($lines as $i => $ln) { $lnNum = $i + 1; if (trim($ln) === "") continue; switch ($ln[0]) { case "#": continue 2; case ":": if ($currentId !== "") { if ($currentContext !== "") $currentId = $currentContext . "\004" . $currentId; if (isset($msgs[$currentId])) $other_error("duplicate message id '$currentId'", $lnNum); $msgs[$currentId] = $currentMessage; } $currentId = json_decode(substr($ln, 2)); $currentContext = ""; $currentMessage = ""; $isInMessage = false; break; case "=": $isInMessage = true; $currentMessage = json_decode(substr($ln, 2)); break; case "?": if ($isInMessage) $syntax_error("context must be defined before start of message", $lnNum); $currentContext = json_decode(substr($ln, 2)); break; case " ": if ($isInMessage) $currentMessage .= json_decode(substr($ln, 2)); else $currentId .= json_decode(substr($ln, 2)); break; default: $syntax_error("invalid start of line '" . $ln[0] . "' (0x" . str_pad(strtoupper(dechex(ord($ln[0]))), 2, "0", STR_PAD_LEFT) . ")", $lnNum); break; } } if ($currentId !== "") { if ($currentContext !== "") $currentId = $currentContext . "\x7F" . $currentId; if (isset($msgs[$currentId])) $other_error("duplicate message id '$currentId'", count($lines)); $msgs[$currentId] = $currentMessage; } return $msgs; } function i18n_locale(string $locale) { global $__i18n_current_locale; $__i18n_current_locale = $locale; } function i18n_get(string $msgid, array $params = [], ?string $context = null): string { global $__i18n_current_locale, $__i18n_msg_store; $key = $msgid; if ($context !== null) $key = $context . "\x7F" . $msgid; $msg = ($__i18n_msg_store[$__i18n_current_locale] ?? [])[$msgid] ?? $key; uksort($params, fn(string $a, string $b): int => strlen($b) <=> strlen($a)); return str_replace( array_map(fn(string $k): string => "%$k%", array_keys($params)), array_values($params), $msg ); } function __(string $msgid, array $params = [], ?string $context = null): string { return i18n_get($msgid, $params, $context); } foreach (scandir(MESSAGE_DIR) as $ent) { $path = MESSAGE_DIR . "/" . $ent; if ($ent[0] === "." || !is_file($path) || strcasecmp(pathinfo($ent, PATHINFO_EXTENSION), "msg") !== 0) continue; $lang = pathinfo($ent, PATHINFO_FILENAME); $__i18n_msg_store[$lang] = i18n_parse(file_get_contents($path), $ent); }