feat(12-01): add Laravel request validation to lagoon

- lagoon.ValidateRequest ports Laravel 9 request validation: wildcard
  expansion, implicit-rule stop, bail, sometimes/nullable/blank skipping,
  size messages split by type and character-counted string lengths
- ParseRules, In, CustomRule, UploadedFile and ErrorKeys for rule tables
- pl/en lagoon::validation catalogs ported verbatim from WinterCMS
- lagoon.Validate answers a numeric range failure with the bound that
  failed (min, max or numeric between) instead of always max
This commit is contained in:
Jakub Zych
2026-10-02 11:25:36 +02:00
parent cfe568a214
commit f9b7f2ea33
11 changed files with 2598 additions and 37 deletions

View File

@@ -12,7 +12,7 @@ phrasebook owns every translatable string in a SummerCMS application. At boot, `
- YAML catalogs laid out as `lang/<locale>/<group>.yaml`; nested maps flatten into keys such as `acme.blog::posts.title` (`phrasebook.Catalog.Load`). Duplicate keys, duplicate namespaces, malformed paths and non-string leaves fail at load time.
- Overrides laid out as `lang/<locale>/<namespace>/<group>.yaml` that replace keys of any loaded namespace, including the framework admin strings, and may add locales (`phrasebook.Catalog.Override`).
- Built-in framework namespaces: `lagoon::validate.*` (validation messages) and `backend::lang.*` (admin UI strings), shipped for `en` and `pl`.
- Built-in framework namespaces: `lagoon::validate.*` (the messages of `lagoon.Validate`), `lagoon::validation.*` (the WinterCMS/Laravel validator catalog used by `lagoon.ValidateRequest`, with the size messages split into `numeric`, `file`, `string` and `array`; a key the Polish file lacks, such as `after_or_equal`, falls back to English as in WinterCMS) and `backend::lang.*` (admin UI strings), shipped for `en` and `pl`.
- Lookups by request locale (`phrasebook.Translator.Get`, read from the context through `towel.Locale`) or by explicit locale (`phrasebook.Translator.GetIn`), walking the locale, its parent tags (`pt-BR` to `pt`) and then the fallback locale. A missing key returns the key itself.
- Laravel-style placeholders: `:name`, `:Name` (first letter upper-cased) and `:NAME` (upper-cased).
- Pluralization with `phrasebook.Translator.Choice` and `phrasebook.Translator.ChoiceIn`: CLDR plural maps (`one`, `few`, `many`, `other`, ...) validated against the locale's categories, or Laravel pipes with exact (`{0}`) and range (`[2,*]`) conditions. `:count` is filled in automatically.