diff options
Diffstat (limited to 'src/application')
| -rw-r--r-- | src/application/i18n.php | 98 | ||||
| -rw-r--r-- | src/application/messages/de.msg | 117 | ||||
| -rw-r--r-- | src/application/messages/strings.mst | 111 | ||||
| -rw-r--r-- | src/application/mystic/forum/Database.php | 23 | ||||
| -rw-r--r-- | src/application/views/form_login.php | 2 | ||||
| -rw-r--r-- | src/application/views/form_register.php | 5 | ||||
| -rw-r--r-- | src/application/views/nav_guest.php | 4 | 
7 files changed, 350 insertions, 10 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); +} diff --git a/src/application/messages/de.msg b/src/application/messages/de.msg new file mode 100644 index 0000000..5ee2f6c --- /dev/null +++ b/src/application/messages/de.msg @@ -0,0 +1,117 @@ +: "Log in" += "Anmelden" + +: "Register" += "Registrieren" + +: "Failed to connect to database!" += "Fehler bei Datenbankverbindung" + +: "Username or password incorrect!" += "Nutzername oder Passwort falsch!" + +: "Please activate your user account first!" += "Bitte aktivieren Sie Ihr Nutzerkonto zuerst!" + +: "Public registration disabled" += "Öffentliche Registrierung deaktiviert" + +: "Incorrect CAPTCHA text!" += "Ungültiger CAPTCHA-Text!" + +: "Passwords do not match!" += "Passwörter stimmen nicht überein!" + +: "Password too short! Your password must consist of 8 or more characters" += "Passwort zu kurz! Es muss aus mindestens acht Zeichen bestehen" + +: "Username has an invalid format" += "Der Nutzername weist ein ungültiges Format auf" + +: "Invalid email address" += "Ungültige E-Mail-Adresse" + +: "This username is already taken!" += "Dieser Nutzername ist bereits vergeben!" + +: "This email address is already in use!" += "Diese E-Mail-Adresse wird bereits verwendet!" + +: "Welcome to %forum_title%, %user_display_name%!\n" +  "\n" +  "Please activate your account by clicking the link below:\n" +  "%activation_link%\n" +  "\n" +  "Kind regards,\n" +  "%forum_copyright%" += "Willkommen bei %forum_title%, %user_display_name%!\n" +  "\n" +  "Bitte aktivieren Sie Ihr Nutzerkonto, indem Sie folgenen Link anklicken:\n" +  "%activation_link%\n" +  "" +  "Mit freundlichen Grüßen,\n" +  "%forum_copyright%" + +: "Your account has been created!\nPlease check your emails for an activation link!" += "Ihr Nutzerkonto wurde erstellt!\nBitte überprüfen Sie Ihre E-Mails auf einen Aktivierungslink!" + +: "Invalid token" += "Ungültiges Token" + +: "Invalid signature." += "Ungültige Signatur" + +: "Failed to update user" += "Konnte Nutzer nicht aktualisieren" + +: "Your account has been activated!\nPlease click %link%here%/link% to log in!" += "Ihr Nutzerkonto wurde aktiviert!\nBitte klicken Sie %link%hier%/link%, um sich anzumelden!" + +: "Too many attachments" += "Zu viele Anhänge" + +: "Individual file size exceeded" += "Anhang zu groß" + +: "Message too short or too long!" += "Nachricht zu kurz oder zu lang" + +: "Title too short or too long!" += "Titel zu kurz oder zu lang" + +: "New topic" += "Neues Thema" + +: "No user with name @%user_handle%" += "Kein Nutzer mit dem Namen @%user_handle%" + +: "No user exists with this id" += "Kein Nutzer existiert mit dieser ID" + +: "You can only change your username every 30 days!" += "Sie können Ihren Nutzernamen nur alle 30 Tage ändern!" + +: "Invalid username!" += "Ungültiger Nutzername!" + +: "Please upload an image to change your profile picture" += "Bitte laden Sie ein Bild hoch, um Ihr Profilbild zu ändern" + +: "Please upload a valid PNG or JPEG file" += "Bitte laden Sie eine gültige PNG- oder JPEG-Datei hoch" + +: "Failed to save changes" +? "Update profile" += "Konnte Änderungen nicht übernehmen" + +: "You must be logged in to view attachments" += "Sie müssen angemeldet sein, um Anhänge sehen zu können" + +: "No attachment exists with this id" += "Kein Anhang exsitier mit dieser ID" + +: "Attachment is not an image" += "Anhang ist kein Bild" + +: "Delete post" += "Beitrag löschen" diff --git a/src/application/messages/strings.mst b/src/application/messages/strings.mst new file mode 100644 index 0000000..6f6ec38 --- /dev/null +++ b/src/application/messages/strings.mst @@ -0,0 +1,111 @@ +: "Log in" += "" + +: "Register" += "" + +: "Failed to connect to database!" += "" + +: "Username or password incorrect!" += "" + +: "Please activate your user account first!" += "" + +: "Public registration disabled" += "" + +: "Incorrect CAPTCHA text!" += "" + +: "Passwords do not match!" += "" + +: "Password too short! Your password must consist of 8 or more characters" += "" + +: "Username has an invalid format" += "" + +: "Invalid email address" += "" + +: "This username is already taken!" += "" + +: "This email address is already in use!" += "" + +: "Welcome to %forum_title%, %user_display_name%!\n" +  "\n" +  "Please activate your account by clicking the link below:\n" +  "%activation_link%\n" +  "\n" +  "Kind regards,\n" +  "%forum_copyright%" += "" + +: "Your account has been created!\nPlease check your emails for an activation link!" += "" + +: "Invalid token" += "" + +: "Invalid signature." += "" + +: "Failed to update user" += "" + +: "Your account has been activated!\nPlease click %link%here%/link% to log in!" += "" + +: "Too many attachments" += "" + +: "Individual file size exceeded" += "" + +: "Message too short or too long!" += "" + +: "Title too short or too long!" += "" + +: "New topic" += "" + +: "No user with name @%user_handle%" += "" + +: "No user exists with this id" += "" + +: "You can only change your username every 30 days!" += "" + +: "Invalid username!" += "" + +: "Please upload an image to change your profile picture" += "" + +: "Please upload a valid PNG or JPEG file" += "" + +: "Failed to save changes" +? "Update profile" += "" + +: "You must be logged in to view attachments" += "" + +: "No attachment exists with this id" += "" + +: "Attachment is not an image" += "" + +: "Delete post" += "" diff --git a/src/application/mystic/forum/Database.php b/src/application/mystic/forum/Database.php index 7c9ac7a..6d633fa 100644 --- a/src/application/mystic/forum/Database.php +++ b/src/application/mystic/forum/Database.php @@ -333,15 +333,28 @@ class Database {          return true;      } -    public function fetchWhere(Entity &$entity, string $columnName): bool { +    public function fetchWhere(Entity &$entity, string|array $columnNames): bool {          $entityClassName = get_class($entity);          $tableName = self::getTableName($entityClassName);          $reflClass = new ReflectionClass($entityClassName);          $cols = self::getColumns($reflClass); -        if (!isset($cols[$columnName])) -            throw new \RuntimeException("Column $columnName does not exist!"); -        $query = "SELECT * FROM $tableName WHERE $columnName = \$1 LIMIT 1;"; -        $result = \pg_query_params($this->connection, $query, [ $entity->{$cols[$columnName]["propertyName"]} ]); +        if (!is_array($columnNames)) { +            $columnNames = [ $columnNames ]; +        } + +        $whereClause = []; +        $columnValues = []; +        $count = 0; +        foreach ($columnNames as $columnName) { +            ++$count; +            if (!isset($cols[$columnName])) +                throw new \RuntimeException("Column $columnName does not exist!"); +            $whereClause []= "$columnName = \$$count"; +            $columnValues []= self::stringifyValue($entity->{$cols[$columnName]["propertyName"]}, $cols[$columnName]["columnType"]); +        } +        $whereClause = implode(" AND ", $whereClause); +        $query = "SELECT * FROM $tableName WHERE $whereClause LIMIT 1;"; +        $result = \pg_query_params($this->connection, $query, $columnValues);          if ($result === false)              throw new \RuntimeException("Fetch failed: " . \pg_last_error($this->connection));          $row = \pg_fetch_assoc($result); diff --git a/src/application/views/form_login.php b/src/application/views/form_login.php index 27924e8..de8f28c 100644 --- a/src/application/views/form_login.php +++ b/src/application/views/form_login.php @@ -10,7 +10,7 @@ RequestUtils::clearLastForm();  ?>  <div class="page-header margin-top-0"> -    <h1>Log in</h1> +    <h1><?= __("Log in") ?></h1>  </div>  <div class="col-md-4"></div>  <div class="well col-md-4"> diff --git a/src/application/views/form_register.php b/src/application/views/form_register.php index 6f62652..83f3f4e 100644 --- a/src/application/views/form_register.php +++ b/src/application/views/form_register.php @@ -10,7 +10,7 @@ RequestUtils::clearLastForm();  ?>  <div class="page-header margin-top-0"> -    <h1>Register</h1> +    <h1><?= __("Register") ?></h1>  </div>  <div class="col-md-4"></div>  <div class="well col-md-4"> @@ -22,7 +22,7 @@ if (($_formError = RequestUtils::getAndClearFormError()) !== null) {  <form action="<?= htmlentities($_SERVER["REQUEST_URI"]) ?>" method="post">  <div class="form-group" id="group0">      <label for="i_username">Username:</label> -    <input class="form-control" id="i_username" type="text" name="username" value="<?= htmlentities($lastForm["username"] ?? "") ?>" required> +    <input class="form-control" id="i_username" type="text" name="username" value="" required>  </div>  <div class="form-group" id="group1"> @@ -79,5 +79,6 @@ $(function() {      $("#btn-refresh-captcha").click(function() {          $("#captcha-img").attr("src", "?_action=captcha&t=" + new Date().getTime().toString());      }); +    $("#i_username").prop("disabled", true).prop("required", false);  });  </script> diff --git a/src/application/views/nav_guest.php b/src/application/views/nav_guest.php index f897763..1c65a50 100644 --- a/src/application/views/nav_guest.php +++ b/src/application/views/nav_guest.php @@ -1,6 +1,6 @@  <ul class="nav navbar-nav navbar-right"> -<li<?= $GLOBALS["action"] === "auth" ? ' class="active"' : '' ?>><a href="?_action=auth&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>">Log in</a></li> +<li<?= $GLOBALS["action"] === "auth" ? ' class="active"' : '' ?>><a href="?_action=auth&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>"><?= __("Log in") ?></a></li>  <?php if (REGISTRATION_ENABLED): ?> -<li<?= $GLOBALS["action"] === "register" ? ' class="active"' : '' ?>><a href="?_action=register&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>">Register</a></li> +<li<?= $GLOBALS["action"] === "register" ? ' class="active"' : '' ?>><a href="?_action=register&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>"><?= __("Register") ?></a></li>  <?php endif; ?>  </ul>
\ No newline at end of file |