diff options
Diffstat (limited to 'src/application/i18n.php')
| -rw-r--r-- | src/application/i18n.php | 98 | 
1 files changed, 98 insertions, 0 deletions
| diff --git a/src/application/i18n.php b/src/application/i18n.php new file mode 100644 index 0000000..964439f --- /dev/null +++ b/src/application/i18n.php @@ -0,0 +1,98 @@ +<?php + +const MESSAGE_DIR = __DIR__ . "/messages"; + +$__i18n_msg_store = []; +$__i18n_current_locale = null; + +function i18n_parse(string $contents, ?string $filename = null): array { +    $syntax_error = fn(string $msg, ?int $line = null): never => +        throw new Exception("i18n syntax error: $msg (in " . ($filename ?? "unknown") . ":" . ($line ?? 0) . ")"); + +    $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; +                    $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; +        $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); +} |