summaryrefslogtreecommitdiff
path: root/src/application/i18n.php
blob: 964439f0b604596a3a7c029352709bb893efcd1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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);
}