diff options
| author | Jonas Kohl | 2024-09-16 13:27:51 +0200 | 
|---|---|---|
| committer | Jonas Kohl | 2024-09-16 13:27:51 +0200 | 
| commit | fd3d969cf658475709db6a1e5090069cce51cbbd (patch) | |
| tree | 2fba18a3eb37803afb37117fc100cc4292ba779d /src/application | |
| parent | 38f03c375eafdb6b95190729479c6fa0d721b400 (diff) | |
Even more i18n
Diffstat (limited to 'src/application')
| -rw-r--r-- | src/application/i18n.php | 176 | ||||
| -rw-r--r-- | src/application/messages/de.msg | 27 | ||||
| -rw-r--r-- | src/application/views/template_end.php | 18 | 
3 files changed, 188 insertions, 33 deletions
| diff --git a/src/application/i18n.php b/src/application/i18n.php index 2fcadab..f5b755d 100644 --- a/src/application/i18n.php +++ b/src/application/i18n.php @@ -1,72 +1,163 @@  <?php +use RR\Shunt\Context; +use RR\Shunt\Parser; +use RR\Shunt\Scanner; +  const MESSAGE_DIR = __DIR__ . "/messages";  $__i18n_msg_store = []; +$__i18n_msg_store_plural = []; +$__i18n_msg_metadata = [];  $__i18n_current_locale = null; +const _I18N_MSGID = 0; +const _I18N_MSGSTR = 1; +const _I18N_MSGIDP = 2; +const _I18N_MSGSTRP = 3; +const _I18N_META = 10; +  function i18n_parse(string $contents, ?string $filename = null): array {      $syntax_error = fn(string $msg, int $line): never =>          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 . ")"); +    $metadata = []; +          $msgs = []; +    $pmsgs = [];      $lines = explode("\n", $contents);      $currentId = "";      $currentContext = "";      $currentMessage = ""; -    $isInMessage = false; +    $currentIdP = []; +    $currentMessageP = []; +    $currentMetadataBuffer = []; +    $state = _I18N_MSGID; +     +    $processedLines = 0;      foreach ($lines as $i => $ln) {          $lnNum = $i + 1;          if (trim($ln) === "")              continue; +        if ($ln === "metadata({") { +            if ($processedLines > 0) +                $syntax_error("metadata must come first in file", $lnNum); +            $currentMetadataBuffer []= "{"; +            $state = _I18N_META; +            ++$processedLines; +            continue; +        } +        if ($state == _I18N_META) { +            if ($ln === "})") { +                $currentMetadataBuffer []= "}"; +                $state = _I18N_MSGSTR; +                $currentMetadataBuffer = implode("\n", $currentMetadataBuffer); +                $metadata = json_decode($currentMetadataBuffer, true); +            } else { +                $currentMetadataBuffer []= $ln; +            } +            ++$processedLines; +            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; +                if ($state === _I18N_MSGSTRP) { +                    // plural +                    if (count($currentIdP) > 0) { +                        if (isset($msgs[$currentId])) +                            $other_error("duplicate message id '$currentId'", $lnNum); +                        $pmsgs[$currentIdP[0]] = $currentMessageP; +                    } +                } else { +                    // singular +                    if ($currentId !== "") { +                        if ($currentContext !== "") +                            $currentId = $currentContext . "\x7F" . $currentId; +                        if (isset($msgs[$currentId])) +                            $other_error("duplicate message id '$currentId'", $lnNum); +                        $msgs[$currentId] = $currentMessage; +                    }                  } -                $currentId = json_decode(substr($ln, 2)); +                $currentIdP = []; +                $currentMessageP = []; +                $currentId = "";                  $currentContext = "";                  $currentMessage = ""; -                $isInMessage = false; +                if ($ln === ":...") { +                    // plural +                    $state = _I18N_MSGIDP; +                } else { +                    // singular +                    $currentId = json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum); +                    $state = _I18N_MSGID; +                } +                break; +            case "-": +                if ($state === _I18N_MSGIDP) { +                    $_id = json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum); +                    if ($currentContext !== "") +                        $_id = $currentContext . "\x7F" . $_id; +                    $currentIdP []= $_id; +                } elseif ($state === _I18N_MSGSTRP) { +                    $currentMessageP []= json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum); +                } else +                    $syntax_error("cannot define multiple ids/messages in singular mode", $lnNum);                  break;              case "=": -                $isInMessage = true; -                $currentMessage = json_decode(substr($ln, 2)); +                if ($ln === "=...") { +                    // plural +                    if ($state !== _I18N_MSGIDP) +                        $syntax_error("cannot start plural message in singular mode", $lnNum); +                    $state = _I18N_MSGSTRP; +                } else { +                    // singular +                    $state = _I18N_MSGSTR; +                    $currentMessage = json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum); +                }                  break;              case "?": -                if ($isInMessage) +                if ($state !== _I18N_MSGID)                      $syntax_error("context must be defined before start of message", $lnNum); -                $currentContext = json_decode(substr($ln, 2)); +                $currentContext = json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum);                  break;              case " ": -                if ($isInMessage) -                    $currentMessage .= json_decode(substr($ln, 2)); +                if ($state === _I18N_MSGSTR) +                    $currentMessage .= json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum);                  else -                    $currentId .= json_decode(substr($ln, 2)); +                    $currentId .= json_decode(substr($ln, 2)) ?? $syntax_error("malformed string", $lnNum);                  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;          } +         +        ++$processedLines;      } -    if ($currentId !== "") { -        if ($currentContext !== "") -            $currentId = $currentContext . "\x7F" . $currentId; -        if (isset($msgs[$currentId])) -            $other_error("duplicate message id '$currentId'", count($lines)); -        $msgs[$currentId] = $currentMessage; +    if ($state === _I18N_MSGSTRP) { +        // plural +        if (count($currentIdP) > 0) { +            if (isset($msgs[$currentId])) +                $other_error("duplicate message id '$currentId'", $lnNum); +            $pmsgs[$currentIdP[0]] = $currentMessageP; +        } +    } else { +        // singular +        if ($currentId !== "") { +            if ($currentContext !== "") +                $currentId = $currentContext . "\x7F" . $currentId; +            if (isset($msgs[$currentId])) +                $other_error("duplicate message id '$currentId'", $lnNum); +            $msgs[$currentId] = $currentMessage; +        }      } -    return $msgs; +    return [$metadata, $msgs, $pmsgs];  }  function i18n_locale(string $locale) { @@ -91,14 +182,51 @@ function i18n_get(string $msgid, array $params = [], ?string $context = null): s      );  } +function i18n_get_plural(string $msgid_singular, string $msgid_plural, int $count, array $params = [], ?string $context = null): string { +    global $__i18n_current_locale, $__i18n_msg_metadata, $__i18n_msg_store_plural; + +    $key = $msgid_singular; +    if ($context !== null) +        $key = $context . "\x7F" . $key; + +    $msgs = ($__i18n_msg_store_plural[$__i18n_current_locale] ?? [])[$msgid_singular] ?? null; +    $msg = ""; +    if ($msgs === null) { +        $msg = $count === 1 ? $msgid_singular : $msgid_plural; +    } else { +        $expression = (($__i18n_msg_metadata[$__i18n_current_locale] ?? [])["plural"] ?? [])["indexMapping"] ?? "n == 1 ? 0 : 1"; +        $ctx = new Context(); +        $ctx->def("ifelse", function(bool $c, $a, $b) { return $c ? $a : $b; }); +        $ctx->def("n", $count); +        $parser = new Parser(new Scanner($expression)); +        $index = $parser->reduce($ctx); +        $index = max(0, min(count($msgs), $index)); +        $msg = $msgs[$index]; +    } + +    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);  } +function ___(string $msgid_singular, string $msgid_plural, int $count, array $params = [], ?string $context = null): string { +    return i18n_get_plural($msgid_singular, $msgid_plural, $count, $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); +    [$meta, $msgs, $pmsgs] = i18n_parse(file_get_contents($path), $ent); +    $__i18n_msg_store[$lang] = $msgs; +    $__i18n_msg_store_plural[$lang] = $pmsgs; +    $__i18n_msg_metadata[$lang] = $meta;  } diff --git a/src/application/messages/de.msg b/src/application/messages/de.msg index 692e559..c797241 100644 --- a/src/application/messages/de.msg +++ b/src/application/messages/de.msg @@ -1,3 +1,9 @@ +metadata({ +  "plural": { +    "indexMapping": "ifelse(n = 0, 2, ifelse(n = 1, 0, 1))" +  } +}) +  : "Log in"  = "Anmelden" @@ -254,9 +260,18 @@  : "Upload new profile picture"  = "Neues Profilbild hochladen" - -#:: -#- "Select file" -#- "Select files" -#= "Datei auswählen" -#- "Dateien auswählen" +:... +- "Select file" +- "Select files" +=... +- "Datei auswählen" +- "Dateien auswählen" + +:... +- "%n% file selected" +- "%n% files selected" +- "No files selected" +=... +- "%n% Datei ausgewählt" +- "%n% Dateien ausgewählt" +- "Keine Datei ausgewählt" diff --git a/src/application/views/template_end.php b/src/application/views/template_end.php index 7757780..c30330e 100644 --- a/src/application/views/template_end.php +++ b/src/application/views/template_end.php @@ -12,6 +12,18 @@  <script>  $(function() { +    var _messages = <?= json_encode([ +        "selectFiles" => [ +            ___("Select file", "Select files", 1), +            ___("Select file", "Select files", 2), +        ], +        "filesSelected" => [ +            ___("%n% file selected", "No files selected", 0), +            ___("%n% file selected", "%n% files selected", 1), +            ___("%n% file selected", "%n% files selected", 2), +        ], +    ]) ?> +      $("._time").each(function(i, e) {          var date = new Date($(e).text());          $(e).text(date.toLocaleString()); @@ -27,11 +39,11 @@ $(function() {      $("input[type=file]").each(function(i, e) {          var isMultiple = !!$(e).prop("multiple"); -        var $input = $('<input type="text" readonly class="form-control" />').attr("placeholder", "No file" + (isMultiple ? "s" : "") + " selected"); +        var $input = $('<input type="text" readonly class="form-control" />').attr("placeholder", _messages.filesSelected[0]);          $(e).after($('<div class="input-group file-input-group"></div>').append(              $input,              $('<span class="input-group-btn"></span>').append( -                $('<button class="btn btn-default" type="button"></button>').text("Select file" + (isMultiple ? "s" : "") + "...").click(function() { +                $('<button class="btn btn-default" type="button"></button>').text(_messages.selectFiles[isMultiple ? 1 : 0]).click(function() {                      $(e).click();                  })              ) @@ -43,7 +55,7 @@ $(function() {              else if (files.length === 1)                  $input.val(files[0].name);              else -                $input.val(files.length + " files selected"); +                $input.val(_messages.filesSelected[2].replace("%n%", files.length));          });      })  }); |