This commit is contained in:
2026-05-01 23:40:14 +08:00
commit b8f599a617
3867 changed files with 478663 additions and 0 deletions
+2148
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
{
"name": "webman/validation",
"type": "library",
"license": "MIT",
"description": "Webman plugin webman/validation",
"require": {
"illuminate/validation": "*",
"illuminate/translation": "*",
"workerman/webman-framework": "^2.1 || dev-master",
"webman/console": ">=2.1.7"
},
"autoload": {
"psr-4": {
"Webman\\Validation\\": "src",
"support\\validation\\": "src/support/validation"
}
},
"autoload-dev": {
"psr-4": {
"Webman\\Validation\\Tests\\": "src/Tests"
}
},
"require-dev": {
"phpunit/phpunit": "*"
}
}
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="src/Tests/bootstrap.php"
colors="true"
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="webman-validation">
<directory>src/Tests</directory>
</testsuite>
</testsuites>
</phpunit>
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validierungsmeldungen
|--------------------------------------------------------------------------
*/
'accepted' => 'Das Feld :attribute muss akzeptiert werden.',
'accepted_if' => 'Das Feld :attribute muss akzeptiert werden, wenn :other :value ist.',
'active_url' => 'Das Feld :attribute muss eine gültige URL sein.',
'after' => 'Das Feld :attribute muss ein Datum nach dem :date sein.',
'after_or_equal' => 'Das Feld :attribute muss ein Datum nach oder gleich :date sein.',
'alpha' => 'Das Feld :attribute darf nur Buchstaben enthalten.',
'alpha_dash' => 'Das Feld :attribute darf nur Buchstaben, Zahlen, Bindestriche und Unterstriche enthalten.',
'alpha_num' => 'Das Feld :attribute darf nur Buchstaben und Zahlen enthalten.',
'any_of' => 'Das Feld :attribute ist ungültig.',
'array' => 'Das Feld :attribute muss ein Array sein.',
'ascii' => 'Das Feld :attribute darf nur einbyte alphanumerische Zeichen und Symbole enthalten.',
'before' => 'Das Feld :attribute muss ein Datum vor dem :date sein.',
'before_or_equal' => 'Das Feld :attribute muss ein Datum vor oder gleich :date sein.',
'between' => [
'array' => 'Das Feld :attribute muss zwischen :min und :max Elemente haben.',
'file' => 'Das Feld :attribute muss zwischen :min und :max Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss zwischen :min und :max liegen.',
'string' => 'Das Feld :attribute muss zwischen :min und :max Zeichen haben.',
],
'boolean' => 'Das Feld :attribute muss wahr oder falsch sein.',
'can' => 'Das Feld :attribute enthält einen nicht autorisierten Wert.',
'confirmed' => 'Die Bestätigung des Feldes :attribute stimmt nicht überein.',
'contains' => 'Das Feld :attribute enthält keinen erforderlichen Wert.',
'current_password' => 'Das Passwort ist falsch.',
'date' => 'Das Feld :attribute muss ein gültiges Datum sein.',
'date_equals' => 'Das Feld :attribute muss ein Datum gleich :date sein.',
'date_format' => 'Das Feld :attribute muss dem Format :format entsprechen.',
'decimal' => 'Das Feld :attribute muss :decimal Dezimalstellen haben.',
'declined' => 'Das Feld :attribute muss abgelehnt werden.',
'declined_if' => 'Das Feld :attribute muss abgelehnt werden, wenn :other :value ist.',
'different' => 'Die Felder :attribute und :other müssen unterschiedlich sein.',
'digits' => 'Das Feld :attribute muss :digits Ziffern haben.',
'digits_between' => 'Das Feld :attribute muss zwischen :min und :max Ziffern haben.',
'dimensions' => 'Das Feld :attribute hat ungültige Bildabmessungen.',
'distinct' => 'Das Feld :attribute hat einen doppelten Wert.',
'doesnt_contain' => 'Das Feld :attribute darf Folgendes nicht enthalten: :values.',
'doesnt_end_with' => 'Das Feld :attribute darf nicht mit Folgendem enden: :values.',
'doesnt_start_with' => 'Das Feld :attribute darf nicht mit Folgendem beginnen: :values.',
'email' => 'Das Feld :attribute muss eine gültige E-Mail-Adresse sein.',
'encoding' => 'Das Feld :attribute muss in :encoding kodiert sein.',
'ends_with' => 'Das Feld :attribute muss mit Folgendem enden: :values.',
'enum' => 'Die ausgewählte :attribute ist ungültig.',
'exists' => 'Die ausgewählte :attribute ist ungültig.',
'extensions' => 'Das Feld :attribute muss eine der folgenden Erweiterungen haben: :values.',
'file' => 'Das Feld :attribute muss eine Datei sein.',
'filled' => 'Das Feld :attribute muss einen Wert haben.',
'gt' => [
'array' => 'Das Feld :attribute muss mehr als :value Elemente haben.',
'file' => 'Das Feld :attribute muss größer als :value Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss größer als :value sein.',
'string' => 'Das Feld :attribute muss mehr als :value Zeichen haben.',
],
'gte' => [
'array' => 'Das Feld :attribute muss :value oder mehr Elemente haben.',
'file' => 'Das Feld :attribute muss größer oder gleich :value Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss größer oder gleich :value sein.',
'string' => 'Das Feld :attribute muss :value oder mehr Zeichen haben.',
],
'hex_color' => 'Das Feld :attribute muss eine gültige Hexadezimalfarbe sein.',
'image' => 'Das Feld :attribute muss ein Bild sein.',
'in' => 'Die ausgewählte :attribute ist ungültig.',
'in_array' => 'Das Feld :attribute muss in :other existieren.',
'in_array_keys' => 'Das Feld :attribute muss mindestens einen der folgenden Schlüssel enthalten: :values.',
'integer' => 'Das Feld :attribute muss eine ganze Zahl sein.',
'ip' => 'Das Feld :attribute muss eine gültige IP-Adresse sein.',
'ipv4' => 'Das Feld :attribute muss eine gültige IPv4-Adresse sein.',
'ipv6' => 'Das Feld :attribute muss eine gültige IPv6-Adresse sein.',
'json' => 'Das Feld :attribute muss eine gültige JSON-Zeichenkette sein.',
'list' => 'Das Feld :attribute muss eine Liste sein.',
'lowercase' => 'Das Feld :attribute muss in Kleinbuchstaben sein.',
'lt' => [
'array' => 'Das Feld :attribute muss weniger als :value Elemente haben.',
'file' => 'Das Feld :attribute muss kleiner als :value Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss kleiner als :value sein.',
'string' => 'Das Feld :attribute muss weniger als :value Zeichen haben.',
],
'lte' => [
'array' => 'Das Feld :attribute darf nicht mehr als :value Elemente haben.',
'file' => 'Das Feld :attribute muss kleiner oder gleich :value Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss kleiner oder gleich :value sein.',
'string' => 'Das Feld :attribute darf nicht mehr als :value Zeichen haben.',
],
'mac_address' => 'Das Feld :attribute muss eine gültige MAC-Adresse sein.',
'max' => [
'array' => 'Das Feld :attribute darf nicht mehr als :max Elemente haben.',
'file' => 'Das Feld :attribute darf nicht größer als :max Kilobyte sein.',
'numeric' => 'Das Feld :attribute darf nicht größer als :max sein.',
'string' => 'Das Feld :attribute darf nicht mehr als :max Zeichen haben.',
],
'max_digits' => 'Das Feld :attribute darf nicht mehr als :max Ziffern haben.',
'mimes' => 'Das Feld :attribute muss eine Datei vom Typ :values sein.',
'mimetypes' => 'Das Feld :attribute muss eine Datei vom Typ :values sein.',
'min' => [
'array' => 'Das Feld :attribute muss mindestens :min Elemente haben.',
'file' => 'Das Feld :attribute muss mindestens :min Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss mindestens :min sein.',
'string' => 'Das Feld :attribute muss mindestens :min Zeichen haben.',
],
'min_digits' => 'Das Feld :attribute muss mindestens :min Ziffern haben.',
'missing' => 'Das Feld :attribute muss fehlen.',
'missing_if' => 'Das Feld :attribute muss fehlen, wenn :other :value ist.',
'missing_unless' => 'Das Feld :attribute muss fehlen, es sei denn :other ist :value.',
'missing_with' => 'Das Feld :attribute muss fehlen, wenn :values vorhanden ist.',
'missing_with_all' => 'Das Feld :attribute muss fehlen, wenn :values vorhanden sind.',
'multiple_of' => 'Das Feld :attribute muss ein Vielfaches von :value sein.',
'not_in' => 'Die ausgewählte :attribute ist ungültig.',
'not_regex' => 'Das Format des Feldes :attribute ist ungültig.',
'numeric' => 'Das Feld :attribute muss eine Zahl sein.',
'password' => [
'letters' => 'Das Feld :attribute muss mindestens einen Buchstaben enthalten.',
'mixed' => 'Das Feld :attribute muss mindestens einen Groß- und einen Kleinbuchstaben enthalten.',
'numbers' => 'Das Feld :attribute muss mindestens eine Zahl enthalten.',
'symbols' => 'Das Feld :attribute muss mindestens ein Symbol enthalten.',
'uncompromised' => 'Die angegebene :attribute ist in einem Datenleck aufgetaucht. Bitte wählen Sie eine andere :attribute.',
],
'present' => 'Das Feld :attribute muss vorhanden sein.',
'present_if' => 'Das Feld :attribute muss vorhanden sein, wenn :other :value ist.',
'present_unless' => 'Das Feld :attribute muss vorhanden sein, es sei denn :other ist :value.',
'present_with' => 'Das Feld :attribute muss vorhanden sein, wenn :values vorhanden ist.',
'present_with_all' => 'Das Feld :attribute muss vorhanden sein, wenn :values vorhanden sind.',
'prohibited' => 'Das Feld :attribute ist verboten.',
'prohibited_if' => 'Das Feld :attribute ist verboten, wenn :other :value ist.',
'prohibited_if_accepted' => 'Das Feld :attribute ist verboten, wenn :other akzeptiert wird.',
'prohibited_if_declined' => 'Das Feld :attribute ist verboten, wenn :other abgelehnt wird.',
'prohibited_unless' => 'Das Feld :attribute ist verboten, es sei denn :other ist in :values.',
'prohibits' => 'Das Feld :attribute verbietet die Anwesenheit von :other.',
'regex' => 'Das Format des Feldes :attribute ist ungültig.',
'required' => 'Das Feld :attribute ist erforderlich.',
'required_array_keys' => 'Das Feld :attribute muss Einträge für Folgendes enthalten: :values.',
'required_if' => 'Das Feld :attribute ist erforderlich, wenn :other :value ist.',
'required_if_accepted' => 'Das Feld :attribute ist erforderlich, wenn :other akzeptiert wird.',
'required_if_declined' => 'Das Feld :attribute ist erforderlich, wenn :other abgelehnt wird.',
'required_unless' => 'Das Feld :attribute ist erforderlich, es sei denn :other ist in :values.',
'required_with' => 'Das Feld :attribute ist erforderlich, wenn :values vorhanden ist.',
'required_with_all' => 'Das Feld :attribute ist erforderlich, wenn :values vorhanden sind.',
'required_without' => 'Das Feld :attribute ist erforderlich, wenn :values nicht vorhanden ist.',
'required_without_all' => 'Das Feld :attribute ist erforderlich, wenn keines von :values vorhanden ist.',
'same' => 'Die Felder :attribute und :other müssen übereinstimmen.',
'size' => [
'array' => 'Das Feld :attribute muss :size Elemente enthalten.',
'file' => 'Das Feld :attribute muss :size Kilobyte sein.',
'numeric' => 'Das Feld :attribute muss :size sein.',
'string' => 'Das Feld :attribute muss :size Zeichen haben.',
],
'starts_with' => 'Das Feld :attribute muss mit Folgendem beginnen: :values.',
'string' => 'Das Feld :attribute muss eine Zeichenkette sein.',
'timezone' => 'Das Feld :attribute muss eine gültige Zeitzone sein.',
'unique' => 'Die :attribute wurde bereits verwendet.',
'uploaded' => 'Der Upload von :attribute ist fehlgeschlagen.',
'uppercase' => 'Das Feld :attribute muss in Großbuchstaben sein.',
'url' => 'Das Feld :attribute muss eine gültige URL sein.',
'ulid' => 'Das Feld :attribute muss eine gültige ULID sein.',
'uuid' => 'Das Feld :attribute muss eine gültige UUID sein.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,180 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
*/
'accepted' => 'The :attribute field must be accepted.',
'accepted_if' => 'The :attribute field must be accepted when :other is :value.',
'active_url' => 'The :attribute field must be a valid URL.',
'after' => 'The :attribute field must be a date after :date.',
'after_or_equal' => 'The :attribute field must be a date after or equal to :date.',
'alpha' => 'The :attribute field must only contain letters.',
'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.',
'alpha_num' => 'The :attribute field must only contain letters and numbers.',
'any_of' => 'The :attribute field is invalid.',
'array' => 'The :attribute field must be an array.',
'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.',
'before' => 'The :attribute field must be a date before :date.',
'before_or_equal' => 'The :attribute field must be a date before or equal to :date.',
'between' => [
'array' => 'The :attribute field must have between :min and :max items.',
'file' => 'The :attribute field must be between :min and :max kilobytes.',
'numeric' => 'The :attribute field must be between :min and :max.',
'string' => 'The :attribute field must be between :min and :max characters.',
],
'boolean' => 'The :attribute field must be true or false.',
'can' => 'The :attribute field contains an unauthorized value.',
'confirmed' => 'The :attribute field confirmation does not match.',
'contains' => 'The :attribute field is missing a required value.',
'current_password' => 'The password is incorrect.',
'date' => 'The :attribute field must be a valid date.',
'date_equals' => 'The :attribute field must be a date equal to :date.',
'date_format' => 'The :attribute field must match the format :format.',
'decimal' => 'The :attribute field must have :decimal decimal places.',
'declined' => 'The :attribute field must be declined.',
'declined_if' => 'The :attribute field must be declined when :other is :value.',
'different' => 'The :attribute field and :other must be different.',
'digits' => 'The :attribute field must be :digits digits.',
'digits_between' => 'The :attribute field must be between :min and :max digits.',
'dimensions' => 'The :attribute field has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'doesnt_contain' => 'The :attribute field must not contain any of the following: :values.',
'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.',
'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.',
'email' => 'The :attribute field must be a valid email address.',
'encoding' => 'The :attribute field must be encoded in :encoding.',
'ends_with' => 'The :attribute field must end with one of the following: :values.',
'enum' => 'The selected :attribute is invalid.',
'exists' => 'The selected :attribute is invalid.',
'extensions' => 'The :attribute field must have one of the following extensions: :values.',
'file' => 'The :attribute field must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'array' => 'The :attribute field must have more than :value items.',
'file' => 'The :attribute field must be greater than :value kilobytes.',
'numeric' => 'The :attribute field must be greater than :value.',
'string' => 'The :attribute field must be greater than :value characters.',
],
'gte' => [
'array' => 'The :attribute field must have :value items or more.',
'file' => 'The :attribute field must be greater than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be greater than or equal to :value.',
'string' => 'The :attribute field must be greater than or equal to :value characters.',
],
'hex_color' => 'The :attribute field must be a valid hexadecimal color.',
'image' => 'The :attribute field must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field must exist in :other.',
'in_array_keys' => 'The :attribute field must contain at least one of the following keys: :values.',
'integer' => 'The :attribute field must be an integer.',
'ip' => 'The :attribute field must be a valid IP address.',
'ipv4' => 'The :attribute field must be a valid IPv4 address.',
'ipv6' => 'The :attribute field must be a valid IPv6 address.',
'json' => 'The :attribute field must be a valid JSON string.',
'list' => 'The :attribute field must be a list.',
'lowercase' => 'The :attribute field must be lowercase.',
'lt' => [
'array' => 'The :attribute field must have less than :value items.',
'file' => 'The :attribute field must be less than :value kilobytes.',
'numeric' => 'The :attribute field must be less than :value.',
'string' => 'The :attribute field must be less than :value characters.',
],
'lte' => [
'array' => 'The :attribute field must not have more than :value items.',
'file' => 'The :attribute field must be less than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be less than or equal to :value.',
'string' => 'The :attribute field must be less than or equal to :value characters.',
],
'mac_address' => 'The :attribute field must be a valid MAC address.',
'max' => [
'array' => 'The :attribute field must not have more than :max items.',
'file' => 'The :attribute field must not be greater than :max kilobytes.',
'numeric' => 'The :attribute field must not be greater than :max.',
'string' => 'The :attribute field must not be greater than :max characters.',
],
'max_digits' => 'The :attribute field must not have more than :max digits.',
'mimes' => 'The :attribute field must be a file of type: :values.',
'mimetypes' => 'The :attribute field must be a file of type: :values.',
'min' => [
'array' => 'The :attribute field must have at least :min items.',
'file' => 'The :attribute field must be at least :min kilobytes.',
'numeric' => 'The :attribute field must be at least :min.',
'string' => 'The :attribute field must be at least :min characters.',
],
'min_digits' => 'The :attribute field must have at least :min digits.',
'missing' => 'The :attribute field must be missing.',
'missing_if' => 'The :attribute field must be missing when :other is :value.',
'missing_unless' => 'The :attribute field must be missing unless :other is :value.',
'missing_with' => 'The :attribute field must be missing when :values is present.',
'missing_with_all' => 'The :attribute field must be missing when :values are present.',
'multiple_of' => 'The :attribute field must be a multiple of :value.',
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute field format is invalid.',
'numeric' => 'The :attribute field must be a number.',
'password' => [
'letters' => 'The :attribute field must contain at least one letter.',
'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.',
'numbers' => 'The :attribute field must contain at least one number.',
'symbols' => 'The :attribute field must contain at least one symbol.',
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
],
'present' => 'The :attribute field must be present.',
'present_if' => 'The :attribute field must be present when :other is :value.',
'present_unless' => 'The :attribute field must be present unless :other is :value.',
'present_with' => 'The :attribute field must be present when :values is present.',
'present_with_all' => 'The :attribute field must be present when :values are present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_if_accepted' => 'The :attribute field is prohibited when :other is accepted.',
'prohibited_if_declined' => 'The :attribute field is prohibited when :other is declined.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
'prohibits' => 'The :attribute field prohibits :other from being present.',
'regex' => 'The :attribute field format is invalid.',
'required' => 'The :attribute field is required.',
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
'required_if_declined' => 'The :attribute field is required when :other is declined.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values are present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute field must match :other.',
'size' => [
'array' => 'The :attribute field must contain :size items.',
'file' => 'The :attribute field must be :size kilobytes.',
'numeric' => 'The :attribute field must be :size.',
'string' => 'The :attribute field must be :size characters.',
],
'starts_with' => 'The :attribute field must start with one of the following: :values.',
'string' => 'The :attribute field must be a string.',
'timezone' => 'The :attribute field must be a valid timezone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'uppercase' => 'The :attribute field must be uppercase.',
'url' => 'The :attribute field must be a valid URL.',
'ulid' => 'The :attribute field must be a valid ULID.',
'uuid' => 'The :attribute field must be a valid UUID.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
*/
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Mensajes de validación
|--------------------------------------------------------------------------
*/
'accepted' => 'El campo :attribute debe ser aceptado.',
'accepted_if' => 'El campo :attribute debe ser aceptado cuando :other sea :value.',
'active_url' => 'El campo :attribute debe ser una URL válida.',
'after' => 'El campo :attribute debe ser una fecha posterior a :date.',
'after_or_equal' => 'El campo :attribute debe ser una fecha posterior o igual a :date.',
'alpha' => 'El campo :attribute solo debe contener letras.',
'alpha_dash' => 'El campo :attribute solo debe contener letras, números, guiones y guiones bajos.',
'alpha_num' => 'El campo :attribute solo debe contener letras y números.',
'any_of' => 'El campo :attribute no es válido.',
'array' => 'El campo :attribute debe ser un array.',
'ascii' => 'El campo :attribute solo debe contener caracteres alfanuméricos y símbolos de un byte.',
'before' => 'El campo :attribute debe ser una fecha anterior a :date.',
'before_or_equal' => 'El campo :attribute debe ser una fecha anterior o igual a :date.',
'between' => [
'array' => 'El campo :attribute debe tener entre :min y :max elementos.',
'file' => 'El campo :attribute debe tener entre :min y :max kilobytes.',
'numeric' => 'El campo :attribute debe estar entre :min y :max.',
'string' => 'El campo :attribute debe tener entre :min y :max caracteres.',
],
'boolean' => 'El campo :attribute debe ser verdadero o falso.',
'can' => 'El campo :attribute contiene un valor no autorizado.',
'confirmed' => 'La confirmación del campo :attribute no coincide.',
'contains' => 'El campo :attribute no contiene un valor requerido.',
'current_password' => 'La contraseña es incorrecta.',
'date' => 'El campo :attribute debe ser una fecha válida.',
'date_equals' => 'El campo :attribute debe ser una fecha igual a :date.',
'date_format' => 'El campo :attribute debe coincidir con el formato :format.',
'decimal' => 'El campo :attribute debe tener :decimal decimales.',
'declined' => 'El campo :attribute debe ser rechazado.',
'declined_if' => 'El campo :attribute debe ser rechazado cuando :other sea :value.',
'different' => 'Los campos :attribute y :other deben ser diferentes.',
'digits' => 'El campo :attribute debe tener :digits dígitos.',
'digits_between' => 'El campo :attribute debe tener entre :min y :max dígitos.',
'dimensions' => 'El campo :attribute tiene dimensiones de imagen inválidas.',
'distinct' => 'El campo :attribute tiene un valor duplicado.',
'doesnt_contain' => 'El campo :attribute no debe contener: :values.',
'doesnt_end_with' => 'El campo :attribute no debe terminar con: :values.',
'doesnt_start_with' => 'El campo :attribute no debe comenzar con: :values.',
'email' => 'El campo :attribute debe ser una dirección de correo electrónico válida.',
'encoding' => 'El campo :attribute debe estar codificado en :encoding.',
'ends_with' => 'El campo :attribute debe terminar con: :values.',
'enum' => 'El :attribute seleccionado no es válido.',
'exists' => 'El :attribute seleccionado no es válido.',
'extensions' => 'El campo :attribute debe tener una de las siguientes extensiones: :values.',
'file' => 'El campo :attribute debe ser un archivo.',
'filled' => 'El campo :attribute debe tener un valor.',
'gt' => [
'array' => 'El campo :attribute debe tener más de :value elementos.',
'file' => 'El campo :attribute debe ser mayor que :value kilobytes.',
'numeric' => 'El campo :attribute debe ser mayor que :value.',
'string' => 'El campo :attribute debe tener más de :value caracteres.',
],
'gte' => [
'array' => 'El campo :attribute debe tener :value elementos o más.',
'file' => 'El campo :attribute debe ser mayor o igual que :value kilobytes.',
'numeric' => 'El campo :attribute debe ser mayor o igual que :value.',
'string' => 'El campo :attribute debe tener :value caracteres o más.',
],
'hex_color' => 'El campo :attribute debe ser un color hexadecimal válido.',
'image' => 'El campo :attribute debe ser una imagen.',
'in' => 'El :attribute seleccionado no es válido.',
'in_array' => 'El campo :attribute debe existir en :other.',
'in_array_keys' => 'El campo :attribute debe contener al menos una de las siguientes claves: :values.',
'integer' => 'El campo :attribute debe ser un número entero.',
'ip' => 'El campo :attribute debe ser una dirección IP válida.',
'ipv4' => 'El campo :attribute debe ser una dirección IPv4 válida.',
'ipv6' => 'El campo :attribute debe ser una dirección IPv6 válida.',
'json' => 'El campo :attribute debe ser una cadena JSON válida.',
'list' => 'El campo :attribute debe ser una lista.',
'lowercase' => 'El campo :attribute debe estar en minúsculas.',
'lt' => [
'array' => 'El campo :attribute debe tener menos de :value elementos.',
'file' => 'El campo :attribute debe ser menor que :value kilobytes.',
'numeric' => 'El campo :attribute debe ser menor que :value.',
'string' => 'El campo :attribute debe tener menos de :value caracteres.',
],
'lte' => [
'array' => 'El campo :attribute no debe tener más de :value elementos.',
'file' => 'El campo :attribute debe ser menor o igual que :value kilobytes.',
'numeric' => 'El campo :attribute debe ser menor o igual que :value.',
'string' => 'El campo :attribute debe tener :value caracteres o menos.',
],
'mac_address' => 'El campo :attribute debe ser una dirección MAC válida.',
'max' => [
'array' => 'El campo :attribute no debe tener más de :max elementos.',
'file' => 'El campo :attribute no debe ser mayor que :max kilobytes.',
'numeric' => 'El campo :attribute no debe ser mayor que :max.',
'string' => 'El campo :attribute no debe tener más de :max caracteres.',
],
'max_digits' => 'El campo :attribute no debe tener más de :max dígitos.',
'mimes' => 'El campo :attribute debe ser un archivo de tipo: :values.',
'mimetypes' => 'El campo :attribute debe ser un archivo de tipo: :values.',
'min' => [
'array' => 'El campo :attribute debe tener al menos :min elementos.',
'file' => 'El campo :attribute debe tener al menos :min kilobytes.',
'numeric' => 'El campo :attribute debe ser al menos :min.',
'string' => 'El campo :attribute debe tener al menos :min caracteres.',
],
'min_digits' => 'El campo :attribute debe tener al menos :min dígitos.',
'missing' => 'El campo :attribute debe estar ausente.',
'missing_if' => 'El campo :attribute debe estar ausente cuando :other sea :value.',
'missing_unless' => 'El campo :attribute debe estar ausente a menos que :other sea :value.',
'missing_with' => 'El campo :attribute debe estar ausente cuando :values esté presente.',
'missing_with_all' => 'El campo :attribute debe estar ausente cuando :values estén presentes.',
'multiple_of' => 'El campo :attribute debe ser un múltiplo de :value.',
'not_in' => 'El :attribute seleccionado no es válido.',
'not_regex' => 'El formato del campo :attribute no es válido.',
'numeric' => 'El campo :attribute debe ser un número.',
'password' => [
'letters' => 'El campo :attribute debe contener al menos una letra.',
'mixed' => 'El campo :attribute debe contener al menos una mayúscula y una minúscula.',
'numbers' => 'El campo :attribute debe contener al menos un número.',
'symbols' => 'El campo :attribute debe contener al menos un símbolo.',
'uncompromised' => 'El :attribute proporcionado ha aparecido en una filtración de datos. Por favor elija un :attribute diferente.',
],
'present' => 'El campo :attribute debe estar presente.',
'present_if' => 'El campo :attribute debe estar presente cuando :other sea :value.',
'present_unless' => 'El campo :attribute debe estar presente a menos que :other sea :value.',
'present_with' => 'El campo :attribute debe estar presente cuando :values esté presente.',
'present_with_all' => 'El campo :attribute debe estar presente cuando :values estén presentes.',
'prohibited' => 'El campo :attribute está prohibido.',
'prohibited_if' => 'El campo :attribute está prohibido cuando :other sea :value.',
'prohibited_if_accepted' => 'El campo :attribute está prohibido cuando :other sea aceptado.',
'prohibited_if_declined' => 'El campo :attribute está prohibido cuando :other sea rechazado.',
'prohibited_unless' => 'El campo :attribute está prohibido a menos que :other esté en :values.',
'prohibits' => 'El campo :attribute prohíbe que :other esté presente.',
'regex' => 'El formato del campo :attribute no es válido.',
'required' => 'El campo :attribute es obligatorio.',
'required_array_keys' => 'El campo :attribute debe contener entradas para: :values.',
'required_if' => 'El campo :attribute es obligatorio cuando :other sea :value.',
'required_if_accepted' => 'El campo :attribute es obligatorio cuando :other sea aceptado.',
'required_if_declined' => 'El campo :attribute es obligatorio cuando :other sea rechazado.',
'required_unless' => 'El campo :attribute es obligatorio a menos que :other esté en :values.',
'required_with' => 'El campo :attribute es obligatorio cuando :values esté presente.',
'required_with_all' => 'El campo :attribute es obligatorio cuando :values estén presentes.',
'required_without' => 'El campo :attribute es obligatorio cuando :values no esté presente.',
'required_without_all' => 'El campo :attribute es obligatorio cuando ninguno de :values esté presente.',
'same' => 'Los campos :attribute y :other deben coincidir.',
'size' => [
'array' => 'El campo :attribute debe contener :size elementos.',
'file' => 'El campo :attribute debe tener :size kilobytes.',
'numeric' => 'El campo :attribute debe ser :size.',
'string' => 'El campo :attribute debe tener :size caracteres.',
],
'starts_with' => 'El campo :attribute debe comenzar con: :values.',
'string' => 'El campo :attribute debe ser una cadena de caracteres.',
'timezone' => 'El campo :attribute debe ser una zona horaria válida.',
'unique' => 'El :attribute ya ha sido utilizado.',
'uploaded' => 'La subida del :attribute ha fallado.',
'uppercase' => 'El campo :attribute debe estar en mayúsculas.',
'url' => 'El campo :attribute debe ser una URL válida.',
'ulid' => 'El campo :attribute debe ser un ULID válido.',
'uuid' => 'El campo :attribute debe ser un UUID válido.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Messages de validation
|--------------------------------------------------------------------------
*/
'accepted' => 'Le champ :attribute doit être accepté.',
'accepted_if' => 'Le champ :attribute doit être accepté lorsque :other est :value.',
'active_url' => 'Le champ :attribute doit être une URL valide.',
'after' => 'Le champ :attribute doit être une date postérieure à :date.',
'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale à :date.',
'alpha' => 'Le champ :attribute ne doit contenir que des lettres.',
'alpha_dash' => 'Le champ :attribute ne doit contenir que des lettres, des chiffres, des tirets et des underscores.',
'alpha_num' => 'Le champ :attribute ne doit contenir que des lettres et des chiffres.',
'any_of' => 'Le champ :attribute est invalide.',
'array' => 'Le champ :attribute doit être un tableau.',
'ascii' => 'Le champ :attribute ne doit contenir que des caractères alphanumériques et des symboles sur un octet.',
'before' => 'Le champ :attribute doit être une date antérieure à :date.',
'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale à :date.',
'between' => [
'array' => 'Le champ :attribute doit contenir entre :min et :max éléments.',
'file' => 'Le champ :attribute doit être entre :min et :max kilo-octets.',
'numeric' => 'Le champ :attribute doit être entre :min et :max.',
'string' => 'Le champ :attribute doit contenir entre :min et :max caractères.',
],
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
'can' => 'Le champ :attribute contient une valeur non autorisée.',
'confirmed' => 'La confirmation du champ :attribute ne correspond pas.',
'contains' => 'Le champ :attribute ne contient pas une valeur requise.',
'current_password' => 'Le mot de passe est incorrect.',
'date' => 'Le champ :attribute doit être une date valide.',
'date_equals' => 'Le champ :attribute doit être une date égale à :date.',
'date_format' => 'Le champ :attribute doit correspondre au format :format.',
'decimal' => 'Le champ :attribute doit avoir :decimal décimales.',
'declined' => 'Le champ :attribute doit être refusé.',
'declined_if' => 'Le champ :attribute doit être refusé lorsque :other est :value.',
'different' => 'Les champs :attribute et :other doivent être différents.',
'digits' => 'Le champ :attribute doit contenir :digits chiffres.',
'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.',
'dimensions' => 'Le champ :attribute a des dimensions d\'image invalides.',
'distinct' => 'Le champ :attribute a une valeur en double.',
'doesnt_contain' => 'Le champ :attribute ne doit pas contenir :values.',
'doesnt_end_with' => 'Le champ :attribute ne doit pas se terminer par :values.',
'doesnt_start_with' => 'Le champ :attribute ne doit pas commencer par :values.',
'email' => 'Le champ :attribute doit être une adresse e-mail valide.',
'encoding' => 'Le champ :attribute doit être encodé en :encoding.',
'ends_with' => 'Le champ :attribute doit se terminer par :values.',
'enum' => 'Le :attribute sélectionné est invalide.',
'exists' => 'Le :attribute sélectionné est invalide.',
'extensions' => 'Le champ :attribute doit avoir l\'une des extensions suivantes : :values.',
'file' => 'Le champ :attribute doit être un fichier.',
'filled' => 'Le champ :attribute doit avoir une valeur.',
'gt' => [
'array' => 'Le champ :attribute doit contenir plus de :value éléments.',
'file' => 'Le champ :attribute doit être supérieur à :value kilo-octets.',
'numeric' => 'Le champ :attribute doit être supérieur à :value.',
'string' => 'Le champ :attribute doit contenir plus de :value caractères.',
],
'gte' => [
'array' => 'Le champ :attribute doit contenir :value éléments ou plus.',
'file' => 'Le champ :attribute doit être supérieur ou égal à :value kilo-octets.',
'numeric' => 'Le champ :attribute doit être supérieur ou égal à :value.',
'string' => 'Le champ :attribute doit contenir :value caractères ou plus.',
],
'hex_color' => 'Le champ :attribute doit être une couleur hexadécimale valide.',
'image' => 'Le champ :attribute doit être une image.',
'in' => 'Le :attribute sélectionné est invalide.',
'in_array' => 'Le champ :attribute doit exister dans :other.',
'in_array_keys' => 'Le champ :attribute doit contenir au moins une des clés suivantes : :values.',
'integer' => 'Le champ :attribute doit être un entier.',
'ip' => 'Le champ :attribute doit être une adresse IP valide.',
'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.',
'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.',
'json' => 'Le champ :attribute doit être une chaîne JSON valide.',
'list' => 'Le champ :attribute doit être une liste.',
'lowercase' => 'Le champ :attribute doit être en minuscules.',
'lt' => [
'array' => 'Le champ :attribute doit contenir moins de :value éléments.',
'file' => 'Le champ :attribute doit être inférieur à :value kilo-octets.',
'numeric' => 'Le champ :attribute doit être inférieur à :value.',
'string' => 'Le champ :attribute doit contenir moins de :value caractères.',
],
'lte' => [
'array' => 'Le champ :attribute ne doit pas contenir plus de :value éléments.',
'file' => 'Le champ :attribute doit être inférieur ou égal à :value kilo-octets.',
'numeric' => 'Le champ :attribute doit être inférieur ou égal à :value.',
'string' => 'Le champ :attribute doit contenir :value caractères ou moins.',
],
'mac_address' => 'Le champ :attribute doit être une adresse MAC valide.',
'max' => [
'array' => 'Le champ :attribute ne doit pas contenir plus de :max éléments.',
'file' => 'Le champ :attribute ne doit pas dépasser :max kilo-octets.',
'numeric' => 'Le champ :attribute ne doit pas être supérieur à :max.',
'string' => 'Le champ :attribute ne doit pas contenir plus de :max caractères.',
],
'max_digits' => 'Le champ :attribute ne doit pas contenir plus de :max chiffres.',
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
'min' => [
'array' => 'Le champ :attribute doit contenir au moins :min éléments.',
'file' => 'Le champ :attribute doit faire au moins :min kilo-octets.',
'numeric' => 'Le champ :attribute doit être au moins :min.',
'string' => 'Le champ :attribute doit contenir au moins :min caractères.',
],
'min_digits' => 'Le champ :attribute doit contenir au moins :min chiffres.',
'missing' => 'Le champ :attribute doit être absent.',
'missing_if' => 'Le champ :attribute doit être absent lorsque :other est :value.',
'missing_unless' => 'Le champ :attribute doit être absent sauf si :other est :value.',
'missing_with' => 'Le champ :attribute doit être absent lorsque :values est présent.',
'missing_with_all' => 'Le champ :attribute doit être absent lorsque :values sont présents.',
'multiple_of' => 'Le champ :attribute doit être un multiple de :value.',
'not_in' => 'Le :attribute sélectionné est invalide.',
'not_regex' => 'Le format du champ :attribute est invalide.',
'numeric' => 'Le champ :attribute doit être un nombre.',
'password' => [
'letters' => 'Le champ :attribute doit contenir au moins une lettre.',
'mixed' => 'Le champ :attribute doit contenir au moins une majuscule et une minuscule.',
'numbers' => 'Le champ :attribute doit contenir au moins un chiffre.',
'symbols' => 'Le champ :attribute doit contenir au moins un symbole.',
'uncompromised' => 'Le :attribute donné est apparu dans une fuite de données. Veuillez choisir un autre :attribute.',
],
'present' => 'Le champ :attribute doit être présent.',
'present_if' => 'Le champ :attribute doit être présent lorsque :other est :value.',
'present_unless' => 'Le champ :attribute doit être présent sauf si :other est :value.',
'present_with' => 'Le champ :attribute doit être présent lorsque :values est présent.',
'present_with_all' => 'Le champ :attribute doit être présent lorsque :values sont présents.',
'prohibited' => 'Le champ :attribute est interdit.',
'prohibited_if' => 'Le champ :attribute est interdit lorsque :other est :value.',
'prohibited_if_accepted' => 'Le champ :attribute est interdit lorsque :other est accepté.',
'prohibited_if_declined' => 'Le champ :attribute est interdit lorsque :other est refusé.',
'prohibited_unless' => 'Le champ :attribute est interdit sauf si :other est dans :values.',
'prohibits' => 'Le champ :attribute interdit la présence de :other.',
'regex' => 'Le format du champ :attribute est invalide.',
'required' => 'Le champ :attribute est obligatoire.',
'required_array_keys' => 'Le champ :attribute doit contenir les entrées suivantes : :values.',
'required_if' => 'Le champ :attribute est obligatoire lorsque :other est :value.',
'required_if_accepted' => 'Le champ :attribute est obligatoire lorsque :other est accepté.',
'required_if_declined' => 'Le champ :attribute est obligatoire lorsque :other est refusé.',
'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est dans :values.',
'required_with' => 'Le champ :attribute est obligatoire lorsque :values est présent.',
'required_with_all' => 'Le champ :attribute est obligatoire lorsque :values sont présents.',
'required_without' => 'Le champ :attribute est obligatoire lorsque :values n\'est pas présent.',
'required_without_all' => 'Le champ :attribute est obligatoire lorsqu\'aucun de :values n\'est présent.',
'same' => 'Les champs :attribute et :other doivent correspondre.',
'size' => [
'array' => 'Le champ :attribute doit contenir :size éléments.',
'file' => 'Le champ :attribute doit faire :size kilo-octets.',
'numeric' => 'Le champ :attribute doit être :size.',
'string' => 'Le champ :attribute doit contenir :size caractères.',
],
'starts_with' => 'Le champ :attribute doit commencer par :values.',
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
'unique' => 'Le :attribute a déjà été utilisé.',
'uploaded' => 'Le téléchargement du :attribute a échoué.',
'uppercase' => 'Le champ :attribute doit être en majuscules.',
'url' => 'Le champ :attribute doit être une URL valide.',
'ulid' => 'Le champ :attribute doit être un ULID valide.',
'uuid' => 'Le champ :attribute doit être un UUID valide.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pesan validasi
|--------------------------------------------------------------------------
*/
'accepted' => 'Bidang :attribute harus diterima.',
'accepted_if' => 'Bidang :attribute harus diterima ketika :other adalah :value.',
'active_url' => 'Bidang :attribute harus berupa URL yang valid.',
'after' => 'Bidang :attribute harus berupa tanggal setelah :date.',
'after_or_equal' => 'Bidang :attribute harus berupa tanggal setelah atau sama dengan :date.',
'alpha' => 'Bidang :attribute hanya boleh berisi huruf.',
'alpha_dash' => 'Bidang :attribute hanya boleh berisi huruf, angka, tanda hubung, dan garis bawah.',
'alpha_num' => 'Bidang :attribute hanya boleh berisi huruf dan angka.',
'any_of' => 'Bidang :attribute tidak valid.',
'array' => 'Bidang :attribute harus berupa array.',
'ascii' => 'Bidang :attribute hanya boleh berisi karakter alfanumerik dan simbol satu byte.',
'before' => 'Bidang :attribute harus berupa tanggal sebelum :date.',
'before_or_equal' => 'Bidang :attribute harus berupa tanggal sebelum atau sama dengan :date.',
'between' => [
'array' => 'Bidang :attribute harus memiliki antara :min dan :max item.',
'file' => 'Bidang :attribute harus antara :min dan :max kilobyte.',
'numeric' => 'Bidang :attribute harus antara :min dan :max.',
'string' => 'Bidang :attribute harus antara :min dan :max karakter.',
],
'boolean' => 'Bidang :attribute harus benar atau salah.',
'can' => 'Bidang :attribute berisi nilai yang tidak diizinkan.',
'confirmed' => 'Konfirmasi bidang :attribute tidak cocok.',
'contains' => 'Bidang :attribute tidak berisi nilai yang wajib.',
'current_password' => 'Kata sandi salah.',
'date' => 'Bidang :attribute harus berupa tanggal yang valid.',
'date_equals' => 'Bidang :attribute harus berupa tanggal yang sama dengan :date.',
'date_format' => 'Bidang :attribute harus sesuai format :format.',
'decimal' => 'Bidang :attribute harus memiliki :decimal tempat desimal.',
'declined' => 'Bidang :attribute harus ditolak.',
'declined_if' => 'Bidang :attribute harus ditolak ketika :other adalah :value.',
'different' => 'Bidang :attribute dan :other harus berbeda.',
'digits' => 'Bidang :attribute harus :digits digit.',
'digits_between' => 'Bidang :attribute harus antara :min dan :max digit.',
'dimensions' => 'Bidang :attribute memiliki dimensi gambar yang tidak valid.',
'distinct' => 'Bidang :attribute memiliki nilai duplikat.',
'doesnt_contain' => 'Bidang :attribute tidak boleh berisi: :values.',
'doesnt_end_with' => 'Bidang :attribute tidak boleh diakhiri dengan: :values.',
'doesnt_start_with' => 'Bidang :attribute tidak boleh diawali dengan: :values.',
'email' => 'Bidang :attribute harus berupa alamat email yang valid.',
'encoding' => 'Bidang :attribute harus dikodekan dalam :encoding.',
'ends_with' => 'Bidang :attribute harus diakhiri dengan: :values.',
'enum' => ':attribute yang dipilih tidak valid.',
'exists' => ':attribute yang dipilih tidak valid.',
'extensions' => 'Bidang :attribute harus memiliki salah satu ekstensi berikut: :values.',
'file' => 'Bidang :attribute harus berupa file.',
'filled' => 'Bidang :attribute harus memiliki nilai.',
'gt' => [
'array' => 'Bidang :attribute harus memiliki lebih dari :value item.',
'file' => 'Bidang :attribute harus lebih besar dari :value kilobyte.',
'numeric' => 'Bidang :attribute harus lebih besar dari :value.',
'string' => 'Bidang :attribute harus lebih dari :value karakter.',
],
'gte' => [
'array' => 'Bidang :attribute harus memiliki :value item atau lebih.',
'file' => 'Bidang :attribute harus lebih besar atau sama dengan :value kilobyte.',
'numeric' => 'Bidang :attribute harus lebih besar atau sama dengan :value.',
'string' => 'Bidang :attribute harus :value karakter atau lebih.',
],
'hex_color' => 'Bidang :attribute harus berupa warna heksadesimal yang valid.',
'image' => 'Bidang :attribute harus berupa gambar.',
'in' => ':attribute yang dipilih tidak valid.',
'in_array' => 'Bidang :attribute harus ada di :other.',
'in_array_keys' => 'Bidang :attribute harus berisi setidaknya salah satu kunci berikut: :values.',
'integer' => 'Bidang :attribute harus berupa bilangan bulat.',
'ip' => 'Bidang :attribute harus berupa alamat IP yang valid.',
'ipv4' => 'Bidang :attribute harus berupa alamat IPv4 yang valid.',
'ipv6' => 'Bidang :attribute harus berupa alamat IPv6 yang valid.',
'json' => 'Bidang :attribute harus berupa string JSON yang valid.',
'list' => 'Bidang :attribute harus berupa daftar.',
'lowercase' => 'Bidang :attribute harus huruf kecil.',
'lt' => [
'array' => 'Bidang :attribute harus memiliki kurang dari :value item.',
'file' => 'Bidang :attribute harus kurang dari :value kilobyte.',
'numeric' => 'Bidang :attribute harus kurang dari :value.',
'string' => 'Bidang :attribute harus kurang dari :value karakter.',
],
'lte' => [
'array' => 'Bidang :attribute tidak boleh memiliki lebih dari :value item.',
'file' => 'Bidang :attribute harus kurang dari atau sama dengan :value kilobyte.',
'numeric' => 'Bidang :attribute harus kurang dari atau sama dengan :value.',
'string' => 'Bidang :attribute harus :value karakter atau kurang.',
],
'mac_address' => 'Bidang :attribute harus berupa alamat MAC yang valid.',
'max' => [
'array' => 'Bidang :attribute tidak boleh memiliki lebih dari :max item.',
'file' => 'Bidang :attribute tidak boleh lebih besar dari :max kilobyte.',
'numeric' => 'Bidang :attribute tidak boleh lebih besar dari :max.',
'string' => 'Bidang :attribute tidak boleh lebih dari :max karakter.',
],
'max_digits' => 'Bidang :attribute tidak boleh memiliki lebih dari :max digit.',
'mimes' => 'Bidang :attribute harus berupa file bertipe: :values.',
'mimetypes' => 'Bidang :attribute harus berupa file bertipe: :values.',
'min' => [
'array' => 'Bidang :attribute harus memiliki setidaknya :min item.',
'file' => 'Bidang :attribute harus setidaknya :min kilobyte.',
'numeric' => 'Bidang :attribute harus setidaknya :min.',
'string' => 'Bidang :attribute harus setidaknya :min karakter.',
],
'min_digits' => 'Bidang :attribute harus memiliki setidaknya :min digit.',
'missing' => 'Bidang :attribute harus tidak ada.',
'missing_if' => 'Bidang :attribute harus tidak ada ketika :other adalah :value.',
'missing_unless' => 'Bidang :attribute harus tidak ada kecuali :other adalah :value.',
'missing_with' => 'Bidang :attribute harus tidak ada ketika :values ada.',
'missing_with_all' => 'Bidang :attribute harus tidak ada ketika :values ada.',
'multiple_of' => 'Bidang :attribute harus kelipatan dari :value.',
'not_in' => ':attribute yang dipilih tidak valid.',
'not_regex' => 'Format bidang :attribute tidak valid.',
'numeric' => 'Bidang :attribute harus berupa angka.',
'password' => [
'letters' => 'Bidang :attribute harus berisi setidaknya satu huruf.',
'mixed' => 'Bidang :attribute harus berisi setidaknya satu huruf besar dan satu huruf kecil.',
'numbers' => 'Bidang :attribute harus berisi setidaknya satu angka.',
'symbols' => 'Bidang :attribute harus berisi setidaknya satu simbol.',
'uncompromised' => ':attribute yang diberikan telah muncul dalam kebocoran data. Silakan pilih :attribute yang berbeda.',
],
'present' => 'Bidang :attribute harus ada.',
'present_if' => 'Bidang :attribute harus ada ketika :other adalah :value.',
'present_unless' => 'Bidang :attribute harus ada kecuali :other adalah :value.',
'present_with' => 'Bidang :attribute harus ada ketika :values ada.',
'present_with_all' => 'Bidang :attribute harus ada ketika :values ada.',
'prohibited' => 'Bidang :attribute dilarang.',
'prohibited_if' => 'Bidang :attribute dilarang ketika :other adalah :value.',
'prohibited_if_accepted' => 'Bidang :attribute dilarang ketika :other diterima.',
'prohibited_if_declined' => 'Bidang :attribute dilarang ketika :other ditolak.',
'prohibited_unless' => 'Bidang :attribute dilarang kecuali :other ada di :values.',
'prohibits' => 'Bidang :attribute melarang :other untuk ada.',
'regex' => 'Format bidang :attribute tidak valid.',
'required' => 'Bidang :attribute wajib diisi.',
'required_array_keys' => 'Bidang :attribute harus berisi entri untuk: :values.',
'required_if' => 'Bidang :attribute wajib diisi ketika :other adalah :value.',
'required_if_accepted' => 'Bidang :attribute wajib diisi ketika :other diterima.',
'required_if_declined' => 'Bidang :attribute wajib diisi ketika :other ditolak.',
'required_unless' => 'Bidang :attribute wajib diisi kecuali :other ada di :values.',
'required_with' => 'Bidang :attribute wajib diisi ketika :values ada.',
'required_with_all' => 'Bidang :attribute wajib diisi ketika :values ada.',
'required_without' => 'Bidang :attribute wajib diisi ketika :values tidak ada.',
'required_without_all' => 'Bidang :attribute wajib diisi ketika tidak ada :values.',
'same' => 'Bidang :attribute dan :other harus cocok.',
'size' => [
'array' => 'Bidang :attribute harus berisi :size item.',
'file' => 'Bidang :attribute harus :size kilobyte.',
'numeric' => 'Bidang :attribute harus :size.',
'string' => 'Bidang :attribute harus :size karakter.',
],
'starts_with' => 'Bidang :attribute harus diawali dengan: :values.',
'string' => 'Bidang :attribute harus berupa string.',
'timezone' => 'Bidang :attribute harus berupa zona waktu yang valid.',
'unique' => ':attribute sudah digunakan.',
'uploaded' => 'Unggah :attribute gagal.',
'uppercase' => 'Bidang :attribute harus huruf besar.',
'url' => 'Bidang :attribute harus berupa URL yang valid.',
'ulid' => 'Bidang :attribute harus berupa ULID yang valid.',
'uuid' => 'Bidang :attribute harus berupa UUID yang valid.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| バリデーション文言
|--------------------------------------------------------------------------
*/
'accepted' => ':attribute を受け入れる必要があります。',
'accepted_if' => ':other が :value の場合、:attribute を受け入れる必要があります。',
'active_url' => ':attribute は有効なURLである必要があります。',
'after' => ':attribute は :date より後の日付である必要があります。',
'after_or_equal' => ':attribute は :date 以降の日付である必要があります。',
'alpha' => ':attribute は英字のみを含む必要があります。',
'alpha_dash' => ':attribute は英字、数字、ハイフン、アンダースコアのみを含む必要があります。',
'alpha_num' => ':attribute は英字と数字のみを含む必要があります。',
'any_of' => ':attribute は無効です。',
'array' => ':attribute は配列である必要があります。',
'ascii' => ':attribute はASCII文字のみを含む必要があります。',
'before' => ':attribute は :date より前の日付である必要があります。',
'before_or_equal' => ':attribute は :date 以前の日付である必要があります。',
'between' => [
'array' => ':attribute は :min から :max 個である必要があります。',
'file' => ':attribute は :min から :max KB である必要があります。',
'numeric' => ':attribute は :min から :max の間である必要があります。',
'string' => ':attribute は :min から :max 文字である必要があります。',
],
'boolean' => ':attribute は真偽値である必要があります。',
'can' => ':attribute に許可されていない値が含まれています。',
'confirmed' => ':attribute の確認が一致しません。',
'contains' => ':attribute に必須の値が含まれていません。',
'current_password' => 'パスワードが正しくありません。',
'date' => ':attribute は有効な日付である必要があります。',
'date_equals' => ':attribute は :date と等しい日付である必要があります。',
'date_format' => ':attribute は :format 形式である必要があります。',
'decimal' => ':attribute は :decimal 桁の小数である必要があります。',
'declined' => ':attribute は拒否する必要があります。',
'declined_if' => ':other が :value の場合、:attribute は拒否する必要があります。',
'different' => ':attribute と :other は異なる必要があります。',
'digits' => ':attribute は :digits 桁の数字である必要があります。',
'digits_between' => ':attribute は :min から :max 桁の数字である必要があります。',
'dimensions' => ':attribute の画像サイズが無効です。',
'distinct' => ':attribute に重複した値があります。',
'doesnt_contain' => ':attribute に以下を含めることはできません::values。',
'doesnt_end_with' => ':attribute は以下で終わってはいけません::values。',
'doesnt_start_with' => ':attribute は以下で始まってはいけません::values。',
'email' => ':attribute は有効なメールアドレスである必要があります。',
'encoding' => ':attribute は :encoding でエンコードされている必要があります。',
'ends_with' => ':attribute は以下で終わる必要があります::values。',
'enum' => '選択した :attribute は無効です。',
'exists' => '選択した :attribute は無効です。',
'extensions' => ':attribute は以下の拡張子のいずれかである必要があります::values。',
'file' => ':attribute はファイルである必要があります。',
'filled' => ':attribute には値が必要です。',
'gt' => [
'array' => ':attribute は :value 個より多い必要があります。',
'file' => ':attribute は :value KB より大きい必要があります。',
'numeric' => ':attribute は :value より大きい必要があります。',
'string' => ':attribute は :value 文字より多い必要があります。',
],
'gte' => [
'array' => ':attribute は :value 個以上である必要があります。',
'file' => ':attribute は :value KB 以上である必要があります。',
'numeric' => ':attribute は :value 以上である必要があります。',
'string' => ':attribute は :value 文字以上である必要があります。',
],
'hex_color' => ':attribute は有効な16進数カラーである必要があります。',
'image' => ':attribute は画像である必要があります。',
'in' => '選択した :attribute は無効です。',
'in_array' => ':attribute は :other に存在する必要があります。',
'in_array_keys' => ':attribute は以下のキーのいずれかを含む必要があります::values。',
'integer' => ':attribute は整数である必要があります。',
'ip' => ':attribute は有効なIPアドレスである必要があります。',
'ipv4' => ':attribute は有効なIPv4アドレスである必要があります。',
'ipv6' => ':attribute は有効なIPv6アドレスである必要があります。',
'json' => ':attribute は有効なJSON文字列である必要があります。',
'list' => ':attribute はリストである必要があります。',
'lowercase' => ':attribute は小文字である必要があります。',
'lt' => [
'array' => ':attribute は :value 個未満である必要があります。',
'file' => ':attribute は :value KB 未満である必要があります。',
'numeric' => ':attribute は :value 未満である必要があります。',
'string' => ':attribute は :value 文字未満である必要があります。',
],
'lte' => [
'array' => ':attribute は :value 個以下である必要があります。',
'file' => ':attribute は :value KB 以下である必要があります。',
'numeric' => ':attribute は :value 以下である必要があります。',
'string' => ':attribute は :value 文字以下である必要があります。',
],
'mac_address' => ':attribute は有効なMACアドレスである必要があります。',
'max' => [
'array' => ':attribute は :max 個以下である必要があります。',
'file' => ':attribute は :max KB 以下である必要があります。',
'numeric' => ':attribute は :max 以下である必要があります。',
'string' => ':attribute は :max 文字以下である必要があります。',
],
'max_digits' => ':attribute は :max 桁以下である必要があります。',
'mimes' => ':attribute は :values タイプのファイルである必要があります。',
'mimetypes' => ':attribute は :values タイプのファイルである必要があります。',
'min' => [
'array' => ':attribute は少なくとも :min 個である必要があります。',
'file' => ':attribute は少なくとも :min KB である必要があります。',
'numeric' => ':attribute は少なくとも :min である必要があります。',
'string' => ':attribute は少なくとも :min 文字である必要があります。',
],
'min_digits' => ':attribute は少なくとも :min 桁である必要があります。',
'missing' => ':attribute は存在してはいけません。',
'missing_if' => ':other が :value の場合、:attribute は存在してはいけません。',
'missing_unless' => ':other が :value でない限り、:attribute は存在してはいけません。',
'missing_with' => ':values が存在する場合、:attribute は存在してはいけません。',
'missing_with_all' => ':values がすべて存在する場合、:attribute は存在してはいけません。',
'multiple_of' => ':attribute は :value の倍数である必要があります。',
'not_in' => '選択した :attribute は無効です。',
'not_regex' => ':attribute の形式が無効です。',
'numeric' => ':attribute は数値である必要があります。',
'password' => [
'letters' => ':attribute は少なくとも1文字の英字を含む必要があります。',
'mixed' => ':attribute は少なくとも1つの大文字と1つの小文字を含む必要があります。',
'numbers' => ':attribute は少なくとも1つの数字を含む必要があります。',
'symbols' => ':attribute は少なくとも1つの記号を含む必要があります。',
'uncompromised' => ':attribute はデータ漏洩で発見されました。別の :attribute を選択してください。',
],
'present' => ':attribute は存在する必要があります。',
'present_if' => ':other が :value の場合、:attribute は存在する必要があります。',
'present_unless' => ':other が :value でない限り、:attribute は存在する必要があります。',
'present_with' => ':values が存在する場合、:attribute は存在する必要があります。',
'present_with_all' => ':values がすべて存在する場合、:attribute は存在する必要があります。',
'prohibited' => ':attribute は禁止されています。',
'prohibited_if' => ':other が :value の場合、:attribute は禁止されています。',
'prohibited_if_accepted' => ':other が受け入れられた場合、:attribute は禁止されています。',
'prohibited_if_declined' => ':other が拒否された場合、:attribute は禁止されています。',
'prohibited_unless' => ':other が :values に含まれていない限り、:attribute は禁止されています。',
'prohibits' => ':attribute が存在する場合、:other は禁止されています。',
'regex' => ':attribute の形式が無効です。',
'required' => ':attribute は必須です。',
'required_array_keys' => ':attribute は以下のキーを含む必要があります::values。',
'required_if' => ':other が :value の場合、:attribute は必須です。',
'required_if_accepted' => ':other が受け入れられた場合、:attribute は必須です。',
'required_if_declined' => ':other が拒否された場合、:attribute は必須です。',
'required_unless' => ':other が :values に含まれていない限り、:attribute は必須です。',
'required_with' => ':values が存在する場合、:attribute は必須です。',
'required_with_all' => ':values がすべて存在する場合、:attribute は必須です。',
'required_without' => ':values が存在しない場合、:attribute は必須です。',
'required_without_all' => ':values がすべて存在しない場合、:attribute は必須です。',
'same' => ':attribute と :other は一致する必要があります。',
'size' => [
'array' => ':attribute は :size 個である必要があります。',
'file' => ':attribute は :size KB である必要があります。',
'numeric' => ':attribute は :size である必要があります。',
'string' => ':attribute は :size 文字である必要があります。',
],
'starts_with' => ':attribute は以下で始まる必要があります::values。',
'string' => ':attribute は文字列である必要があります。',
'timezone' => ':attribute は有効なタイムゾーンである必要があります。',
'unique' => ':attribute は既に使用されています。',
'uploaded' => ':attribute のアップロードに失敗しました。',
'uppercase' => ':attribute は大文字である必要があります。',
'url' => ':attribute は有効なURLである必要があります。',
'ulid' => ':attribute は有効なULIDである必要があります。',
'uuid' => ':attribute は有効なUUIDである必要があります。',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| 유효성 검사 메시지
|--------------------------------------------------------------------------
*/
'accepted' => ':attribute을(를) 수락해야 합니다.',
'accepted_if' => ':other이(가) :value일 때 :attribute을(를) 수락해야 합니다.',
'active_url' => ':attribute은(는) 유효한 URL이어야 합니다.',
'after' => ':attribute은(는) :date 이후 날짜여야 합니다.',
'after_or_equal' => ':attribute은(는) :date 이후이거나 같은 날짜여야 합니다.',
'alpha' => ':attribute은(는) 문자만 포함해야 합니다.',
'alpha_dash' => ':attribute은(는) 문자, 숫자, 대시, 밑줄만 포함해야 합니다.',
'alpha_num' => ':attribute은(는) 문자와 숫자만 포함해야 합니다.',
'any_of' => ':attribute이(가) 유효하지 않습니다.',
'array' => ':attribute은(는) 배열이어야 합니다.',
'ascii' => ':attribute은(는) ASCII 문자만 포함해야 합니다.',
'before' => ':attribute은(는) :date 이전 날짜여야 합니다.',
'before_or_equal' => ':attribute은(는) :date 이전이거나 같은 날짜여야 합니다.',
'between' => [
'array' => ':attribute은(는) :min개에서 :max개 사이여야 합니다.',
'file' => ':attribute은(는) :min KB에서 :max KB 사이여야 합니다.',
'numeric' => ':attribute은(는) :min에서 :max 사이여야 합니다.',
'string' => ':attribute은(는) :min자에서 :max자 사이여야 합니다.',
],
'boolean' => ':attribute은(는) 참 또는 거짓이어야 합니다.',
'can' => ':attribute에 허용되지 않은 값이 포함되어 있습니다.',
'confirmed' => ':attribute 확인이 일치하지 않습니다.',
'contains' => ':attribute에 필수 값이 없습니다.',
'current_password' => '비밀번호가 올바르지 않습니다.',
'date' => ':attribute은(는) 유효한 날짜여야 합니다.',
'date_equals' => ':attribute은(는) :date와 같은 날짜여야 합니다.',
'date_format' => ':attribute은(는) :format 형식이어야 합니다.',
'decimal' => ':attribute은(는) :decimal자리 소수여야 합니다.',
'declined' => ':attribute을(를) 거부해야 합니다.',
'declined_if' => ':other이(가) :value일 때 :attribute을(를) 거부해야 합니다.',
'different' => ':attribute과(와) :other은(는) 달라야 합니다.',
'digits' => ':attribute은(는) :digits자리 숫자여야 합니다.',
'digits_between' => ':attribute은(는) :min자리에서 :max자리 숫자여야 합니다.',
'dimensions' => ':attribute 이미지 크기가 유효하지 않습니다.',
'distinct' => ':attribute에 중복된 값이 있습니다.',
'doesnt_contain' => ':attribute에 다음을 포함할 수 없습니다: :values.',
'doesnt_end_with' => ':attribute은(는) 다음으로 끝나면 안 됩니다: :values.',
'doesnt_start_with' => ':attribute은(는) 다음으로 시작하면 안 됩니다: :values.',
'email' => ':attribute은(는) 유효한 이메일 주소여야 합니다.',
'encoding' => ':attribute은(는) :encoding으로 인코딩되어야 합니다.',
'ends_with' => ':attribute은(는) 다음 중 하나로 끝나야 합니다: :values.',
'enum' => '선택한 :attribute이(가) 유효하지 않습니다.',
'exists' => '선택한 :attribute이(가) 유효하지 않습니다.',
'extensions' => ':attribute은(는) 다음 확장자 중 하나여야 합니다: :values.',
'file' => ':attribute은(는) 파일이어야 합니다.',
'filled' => ':attribute에는 값이 있어야 합니다.',
'gt' => [
'array' => ':attribute은(는) :value개보다 많아야 합니다.',
'file' => ':attribute은(는) :value KB보다 커야 합니다.',
'numeric' => ':attribute은(는) :value보다 커야 합니다.',
'string' => ':attribute은(는) :value자보다 많아야 합니다.',
],
'gte' => [
'array' => ':attribute은(는) :value개 이상이어야 합니다.',
'file' => ':attribute은(는) :value KB 이상이어야 합니다.',
'numeric' => ':attribute은(는) :value 이상이어야 합니다.',
'string' => ':attribute은(는) :value자 이상이어야 합니다.',
],
'hex_color' => ':attribute은(는) 유효한 16진수 색상이어야 합니다.',
'image' => ':attribute은(는) 이미지여야 합니다.',
'in' => '선택한 :attribute이(가) 유효하지 않습니다.',
'in_array' => ':attribute은(는) :other에 존재해야 합니다.',
'in_array_keys' => ':attribute은(는) 다음 키 중 하나를 포함해야 합니다: :values.',
'integer' => ':attribute은(는) 정수여야 합니다.',
'ip' => ':attribute은(는) 유효한 IP 주소여야 합니다.',
'ipv4' => ':attribute은(는) 유효한 IPv4 주소여야 합니다.',
'ipv6' => ':attribute은(는) 유효한 IPv6 주소여야 합니다.',
'json' => ':attribute은(는) 유효한 JSON 문자열이어야 합니다.',
'list' => ':attribute은(는) 목록이어야 합니다.',
'lowercase' => ':attribute은(는) 소문자여야 합니다.',
'lt' => [
'array' => ':attribute은(는) :value개 미만이어야 합니다.',
'file' => ':attribute은(는) :value KB 미만이어야 합니다.',
'numeric' => ':attribute은(는) :value 미만이어야 합니다.',
'string' => ':attribute은(는) :value자 미만이어야 합니다.',
],
'lte' => [
'array' => ':attribute은(는) :value개 이하여야 합니다.',
'file' => ':attribute은(는) :value KB 이하여야 합니다.',
'numeric' => ':attribute은(는) :value 이하여야 합니다.',
'string' => ':attribute은(는) :value자 이하여야 합니다.',
],
'mac_address' => ':attribute은(는) 유효한 MAC 주소여야 합니다.',
'max' => [
'array' => ':attribute은(는) :max개 이하여야 합니다.',
'file' => ':attribute은(는) :max KB 이하여야 합니다.',
'numeric' => ':attribute은(는) :max 이하여야 합니다.',
'string' => ':attribute은(는) :max자 이하여야 합니다.',
],
'max_digits' => ':attribute은(는) :max자리 이하여야 합니다.',
'mimes' => ':attribute은(는) :values 유형의 파일이어야 합니다.',
'mimetypes' => ':attribute은(는) :values 유형의 파일이어야 합니다.',
'min' => [
'array' => ':attribute은(는) 최소 :min개여야 합니다.',
'file' => ':attribute은(는) 최소 :min KB여야 합니다.',
'numeric' => ':attribute은(는) 최소 :min이어야 합니다.',
'string' => ':attribute은(는) 최소 :min자여야 합니다.',
],
'min_digits' => ':attribute은(는) 최소 :min자리여야 합니다.',
'missing' => ':attribute은(는) 없어야 합니다.',
'missing_if' => ':other이(가) :value일 때 :attribute은(는) 없어야 합니다.',
'missing_unless' => ':other이(가) :value가 아니면 :attribute은(는) 없어야 합니다.',
'missing_with' => ':values이(가) 있으면 :attribute은(는) 없어야 합니다.',
'missing_with_all' => ':values이(가) 모두 있으면 :attribute은(는) 없어야 합니다.',
'multiple_of' => ':attribute은(는) :value의 배수여야 합니다.',
'not_in' => '선택한 :attribute이(가) 유효하지 않습니다.',
'not_regex' => ':attribute 형식이 유효하지 않습니다.',
'numeric' => ':attribute은(는) 숫자여야 합니다.',
'password' => [
'letters' => ':attribute은(는) 최소 한 개의 문자를 포함해야 합니다.',
'mixed' => ':attribute은(는) 최소 한 개의 대문자와 한 개의 소문자를 포함해야 합니다.',
'numbers' => ':attribute은(는) 최소 한 개의 숫자를 포함해야 합니다.',
'symbols' => ':attribute은(는) 최소 한 개의 기호를 포함해야 합니다.',
'uncompromised' => ':attribute이(가) 데이터 유출에서 발견되었습니다. 다른 :attribute을(를) 선택하세요.',
],
'present' => ':attribute이(가) 있어야 합니다.',
'present_if' => ':other이(가) :value일 때 :attribute이(가) 있어야 합니다.',
'present_unless' => ':other이(가) :value가 아니면 :attribute이(가) 있어야 합니다.',
'present_with' => ':values이(가) 있으면 :attribute이(가) 있어야 합니다.',
'present_with_all' => ':values이(가) 모두 있으면 :attribute이(가) 있어야 합니다.',
'prohibited' => ':attribute은(는) 금지되어 있습니다.',
'prohibited_if' => ':other이(가) :value일 때 :attribute은(는) 금지되어 있습니다.',
'prohibited_if_accepted' => ':other이(가) 수락되면 :attribute은(는) 금지되어 있습니다.',
'prohibited_if_declined' => ':other이(가) 거부되면 :attribute은(는) 금지되어 있습니다.',
'prohibited_unless' => ':other이(가) :values에 없으면 :attribute은(는) 금지되어 있습니다.',
'prohibits' => ':attribute이(가) 있으면 :other은(는) 금지되어 있습니다.',
'regex' => ':attribute 형식이 유효하지 않습니다.',
'required' => ':attribute은(는) 필수입니다.',
'required_array_keys' => ':attribute은(는) 다음 키를 포함해야 합니다: :values.',
'required_if' => ':other이(가) :value일 때 :attribute은(는) 필수입니다.',
'required_if_accepted' => ':other이(가) 수락되면 :attribute은(는) 필수입니다.',
'required_if_declined' => ':other이(가) 거부되면 :attribute은(는) 필수입니다.',
'required_unless' => ':other이(가) :values에 없으면 :attribute은(는) 필수입니다.',
'required_with' => ':values이(가) 있으면 :attribute은(는) 필수입니다.',
'required_with_all' => ':values이(가) 모두 있으면 :attribute은(는) 필수입니다.',
'required_without' => ':values이(가) 없으면 :attribute은(는) 필수입니다.',
'required_without_all' => ':values이(가) 모두 없으면 :attribute은(는) 필수입니다.',
'same' => ':attribute과(와) :other은(는) 일치해야 합니다.',
'size' => [
'array' => ':attribute은(는) :size개여야 합니다.',
'file' => ':attribute은(는) :size KB여야 합니다.',
'numeric' => ':attribute은(는) :size여야 합니다.',
'string' => ':attribute은(는) :size자여야 합니다.',
],
'starts_with' => ':attribute은(는) 다음 중 하나로 시작해야 합니다: :values.',
'string' => ':attribute은(는) 문자열이어야 합니다.',
'timezone' => ':attribute은(는) 유효한 시간대여야 합니다.',
'unique' => ':attribute은(는) 이미 사용 중입니다.',
'uploaded' => ':attribute 업로드에 실패했습니다.',
'uppercase' => ':attribute은(는) 대문자여야 합니다.',
'url' => ':attribute은(는) 유효한 URL이어야 합니다.',
'ulid' => ':attribute은(는) 유효한 ULID여야 합니다.',
'uuid' => ':attribute은(는) 유효한 UUID여야 합니다.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Mensagens de validação
|--------------------------------------------------------------------------
*/
'accepted' => 'O campo :attribute deve ser aceito.',
'accepted_if' => 'O campo :attribute deve ser aceito quando :other for :value.',
'active_url' => 'O campo :attribute deve ser uma URL válida.',
'after' => 'O campo :attribute deve ser uma data posterior a :date.',
'after_or_equal' => 'O campo :attribute deve ser uma data posterior ou igual a :date.',
'alpha' => 'O campo :attribute deve conter apenas letras.',
'alpha_dash' => 'O campo :attribute deve conter apenas letras, números, traços e sublinhados.',
'alpha_num' => 'O campo :attribute deve conter apenas letras e números.',
'any_of' => 'O campo :attribute é inválido.',
'array' => 'O campo :attribute deve ser um array.',
'ascii' => 'O campo :attribute deve conter apenas caracteres alfanuméricos e símbolos de um byte.',
'before' => 'O campo :attribute deve ser uma data anterior a :date.',
'before_or_equal' => 'O campo :attribute deve ser uma data anterior ou igual a :date.',
'between' => [
'array' => 'O campo :attribute deve ter entre :min e :max itens.',
'file' => 'O campo :attribute deve ter entre :min e :max kilobytes.',
'numeric' => 'O campo :attribute deve estar entre :min e :max.',
'string' => 'O campo :attribute deve ter entre :min e :max caracteres.',
],
'boolean' => 'O campo :attribute deve ser verdadeiro ou falso.',
'can' => 'O campo :attribute contém um valor não autorizado.',
'confirmed' => 'A confirmação do campo :attribute não corresponde.',
'contains' => 'O campo :attribute não contém um valor obrigatório.',
'current_password' => 'A senha está incorreta.',
'date' => 'O campo :attribute deve ser uma data válida.',
'date_equals' => 'O campo :attribute deve ser uma data igual a :date.',
'date_format' => 'O campo :attribute deve corresponder ao formato :format.',
'decimal' => 'O campo :attribute deve ter :decimal casas decimais.',
'declined' => 'O campo :attribute deve ser recusado.',
'declined_if' => 'O campo :attribute deve ser recusado quando :other for :value.',
'different' => 'Os campos :attribute e :other devem ser diferentes.',
'digits' => 'O campo :attribute deve ter :digits dígitos.',
'digits_between' => 'O campo :attribute deve ter entre :min e :max dígitos.',
'dimensions' => 'O campo :attribute tem dimensões de imagem inválidas.',
'distinct' => 'O campo :attribute tem um valor duplicado.',
'doesnt_contain' => 'O campo :attribute não deve conter: :values.',
'doesnt_end_with' => 'O campo :attribute não deve terminar com: :values.',
'doesnt_start_with' => 'O campo :attribute não deve começar com: :values.',
'email' => 'O campo :attribute deve ser um endereço de e-mail válido.',
'encoding' => 'O campo :attribute deve estar codificado em :encoding.',
'ends_with' => 'O campo :attribute deve terminar com: :values.',
'enum' => 'O :attribute selecionado é inválido.',
'exists' => 'O :attribute selecionado é inválido.',
'extensions' => 'O campo :attribute deve ter uma das seguintes extensões: :values.',
'file' => 'O campo :attribute deve ser um arquivo.',
'filled' => 'O campo :attribute deve ter um valor.',
'gt' => [
'array' => 'O campo :attribute deve ter mais de :value itens.',
'file' => 'O campo :attribute deve ser maior que :value kilobytes.',
'numeric' => 'O campo :attribute deve ser maior que :value.',
'string' => 'O campo :attribute deve ter mais de :value caracteres.',
],
'gte' => [
'array' => 'O campo :attribute deve ter :value itens ou mais.',
'file' => 'O campo :attribute deve ser maior ou igual a :value kilobytes.',
'numeric' => 'O campo :attribute deve ser maior ou igual a :value.',
'string' => 'O campo :attribute deve ter :value caracteres ou mais.',
],
'hex_color' => 'O campo :attribute deve ser uma cor hexadecimal válida.',
'image' => 'O campo :attribute deve ser uma imagem.',
'in' => 'O :attribute selecionado é inválido.',
'in_array' => 'O campo :attribute deve existir em :other.',
'in_array_keys' => 'O campo :attribute deve conter pelo menos uma das seguintes chaves: :values.',
'integer' => 'O campo :attribute deve ser um número inteiro.',
'ip' => 'O campo :attribute deve ser um endereço IP válido.',
'ipv4' => 'O campo :attribute deve ser um endereço IPv4 válido.',
'ipv6' => 'O campo :attribute deve ser um endereço IPv6 válido.',
'json' => 'O campo :attribute deve ser uma string JSON válida.',
'list' => 'O campo :attribute deve ser uma lista.',
'lowercase' => 'O campo :attribute deve estar em minúsculas.',
'lt' => [
'array' => 'O campo :attribute deve ter menos de :value itens.',
'file' => 'O campo :attribute deve ser menor que :value kilobytes.',
'numeric' => 'O campo :attribute deve ser menor que :value.',
'string' => 'O campo :attribute deve ter menos de :value caracteres.',
],
'lte' => [
'array' => 'O campo :attribute não deve ter mais de :value itens.',
'file' => 'O campo :attribute deve ser menor ou igual a :value kilobytes.',
'numeric' => 'O campo :attribute deve ser menor ou igual a :value.',
'string' => 'O campo :attribute deve ter :value caracteres ou menos.',
],
'mac_address' => 'O campo :attribute deve ser um endereço MAC válido.',
'max' => [
'array' => 'O campo :attribute não deve ter mais de :max itens.',
'file' => 'O campo :attribute não deve ser maior que :max kilobytes.',
'numeric' => 'O campo :attribute não deve ser maior que :max.',
'string' => 'O campo :attribute não deve ter mais de :max caracteres.',
],
'max_digits' => 'O campo :attribute não deve ter mais de :max dígitos.',
'mimes' => 'O campo :attribute deve ser um arquivo do tipo: :values.',
'mimetypes' => 'O campo :attribute deve ser um arquivo do tipo: :values.',
'min' => [
'array' => 'O campo :attribute deve ter pelo menos :min itens.',
'file' => 'O campo :attribute deve ter pelo menos :min kilobytes.',
'numeric' => 'O campo :attribute deve ser pelo menos :min.',
'string' => 'O campo :attribute deve ter pelo menos :min caracteres.',
],
'min_digits' => 'O campo :attribute deve ter pelo menos :min dígitos.',
'missing' => 'O campo :attribute deve estar ausente.',
'missing_if' => 'O campo :attribute deve estar ausente quando :other for :value.',
'missing_unless' => 'O campo :attribute deve estar ausente a menos que :other seja :value.',
'missing_with' => 'O campo :attribute deve estar ausente quando :values estiver presente.',
'missing_with_all' => 'O campo :attribute deve estar ausente quando :values estiverem presentes.',
'multiple_of' => 'O campo :attribute deve ser um múltiplo de :value.',
'not_in' => 'O :attribute selecionado é inválido.',
'not_regex' => 'O formato do campo :attribute é inválido.',
'numeric' => 'O campo :attribute deve ser um número.',
'password' => [
'letters' => 'O campo :attribute deve conter pelo menos uma letra.',
'mixed' => 'O campo :attribute deve conter pelo menos uma letra maiúscula e uma minúscula.',
'numbers' => 'O campo :attribute deve conter pelo menos um número.',
'symbols' => 'O campo :attribute deve conter pelo menos um símbolo.',
'uncompromised' => 'O :attribute fornecido apareceu em um vazamento de dados. Por favor, escolha um :attribute diferente.',
],
'present' => 'O campo :attribute deve estar presente.',
'present_if' => 'O campo :attribute deve estar presente quando :other for :value.',
'present_unless' => 'O campo :attribute deve estar presente a menos que :other seja :value.',
'present_with' => 'O campo :attribute deve estar presente quando :values estiver presente.',
'present_with_all' => 'O campo :attribute deve estar presente quando :values estiverem presentes.',
'prohibited' => 'O campo :attribute é proibido.',
'prohibited_if' => 'O campo :attribute é proibido quando :other for :value.',
'prohibited_if_accepted' => 'O campo :attribute é proibido quando :other for aceito.',
'prohibited_if_declined' => 'O campo :attribute é proibido quando :other for recusado.',
'prohibited_unless' => 'O campo :attribute é proibido a menos que :other esteja em :values.',
'prohibits' => 'O campo :attribute proíbe que :other esteja presente.',
'regex' => 'O formato do campo :attribute é inválido.',
'required' => 'O campo :attribute é obrigatório.',
'required_array_keys' => 'O campo :attribute deve conter entradas para: :values.',
'required_if' => 'O campo :attribute é obrigatório quando :other for :value.',
'required_if_accepted' => 'O campo :attribute é obrigatório quando :other for aceito.',
'required_if_declined' => 'O campo :attribute é obrigatório quando :other for recusado.',
'required_unless' => 'O campo :attribute é obrigatório a menos que :other esteja em :values.',
'required_with' => 'O campo :attribute é obrigatório quando :values estiver presente.',
'required_with_all' => 'O campo :attribute é obrigatório quando :values estiverem presentes.',
'required_without' => 'O campo :attribute é obrigatório quando :values não estiver presente.',
'required_without_all' => 'O campo :attribute é obrigatório quando nenhum de :values estiver presente.',
'same' => 'Os campos :attribute e :other devem corresponder.',
'size' => [
'array' => 'O campo :attribute deve conter :size itens.',
'file' => 'O campo :attribute deve ter :size kilobytes.',
'numeric' => 'O campo :attribute deve ser :size.',
'string' => 'O campo :attribute deve ter :size caracteres.',
],
'starts_with' => 'O campo :attribute deve começar com: :values.',
'string' => 'O campo :attribute deve ser uma string.',
'timezone' => 'O campo :attribute deve ser um fuso horário válido.',
'unique' => 'O :attribute já foi utilizado.',
'uploaded' => 'O upload do :attribute falhou.',
'uppercase' => 'O campo :attribute deve estar em maiúsculas.',
'url' => 'O campo :attribute deve ser uma URL válida.',
'ulid' => 'O campo :attribute deve ser um ULID válido.',
'uuid' => 'O campo :attribute deve ser um UUID válido.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Сообщения валидации
|--------------------------------------------------------------------------
*/
'accepted' => 'Поле :attribute должно быть принято.',
'accepted_if' => 'Поле :attribute должно быть принято, когда :other равно :value.',
'active_url' => 'Поле :attribute должно быть действительным URL.',
'after' => 'Поле :attribute должно быть датой после :date.',
'after_or_equal' => 'Поле :attribute должно быть датой после или равной :date.',
'alpha' => 'Поле :attribute должно содержать только буквы.',
'alpha_dash' => 'Поле :attribute должно содержать только буквы, цифры, дефисы и подчёркивания.',
'alpha_num' => 'Поле :attribute должно содержать только буквы и цифры.',
'any_of' => 'Поле :attribute недействительно.',
'array' => 'Поле :attribute должно быть массивом.',
'ascii' => 'Поле :attribute должно содержать только однобайтовые буквенно-цифровые символы и символы.',
'before' => 'Поле :attribute должно быть датой до :date.',
'before_or_equal' => 'Поле :attribute должно быть датой до или равной :date.',
'between' => [
'array' => 'Поле :attribute должно содержать от :min до :max элементов.',
'file' => 'Поле :attribute должно быть от :min до :max килобайт.',
'numeric' => 'Поле :attribute должно быть от :min до :max.',
'string' => 'Поле :attribute должно содержать от :min до :max символов.',
],
'boolean' => 'Поле :attribute должно быть истиной или ложью.',
'can' => 'Поле :attribute содержит неразрешённое значение.',
'confirmed' => 'Подтверждение поля :attribute не совпадает.',
'contains' => 'Поле :attribute не содержит обязательного значения.',
'current_password' => 'Пароль неверный.',
'date' => 'Поле :attribute должно быть действительной датой.',
'date_equals' => 'Поле :attribute должно быть датой, равной :date.',
'date_format' => 'Поле :attribute должно соответствовать формату :format.',
'decimal' => 'Поле :attribute должно иметь :decimal десятичных знаков.',
'declined' => 'Поле :attribute должно быть отклонено.',
'declined_if' => 'Поле :attribute должно быть отклонено, когда :other равно :value.',
'different' => 'Поля :attribute и :other должны различаться.',
'digits' => 'Поле :attribute должно содержать :digits цифр.',
'digits_between' => 'Поле :attribute должно содержать от :min до :max цифр.',
'dimensions' => 'Поле :attribute имеет недействительные размеры изображения.',
'distinct' => 'Поле :attribute содержит повторяющееся значение.',
'doesnt_contain' => 'Поле :attribute не должно содержать: :values.',
'doesnt_end_with' => 'Поле :attribute не должно заканчиваться на: :values.',
'doesnt_start_with' => 'Поле :attribute не должно начинаться с: :values.',
'email' => 'Поле :attribute должно быть действительным адресом электронной почты.',
'encoding' => 'Поле :attribute должно быть закодировано в :encoding.',
'ends_with' => 'Поле :attribute должно заканчиваться на: :values.',
'enum' => 'Выбранное :attribute недействительно.',
'exists' => 'Выбранное :attribute недействительно.',
'extensions' => 'Поле :attribute должно иметь одно из следующих расширений: :values.',
'file' => 'Поле :attribute должно быть файлом.',
'filled' => 'Поле :attribute должно иметь значение.',
'gt' => [
'array' => 'Поле :attribute должно содержать более :value элементов.',
'file' => 'Поле :attribute должно быть больше :value килобайт.',
'numeric' => 'Поле :attribute должно быть больше :value.',
'string' => 'Поле :attribute должно содержать более :value символов.',
],
'gte' => [
'array' => 'Поле :attribute должно содержать :value элементов или более.',
'file' => 'Поле :attribute должно быть больше или равно :value килобайт.',
'numeric' => 'Поле :attribute должно быть больше или равно :value.',
'string' => 'Поле :attribute должно содержать :value символов или более.',
],
'hex_color' => 'Поле :attribute должно быть действительным шестнадцатеричным цветом.',
'image' => 'Поле :attribute должно быть изображением.',
'in' => 'Выбранное :attribute недействительно.',
'in_array' => 'Поле :attribute должно существовать в :other.',
'in_array_keys' => 'Поле :attribute должно содержать хотя бы один из следующих ключей: :values.',
'integer' => 'Поле :attribute должно быть целым числом.',
'ip' => 'Поле :attribute должно быть действительным IP-адресом.',
'ipv4' => 'Поле :attribute должно быть действительным IPv4-адресом.',
'ipv6' => 'Поле :attribute должно быть действительным IPv6-адресом.',
'json' => 'Поле :attribute должно быть действительной JSON-строкой.',
'list' => 'Поле :attribute должно быть списком.',
'lowercase' => 'Поле :attribute должно быть в нижнем регистре.',
'lt' => [
'array' => 'Поле :attribute должно содержать менее :value элементов.',
'file' => 'Поле :attribute должно быть меньше :value килобайт.',
'numeric' => 'Поле :attribute должно быть меньше :value.',
'string' => 'Поле :attribute должно содержать менее :value символов.',
],
'lte' => [
'array' => 'Поле :attribute не должно содержать более :value элементов.',
'file' => 'Поле :attribute должно быть меньше или равно :value килобайт.',
'numeric' => 'Поле :attribute должно быть меньше или равно :value.',
'string' => 'Поле :attribute должно содержать :value символов или менее.',
],
'mac_address' => 'Поле :attribute должно быть действительным MAC-адресом.',
'max' => [
'array' => 'Поле :attribute не должно содержать более :max элементов.',
'file' => 'Поле :attribute не должно быть больше :max килобайт.',
'numeric' => 'Поле :attribute не должно быть больше :max.',
'string' => 'Поле :attribute не должно содержать более :max символов.',
],
'max_digits' => 'Поле :attribute не должно содержать более :max цифр.',
'mimes' => 'Поле :attribute должно быть файлом типа: :values.',
'mimetypes' => 'Поле :attribute должно быть файлом типа: :values.',
'min' => [
'array' => 'Поле :attribute должно содержать не менее :min элементов.',
'file' => 'Поле :attribute должно быть не менее :min килобайт.',
'numeric' => 'Поле :attribute должно быть не менее :min.',
'string' => 'Поле :attribute должно содержать не менее :min символов.',
],
'min_digits' => 'Поле :attribute должно содержать не менее :min цифр.',
'missing' => 'Поле :attribute должно отсутствовать.',
'missing_if' => 'Поле :attribute должно отсутствовать, когда :other равно :value.',
'missing_unless' => 'Поле :attribute должно отсутствовать, если :other не равно :value.',
'missing_with' => 'Поле :attribute должно отсутствовать, когда присутствует :values.',
'missing_with_all' => 'Поле :attribute должно отсутствовать, когда присутствуют :values.',
'multiple_of' => 'Поле :attribute должно быть кратным :value.',
'not_in' => 'Выбранное :attribute недействительно.',
'not_regex' => 'Формат поля :attribute недействителен.',
'numeric' => 'Поле :attribute должно быть числом.',
'password' => [
'letters' => 'Поле :attribute должно содержать хотя бы одну букву.',
'mixed' => 'Поле :attribute должно содержать хотя бы одну заглавную и одну строчную букву.',
'numbers' => 'Поле :attribute должно содержать хотя бы одну цифру.',
'symbols' => 'Поле :attribute должно содержать хотя бы один символ.',
'uncompromised' => 'Указанное :attribute появилось в утечке данных. Пожалуйста, выберите другое :attribute.',
],
'present' => 'Поле :attribute должно присутствовать.',
'present_if' => 'Поле :attribute должно присутствовать, когда :other равно :value.',
'present_unless' => 'Поле :attribute должно присутствовать, если :other не равно :value.',
'present_with' => 'Поле :attribute должно присутствовать, когда присутствует :values.',
'present_with_all' => 'Поле :attribute должно присутствовать, когда присутствуют :values.',
'prohibited' => 'Поле :attribute запрещено.',
'prohibited_if' => 'Поле :attribute запрещено, когда :other равно :value.',
'prohibited_if_accepted' => 'Поле :attribute запрещено, когда :other принято.',
'prohibited_if_declined' => 'Поле :attribute запрещено, когда :other отклонено.',
'prohibited_unless' => 'Поле :attribute запрещено, если :other не в :values.',
'prohibits' => 'Поле :attribute запрещает присутствие :other.',
'regex' => 'Формат поля :attribute недействителен.',
'required' => 'Поле :attribute обязательно.',
'required_array_keys' => 'Поле :attribute должно содержать записи для: :values.',
'required_if' => 'Поле :attribute обязательно, когда :other равно :value.',
'required_if_accepted' => 'Поле :attribute обязательно, когда :other принято.',
'required_if_declined' => 'Поле :attribute обязательно, когда :other отклонено.',
'required_unless' => 'Поле :attribute обязательно, если :other не в :values.',
'required_with' => 'Поле :attribute обязательно, когда присутствует :values.',
'required_with_all' => 'Поле :attribute обязательно, когда присутствуют :values.',
'required_without' => 'Поле :attribute обязательно, когда :values отсутствует.',
'required_without_all' => 'Поле :attribute обязательно, когда ни одно из :values не присутствует.',
'same' => 'Поля :attribute и :other должны совпадать.',
'size' => [
'array' => 'Поле :attribute должно содержать :size элементов.',
'file' => 'Поле :attribute должно быть :size килобайт.',
'numeric' => 'Поле :attribute должно быть :size.',
'string' => 'Поле :attribute должно содержать :size символов.',
],
'starts_with' => 'Поле :attribute должно начинаться с: :values.',
'string' => 'Поле :attribute должно быть строкой.',
'timezone' => 'Поле :attribute должно быть действительным часовым поясом.',
'unique' => 'Такое :attribute уже существует.',
'uploaded' => 'Загрузка :attribute не удалась.',
'uppercase' => 'Поле :attribute должно быть в верхнем регистре.',
'url' => 'Поле :attribute должно быть действительным URL.',
'ulid' => 'Поле :attribute должно быть действительным ULID.',
'uuid' => 'Поле :attribute должно быть действительным UUID.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| ข้อความการตรวจสอบ
|--------------------------------------------------------------------------
*/
'accepted' => 'ฟิลด์ :attribute ต้องได้รับการยอมรับ',
'accepted_if' => 'ฟิลด์ :attribute ต้องได้รับการยอมรับเมื่อ :other เป็น :value',
'active_url' => 'ฟิลด์ :attribute ต้องเป็น URL ที่ถูกต้อง',
'after' => 'ฟิลด์ :attribute ต้องเป็นวันที่หลังจาก :date',
'after_or_equal' => 'ฟิลด์ :attribute ต้องเป็นวันที่หลังหรือเท่ากับ :date',
'alpha' => 'ฟิลด์ :attribute ต้องมีเฉพาะตัวอักษรเท่านั้น',
'alpha_dash' => 'ฟิลด์ :attribute ต้องมีเฉพาะตัวอักษร ตัวเลข ขีด และขีดล่างเท่านั้น',
'alpha_num' => 'ฟิลด์ :attribute ต้องมีเฉพาะตัวอักษรและตัวเลขเท่านั้น',
'any_of' => 'ฟิลด์ :attribute ไม่ถูกต้อง',
'array' => 'ฟิลด์ :attribute ต้องเป็นอาร์เรย์',
'ascii' => 'ฟิลด์ :attribute ต้องมีเฉพาะอักขระตัวเลขและสัญลักษณ์แบบไบต์เดียวเท่านั้น',
'before' => 'ฟิลด์ :attribute ต้องเป็นวันที่ก่อน :date',
'before_or_equal' => 'ฟิลด์ :attribute ต้องเป็นวันที่ก่อนหรือเท่ากับ :date',
'between' => [
'array' => 'ฟิลด์ :attribute ต้องมีระหว่าง :min ถึง :max รายการ',
'file' => 'ฟิลด์ :attribute ต้องมีขนาดระหว่าง :min ถึง :max กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องอยู่ระหว่าง :min ถึง :max',
'string' => 'ฟิลด์ :attribute ต้องมีความยาวระหว่าง :min ถึง :max ตัวอักษร',
],
'boolean' => 'ฟิลด์ :attribute ต้องเป็นจริงหรือเท็จ',
'can' => 'ฟิลด์ :attribute มีค่าที่ไม่ได้รับอนุญาต',
'confirmed' => 'การยืนยันฟิลด์ :attribute ไม่ตรงกัน',
'contains' => 'ฟิลด์ :attribute ไม่มีค่าที่จำเป็น',
'current_password' => 'รหัสผ่านไม่ถูกต้อง',
'date' => 'ฟิลด์ :attribute ต้องเป็นวันที่ที่ถูกต้อง',
'date_equals' => 'ฟิลด์ :attribute ต้องเป็นวันที่เท่ากับ :date',
'date_format' => 'ฟิลด์ :attribute ต้องตรงกับรูปแบบ :format',
'decimal' => 'ฟิลด์ :attribute ต้องมีทศนิยม :decimal ตำแหน่ง',
'declined' => 'ฟิลด์ :attribute ต้องถูกปฏิเสธ',
'declined_if' => 'ฟิลด์ :attribute ต้องถูกปฏิเสธเมื่อ :other เป็น :value',
'different' => 'ฟิลด์ :attribute และ :other ต้องแตกต่างกัน',
'digits' => 'ฟิลด์ :attribute ต้องมี :digits หลัก',
'digits_between' => 'ฟิลด์ :attribute ต้องมีระหว่าง :min ถึง :max หลัก',
'dimensions' => 'ฟิลด์ :attribute มีขนาดรูปภาพไม่ถูกต้อง',
'distinct' => 'ฟิลด์ :attribute มีค่าซ้ำ',
'doesnt_contain' => 'ฟิลด์ :attribute ต้องไม่มี: :values',
'doesnt_end_with' => 'ฟิลด์ :attribute ต้องไม่ลงท้ายด้วย: :values',
'doesnt_start_with' => 'ฟิลด์ :attribute ต้องไม่ขึ้นต้นด้วย: :values',
'email' => 'ฟิลด์ :attribute ต้องเป็นที่อยู่อีเมลที่ถูกต้อง',
'encoding' => 'ฟิลด์ :attribute ต้องเข้ารหัสด้วย :encoding',
'ends_with' => 'ฟิลด์ :attribute ต้องลงท้ายด้วย: :values',
'enum' => ':attribute ที่เลือกไม่ถูกต้อง',
'exists' => ':attribute ที่เลือกไม่ถูกต้อง',
'extensions' => 'ฟิลด์ :attribute ต้องมีนามสกุลใดนามสกุลหนึ่งต่อไปนี้: :values',
'file' => 'ฟิลด์ :attribute ต้องเป็นไฟล์',
'filled' => 'ฟิลด์ :attribute ต้องมีค่า',
'gt' => [
'array' => 'ฟิลด์ :attribute ต้องมีมากกว่า :value รายการ',
'file' => 'ฟิลด์ :attribute ต้องใหญ่กว่า :value กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องมากกว่า :value',
'string' => 'ฟิลด์ :attribute ต้องยาวกว่า :value ตัวอักษร',
],
'gte' => [
'array' => 'ฟิลด์ :attribute ต้องมี :value รายการขึ้นไป',
'file' => 'ฟิลด์ :attribute ต้องใหญ่กว่าหรือเท่ากับ :value กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องมากกว่าหรือเท่ากับ :value',
'string' => 'ฟิลด์ :attribute ต้องยาว :value ตัวอักษรขึ้นไป',
],
'hex_color' => 'ฟิลด์ :attribute ต้องเป็นสีฐานสิบหกที่ถูกต้อง',
'image' => 'ฟิลด์ :attribute ต้องเป็นรูปภาพ',
'in' => ':attribute ที่เลือกไม่ถูกต้อง',
'in_array' => 'ฟิลด์ :attribute ต้องมีอยู่ใน :other',
'in_array_keys' => 'ฟิลด์ :attribute ต้องมีคีย์อย่างน้อยหนึ่งรายการต่อไปนี้: :values',
'integer' => 'ฟิลด์ :attribute ต้องเป็นจำนวนเต็ม',
'ip' => 'ฟิลด์ :attribute ต้องเป็นที่อยู่ IP ที่ถูกต้อง',
'ipv4' => 'ฟิลด์ :attribute ต้องเป็นที่อยู่ IPv4 ที่ถูกต้อง',
'ipv6' => 'ฟิลด์ :attribute ต้องเป็นที่อยู่ IPv6 ที่ถูกต้อง',
'json' => 'ฟิลด์ :attribute ต้องเป็นสตริง JSON ที่ถูกต้อง',
'list' => 'ฟิลด์ :attribute ต้องเป็นรายการ',
'lowercase' => 'ฟิลด์ :attribute ต้องเป็นตัวพิมพ์เล็ก',
'lt' => [
'array' => 'ฟิลด์ :attribute ต้องมีน้อยกว่า :value รายการ',
'file' => 'ฟิลด์ :attribute ต้องเล็กกว่า :value กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องน้อยกว่า :value',
'string' => 'ฟิลด์ :attribute ต้องสั้นกว่า :value ตัวอักษร',
],
'lte' => [
'array' => 'ฟิลด์ :attribute ต้องไม่เกิน :value รายการ',
'file' => 'ฟิลด์ :attribute ต้องเล็กกว่าหรือเท่ากับ :value กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องน้อยกว่าหรือเท่ากับ :value',
'string' => 'ฟิลด์ :attribute ต้องไม่เกิน :value ตัวอักษร',
],
'mac_address' => 'ฟิลด์ :attribute ต้องเป็นที่อยู่ MAC ที่ถูกต้อง',
'max' => [
'array' => 'ฟิลด์ :attribute ต้องไม่เกิน :max รายการ',
'file' => 'ฟิลด์ :attribute ต้องไม่เกิน :max กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องไม่เกิน :max',
'string' => 'ฟิลด์ :attribute ต้องไม่เกิน :max ตัวอักษร',
],
'max_digits' => 'ฟิลด์ :attribute ต้องไม่เกิน :max หลัก',
'mimes' => 'ฟิลด์ :attribute ต้องเป็นไฟล์ประเภท: :values',
'mimetypes' => 'ฟิลด์ :attribute ต้องเป็นไฟล์ประเภท: :values',
'min' => [
'array' => 'ฟิลด์ :attribute ต้องมีอย่างน้อย :min รายการ',
'file' => 'ฟิลด์ :attribute ต้องมีอย่างน้อย :min กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องมีอย่างน้อย :min',
'string' => 'ฟิลด์ :attribute ต้องมีอย่างน้อย :min ตัวอักษร',
],
'min_digits' => 'ฟิลด์ :attribute ต้องมีอย่างน้อย :min หลัก',
'missing' => 'ฟิลด์ :attribute ต้องไม่มี',
'missing_if' => 'ฟิลด์ :attribute ต้องไม่มีเมื่อ :other เป็น :value',
'missing_unless' => 'ฟิลด์ :attribute ต้องไม่มีเว้นแต่ :other เป็น :value',
'missing_with' => 'ฟิลด์ :attribute ต้องไม่มีเมื่อ :values มีอยู่',
'missing_with_all' => 'ฟิลด์ :attribute ต้องไม่มีเมื่อ :values มีอยู่',
'multiple_of' => 'ฟิลด์ :attribute ต้องเป็นพหุคูณของ :value',
'not_in' => ':attribute ที่เลือกไม่ถูกต้อง',
'not_regex' => 'รูปแบบฟิลด์ :attribute ไม่ถูกต้อง',
'numeric' => 'ฟิลด์ :attribute ต้องเป็นตัวเลข',
'password' => [
'letters' => 'ฟิลด์ :attribute ต้องมีอย่างน้อยหนึ่งตัวอักษร',
'mixed' => 'ฟิลด์ :attribute ต้องมีอย่างน้อยหนึ่งตัวพิมพ์ใหญ่และหนึ่งตัวพิมพ์เล็ก',
'numbers' => 'ฟิลด์ :attribute ต้องมีอย่างน้อยหนึ่งตัวเลข',
'symbols' => 'ฟิลด์ :attribute ต้องมีอย่างน้อยหนึ่งสัญลักษณ์',
'uncompromised' => ':attribute ที่ให้มามีการปรากฏในการรั่วไหลของข้อมูล กรุณาเลือก :attribute อื่น',
],
'present' => 'ฟิลด์ :attribute ต้องมีอยู่',
'present_if' => 'ฟิลด์ :attribute ต้องมีอยู่เมื่อ :other เป็น :value',
'present_unless' => 'ฟิลด์ :attribute ต้องมีอยู่เว้นแต่ :other เป็น :value',
'present_with' => 'ฟิลด์ :attribute ต้องมีอยู่เมื่อ :values มีอยู่',
'present_with_all' => 'ฟิลด์ :attribute ต้องมีอยู่เมื่อ :values มีอยู่',
'prohibited' => 'ฟิลด์ :attribute ถูกห้าม',
'prohibited_if' => 'ฟิลด์ :attribute ถูกห้ามเมื่อ :other เป็น :value',
'prohibited_if_accepted' => 'ฟิลด์ :attribute ถูกห้ามเมื่อ :other ถูกยอมรับ',
'prohibited_if_declined' => 'ฟิลด์ :attribute ถูกห้ามเมื่อ :other ถูกปฏิเสธ',
'prohibited_unless' => 'ฟิลด์ :attribute ถูกห้ามเว้นแต่ :other อยู่ใน :values',
'prohibits' => 'ฟิลด์ :attribute ห้าม :other มีอยู่',
'regex' => 'รูปแบบฟิลด์ :attribute ไม่ถูกต้อง',
'required' => 'ฟิลด์ :attribute จำเป็นต้องมี',
'required_array_keys' => 'ฟิลด์ :attribute ต้องมีรายการสำหรับ: :values',
'required_if' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :other เป็น :value',
'required_if_accepted' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :other ถูกยอมรับ',
'required_if_declined' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :other ถูกปฏิเสธ',
'required_unless' => 'ฟิลด์ :attribute จำเป็นต้องมีเว้นแต่ :other อยู่ใน :values',
'required_with' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :values มีอยู่',
'required_with_all' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :values มีอยู่',
'required_without' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อ :values ไม่มีอยู่',
'required_without_all' => 'ฟิลด์ :attribute จำเป็นต้องมีเมื่อไม่มี :values',
'same' => 'ฟิลด์ :attribute และ :other ต้องตรงกัน',
'size' => [
'array' => 'ฟิลด์ :attribute ต้องมี :size รายการ',
'file' => 'ฟิลด์ :attribute ต้องมีขนาด :size กิโลไบต์',
'numeric' => 'ฟิลด์ :attribute ต้องเป็น :size',
'string' => 'ฟิลด์ :attribute ต้องมีความยาว :size ตัวอักษร',
],
'starts_with' => 'ฟิลด์ :attribute ต้องขึ้นต้นด้วย: :values',
'string' => 'ฟิลด์ :attribute ต้องเป็นสตริง',
'timezone' => 'ฟิลด์ :attribute ต้องเป็นเขตเวลาที่ถูกต้อง',
'unique' => ':attribute ถูกใช้ไปแล้ว',
'uploaded' => 'การอัปโหลด :attribute ล้มเหลว',
'uppercase' => 'ฟิลด์ :attribute ต้องเป็นตัวพิมพ์ใหญ่',
'url' => 'ฟิลด์ :attribute ต้องเป็น URL ที่ถูกต้อง',
'ulid' => 'ฟิลด์ :attribute ต้องเป็น ULID ที่ถูกต้อง',
'uuid' => 'ฟิลด์ :attribute ต้องเป็น UUID ที่ถูกต้อง',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Doğrulama mesajları
|--------------------------------------------------------------------------
*/
'accepted' => ':attribute alanı kabul edilmelidir.',
'accepted_if' => ':other :value olduğunda :attribute alanı kabul edilmelidir.',
'active_url' => ':attribute alanı geçerli bir URL olmalıdır.',
'after' => ':attribute alanı :date tarihinden sonra bir tarih olmalıdır.',
'after_or_equal' => ':attribute alanı :date tarihinden sonra veya aynı tarihte olmalıdır.',
'alpha' => ':attribute alanı yalnızca harf içermelidir.',
'alpha_dash' => ':attribute alanı yalnızca harf, rakam, tire ve alt çizgi içermelidir.',
'alpha_num' => ':attribute alanı yalnızca harf ve rakam içermelidir.',
'any_of' => ':attribute alanı geçersiz.',
'array' => ':attribute alanı bir dizi olmalıdır.',
'ascii' => ':attribute alanı yalnızca tek baytlık alfanümerik karakterler ve semboller içermelidir.',
'before' => ':attribute alanı :date tarihinden önce bir tarih olmalıdır.',
'before_or_equal' => ':attribute alanı :date tarihinden önce veya aynı tarihte olmalıdır.',
'between' => [
'array' => ':attribute alanı :min ile :max öğe arasında olmalıdır.',
'file' => ':attribute alanı :min ile :max kilobayt arasında olmalıdır.',
'numeric' => ':attribute alanı :min ile :max arasında olmalıdır.',
'string' => ':attribute alanı :min ile :max karakter arasında olmalıdır.',
],
'boolean' => ':attribute alanı doğru veya yanlış olmalıdır.',
'can' => ':attribute alanı yetkisiz bir değer içeriyor.',
'confirmed' => ':attribute alanı onayı eşleşmiyor.',
'contains' => ':attribute alanı gerekli bir değer içermiyor.',
'current_password' => 'Şifre yanlış.',
'date' => ':attribute alanı geçerli bir tarih olmalıdır.',
'date_equals' => ':attribute alanı :date tarihine eşit bir tarih olmalıdır.',
'date_format' => ':attribute alanı :format biçimine uymalıdır.',
'decimal' => ':attribute alanı :decimal ondalık basamağa sahip olmalıdır.',
'declined' => ':attribute alanı reddedilmelidir.',
'declined_if' => ':other :value olduğunda :attribute alanı reddedilmelidir.',
'different' => ':attribute ve :other alanları farklı olmalıdır.',
'digits' => ':attribute alanı :digits basamaklı olmalıdır.',
'digits_between' => ':attribute alanı :min ile :max basamak arasında olmalıdır.',
'dimensions' => ':attribute alanının görsel boyutları geçersiz.',
'distinct' => ':attribute alanında yinelenen bir değer var.',
'doesnt_contain' => ':attribute alanı şunları içermemelidir: :values.',
'doesnt_end_with' => ':attribute alanı şunlarla bitmemelidir: :values.',
'doesnt_start_with' => ':attribute alanı şunlarla başlamamalıdır: :values.',
'email' => ':attribute alanı geçerli bir e-posta adresi olmalıdır.',
'encoding' => ':attribute alanı :encoding ile kodlanmış olmalıdır.',
'ends_with' => ':attribute alanı şunlarla bitmelidir: :values.',
'enum' => 'Seçilen :attribute geçersiz.',
'exists' => 'Seçilen :attribute geçersiz.',
'extensions' => ':attribute alanı şu uzantılardan birine sahip olmalıdır: :values.',
'file' => ':attribute alanı bir dosya olmalıdır.',
'filled' => ':attribute alanının bir değeri olmalıdır.',
'gt' => [
'array' => ':attribute alanı :value öğeden fazla içermelidir.',
'file' => ':attribute alanı :value kilobayttan büyük olmalıdır.',
'numeric' => ':attribute alanı :value değerinden büyük olmalıdır.',
'string' => ':attribute alanı :value karakterden fazla içermelidir.',
],
'gte' => [
'array' => ':attribute alanı :value veya daha fazla öğe içermelidir.',
'file' => ':attribute alanı :value kilobayt veya daha fazla olmalıdır.',
'numeric' => ':attribute alanı :value veya daha fazla olmalıdır.',
'string' => ':attribute alanı :value veya daha fazla karakter içermelidir.',
],
'hex_color' => ':attribute alanı geçerli bir onaltılık renk olmalıdır.',
'image' => ':attribute alanı bir görsel olmalıdır.',
'in' => 'Seçilen :attribute geçersiz.',
'in_array' => ':attribute alanı :other içinde bulunmalıdır.',
'in_array_keys' => ':attribute alanı şu anahtarlardan en az birini içermelidir: :values.',
'integer' => ':attribute alanı bir tam sayı olmalıdır.',
'ip' => ':attribute alanı geçerli bir IP adresi olmalıdır.',
'ipv4' => ':attribute alanı geçerli bir IPv4 adresi olmalıdır.',
'ipv6' => ':attribute alanı geçerli bir IPv6 adresi olmalıdır.',
'json' => ':attribute alanı geçerli bir JSON dizisi olmalıdır.',
'list' => ':attribute alanı bir liste olmalıdır.',
'lowercase' => ':attribute alanı küçük harf olmalıdır.',
'lt' => [
'array' => ':attribute alanı :value öğeden az içermelidir.',
'file' => ':attribute alanı :value kilobayttan küçük olmalıdır.',
'numeric' => ':attribute alanı :value değerinden küçük olmalıdır.',
'string' => ':attribute alanı :value karakterden az içermelidir.',
],
'lte' => [
'array' => ':attribute alanı :value öğeden fazla içermemelidir.',
'file' => ':attribute alanı :value kilobayt veya daha az olmalıdır.',
'numeric' => ':attribute alanı :value veya daha az olmalıdır.',
'string' => ':attribute alanı :value karakter veya daha az içermelidir.',
],
'mac_address' => ':attribute alanı geçerli bir MAC adresi olmalıdır.',
'max' => [
'array' => ':attribute alanı :max öğeden fazla içermemelidir.',
'file' => ':attribute alanı :max kilobayttan büyük olmamalıdır.',
'numeric' => ':attribute alanı :max değerinden büyük olmamalıdır.',
'string' => ':attribute alanı :max karakterden fazla içermemelidir.',
],
'max_digits' => ':attribute alanı :max basamaktan fazla içermemelidir.',
'mimes' => ':attribute alanı şu türde bir dosya olmalıdır: :values.',
'mimetypes' => ':attribute alanı şu türde bir dosya olmalıdır: :values.',
'min' => [
'array' => ':attribute alanı en az :min öğe içermelidir.',
'file' => ':attribute alanı en az :min kilobayt olmalıdır.',
'numeric' => ':attribute alanı en az :min olmalıdır.',
'string' => ':attribute alanı en az :min karakter içermelidir.',
],
'min_digits' => ':attribute alanı en az :min basamak içermelidir.',
'missing' => ':attribute alanı eksik olmalıdır.',
'missing_if' => ':other :value olduğunda :attribute alanı eksik olmalıdır.',
'missing_unless' => ':other :value olmadıkça :attribute alanı eksik olmalıdır.',
'missing_with' => ':values mevcut olduğunda :attribute alanı eksik olmalıdır.',
'missing_with_all' => ':values mevcut olduğunda :attribute alanı eksik olmalıdır.',
'multiple_of' => ':attribute alanı :value\'nin katı olmalıdır.',
'not_in' => 'Seçilen :attribute geçersiz.',
'not_regex' => ':attribute alanı biçimi geçersiz.',
'numeric' => ':attribute alanı bir sayı olmalıdır.',
'password' => [
'letters' => ':attribute alanı en az bir harf içermelidir.',
'mixed' => ':attribute alanı en az bir büyük ve bir küçük harf içermelidir.',
'numbers' => ':attribute alanı en az bir rakam içermelidir.',
'symbols' => ':attribute alanı en az bir sembol içermelidir.',
'uncompromised' => 'Verilen :attribute bir veri sızıntısında göründü. Lütfen farklı bir :attribute seçin.',
],
'present' => ':attribute alanı mevcut olmalıdır.',
'present_if' => ':other :value olduğunda :attribute alanı mevcut olmalıdır.',
'present_unless' => ':other :value olmadıkça :attribute alanı mevcut olmalıdır.',
'present_with' => ':values mevcut olduğunda :attribute alanı mevcut olmalıdır.',
'present_with_all' => ':values mevcut olduğunda :attribute alanı mevcut olmalıdır.',
'prohibited' => ':attribute alanı yasaktır.',
'prohibited_if' => ':other :value olduğunda :attribute alanı yasaktır.',
'prohibited_if_accepted' => ':other kabul edildiğinde :attribute alanı yasaktır.',
'prohibited_if_declined' => ':other reddedildiğinde :attribute alanı yasaktır.',
'prohibited_unless' => ':other :values içinde olmadıkça :attribute alanı yasaktır.',
'prohibits' => ':attribute alanı :other\'ın mevcut olmasını yasaklar.',
'regex' => ':attribute alanı biçimi geçersiz.',
'required' => ':attribute alanı zorunludur.',
'required_array_keys' => ':attribute alanı şunlar için girişler içermelidir: :values.',
'required_if' => ':other :value olduğunda :attribute alanı zorunludur.',
'required_if_accepted' => ':other kabul edildiğinde :attribute alanı zorunludur.',
'required_if_declined' => ':other reddedildiğinde :attribute alanı zorunludur.',
'required_unless' => ':other :values içinde olmadıkça :attribute alanı zorunludur.',
'required_with' => ':values mevcut olduğunda :attribute alanı zorunludur.',
'required_with_all' => ':values mevcut olduğunda :attribute alanı zorunludur.',
'required_without' => ':values mevcut olmadığında :attribute alanı zorunludur.',
'required_without_all' => ':values\'tan hiçbiri mevcut olmadığında :attribute alanı zorunludur.',
'same' => ':attribute ve :other alanları eşleşmelidir.',
'size' => [
'array' => ':attribute alanı :size öğe içermelidir.',
'file' => ':attribute alanı :size kilobayt olmalıdır.',
'numeric' => ':attribute alanı :size olmalıdır.',
'string' => ':attribute alanı :size karakter içermelidir.',
],
'starts_with' => ':attribute alanı şunlarla başlamalıdır: :values.',
'string' => ':attribute alanı bir dizge olmalıdır.',
'timezone' => ':attribute alanı geçerli bir saat dilimi olmalıdır.',
'unique' => ':attribute zaten kullanılmış.',
'uploaded' => ':attribute yüklenemedi.',
'uppercase' => ':attribute alanı büyük harf olmalıdır.',
'url' => ':attribute alanı geçerli bir URL olmalıdır.',
'ulid' => ':attribute alanı geçerli bir ULID olmalıdır.',
'uuid' => ':attribute alanı geçerli bir UUID olmalıdır.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Thông báo xác thực
|--------------------------------------------------------------------------
*/
'accepted' => 'Trường :attribute phải được chấp nhận.',
'accepted_if' => 'Trường :attribute phải được chấp nhận khi :other là :value.',
'active_url' => 'Trường :attribute phải là URL hợp lệ.',
'after' => 'Trường :attribute phải là ngày sau :date.',
'after_or_equal' => 'Trường :attribute phải là ngày sau hoặc bằng :date.',
'alpha' => 'Trường :attribute chỉ được chứa chữ cái.',
'alpha_dash' => 'Trường :attribute chỉ được chứa chữ cái, số, dấu gạch ngang và gạch dưới.',
'alpha_num' => 'Trường :attribute chỉ được chứa chữ cái và số.',
'any_of' => 'Trường :attribute không hợp lệ.',
'array' => 'Trường :attribute phải là mảng.',
'ascii' => 'Trường :attribute chỉ được chứa ký tự chữ số và ký hiệu một byte.',
'before' => 'Trường :attribute phải là ngày trước :date.',
'before_or_equal' => 'Trường :attribute phải là ngày trước hoặc bằng :date.',
'between' => [
'array' => 'Trường :attribute phải có từ :min đến :max phần tử.',
'file' => 'Trường :attribute phải từ :min đến :max kilobyte.',
'numeric' => 'Trường :attribute phải từ :min đến :max.',
'string' => 'Trường :attribute phải có từ :min đến :max ký tự.',
],
'boolean' => 'Trường :attribute phải là đúng hoặc sai.',
'can' => 'Trường :attribute chứa giá trị không được phép.',
'confirmed' => 'Xác nhận trường :attribute không khớp.',
'contains' => 'Trường :attribute thiếu giá trị bắt buộc.',
'current_password' => 'Mật khẩu không đúng.',
'date' => 'Trường :attribute phải là ngày hợp lệ.',
'date_equals' => 'Trường :attribute phải là ngày bằng :date.',
'date_format' => 'Trường :attribute phải khớp với định dạng :format.',
'decimal' => 'Trường :attribute phải có :decimal chữ số thập phân.',
'declined' => 'Trường :attribute phải bị từ chối.',
'declined_if' => 'Trường :attribute phải bị từ chối khi :other là :value.',
'different' => 'Các trường :attribute và :other phải khác nhau.',
'digits' => 'Trường :attribute phải có :digits chữ số.',
'digits_between' => 'Trường :attribute phải có từ :min đến :max chữ số.',
'dimensions' => 'Trường :attribute có kích thước hình ảnh không hợp lệ.',
'distinct' => 'Trường :attribute có giá trị trùng lặp.',
'doesnt_contain' => 'Trường :attribute không được chứa: :values.',
'doesnt_end_with' => 'Trường :attribute không được kết thúc bằng: :values.',
'doesnt_start_with' => 'Trường :attribute không được bắt đầu bằng: :values.',
'email' => 'Trường :attribute phải là địa chỉ email hợp lệ.',
'encoding' => 'Trường :attribute phải được mã hóa bằng :encoding.',
'ends_with' => 'Trường :attribute phải kết thúc bằng: :values.',
'enum' => ':attribute được chọn không hợp lệ.',
'exists' => ':attribute được chọn không hợp lệ.',
'extensions' => 'Trường :attribute phải có một trong các phần mở rộng sau: :values.',
'file' => 'Trường :attribute phải là tệp.',
'filled' => 'Trường :attribute phải có giá trị.',
'gt' => [
'array' => 'Trường :attribute phải có nhiều hơn :value phần tử.',
'file' => 'Trường :attribute phải lớn hơn :value kilobyte.',
'numeric' => 'Trường :attribute phải lớn hơn :value.',
'string' => 'Trường :attribute phải có nhiều hơn :value ký tự.',
],
'gte' => [
'array' => 'Trường :attribute phải có :value phần tử trở lên.',
'file' => 'Trường :attribute phải lớn hơn hoặc bằng :value kilobyte.',
'numeric' => 'Trường :attribute phải lớn hơn hoặc bằng :value.',
'string' => 'Trường :attribute phải có :value ký tự trở lên.',
],
'hex_color' => 'Trường :attribute phải là màu hex hợp lệ.',
'image' => 'Trường :attribute phải là hình ảnh.',
'in' => ':attribute được chọn không hợp lệ.',
'in_array' => 'Trường :attribute phải tồn tại trong :other.',
'in_array_keys' => 'Trường :attribute phải chứa ít nhất một trong các khóa sau: :values.',
'integer' => 'Trường :attribute phải là số nguyên.',
'ip' => 'Trường :attribute phải là địa chỉ IP hợp lệ.',
'ipv4' => 'Trường :attribute phải là địa chỉ IPv4 hợp lệ.',
'ipv6' => 'Trường :attribute phải là địa chỉ IPv6 hợp lệ.',
'json' => 'Trường :attribute phải là chuỗi JSON hợp lệ.',
'list' => 'Trường :attribute phải là danh sách.',
'lowercase' => 'Trường :attribute phải viết thường.',
'lt' => [
'array' => 'Trường :attribute phải có ít hơn :value phần tử.',
'file' => 'Trường :attribute phải nhỏ hơn :value kilobyte.',
'numeric' => 'Trường :attribute phải nhỏ hơn :value.',
'string' => 'Trường :attribute phải có ít hơn :value ký tự.',
],
'lte' => [
'array' => 'Trường :attribute không được có quá :value phần tử.',
'file' => 'Trường :attribute phải nhỏ hơn hoặc bằng :value kilobyte.',
'numeric' => 'Trường :attribute phải nhỏ hơn hoặc bằng :value.',
'string' => 'Trường :attribute phải có :value ký tự trở xuống.',
],
'mac_address' => 'Trường :attribute phải là địa chỉ MAC hợp lệ.',
'max' => [
'array' => 'Trường :attribute không được có quá :max phần tử.',
'file' => 'Trường :attribute không được lớn hơn :max kilobyte.',
'numeric' => 'Trường :attribute không được lớn hơn :max.',
'string' => 'Trường :attribute không được có quá :max ký tự.',
],
'max_digits' => 'Trường :attribute không được có quá :max chữ số.',
'mimes' => 'Trường :attribute phải là tệp loại: :values.',
'mimetypes' => 'Trường :attribute phải là tệp loại: :values.',
'min' => [
'array' => 'Trường :attribute phải có ít nhất :min phần tử.',
'file' => 'Trường :attribute phải ít nhất :min kilobyte.',
'numeric' => 'Trường :attribute phải ít nhất :min.',
'string' => 'Trường :attribute phải có ít nhất :min ký tự.',
],
'min_digits' => 'Trường :attribute phải có ít nhất :min chữ số.',
'missing' => 'Trường :attribute phải vắng mặt.',
'missing_if' => 'Trường :attribute phải vắng mặt khi :other là :value.',
'missing_unless' => 'Trường :attribute phải vắng mặt trừ khi :other là :value.',
'missing_with' => 'Trường :attribute phải vắng mặt khi :values có mặt.',
'missing_with_all' => 'Trường :attribute phải vắng mặt khi :values có mặt.',
'multiple_of' => 'Trường :attribute phải là bội số của :value.',
'not_in' => ':attribute được chọn không hợp lệ.',
'not_regex' => 'Định dạng trường :attribute không hợp lệ.',
'numeric' => 'Trường :attribute phải là số.',
'password' => [
'letters' => 'Trường :attribute phải chứa ít nhất một chữ cái.',
'mixed' => 'Trường :attribute phải chứa ít nhất một chữ hoa và một chữ thường.',
'numbers' => 'Trường :attribute phải chứa ít nhất một số.',
'symbols' => 'Trường :attribute phải chứa ít nhất một ký hiệu.',
'uncompromised' => ':attribute đã xuất hiện trong rò rỉ dữ liệu. Vui lòng chọn :attribute khác.',
],
'present' => 'Trường :attribute phải có mặt.',
'present_if' => 'Trường :attribute phải có mặt khi :other là :value.',
'present_unless' => 'Trường :attribute phải có mặt trừ khi :other là :value.',
'present_with' => 'Trường :attribute phải có mặt khi :values có mặt.',
'present_with_all' => 'Trường :attribute phải có mặt khi :values có mặt.',
'prohibited' => 'Trường :attribute bị cấm.',
'prohibited_if' => 'Trường :attribute bị cấm khi :other là :value.',
'prohibited_if_accepted' => 'Trường :attribute bị cấm khi :other được chấp nhận.',
'prohibited_if_declined' => 'Trường :attribute bị cấm khi :other bị từ chối.',
'prohibited_unless' => 'Trường :attribute bị cấm trừ khi :other nằm trong :values.',
'prohibits' => 'Trường :attribute cấm :other có mặt.',
'regex' => 'Định dạng trường :attribute không hợp lệ.',
'required' => 'Trường :attribute là bắt buộc.',
'required_array_keys' => 'Trường :attribute phải chứa mục cho: :values.',
'required_if' => 'Trường :attribute là bắt buộc khi :other là :value.',
'required_if_accepted' => 'Trường :attribute là bắt buộc khi :other được chấp nhận.',
'required_if_declined' => 'Trường :attribute là bắt buộc khi :other bị từ chối.',
'required_unless' => 'Trường :attribute là bắt buộc trừ khi :other nằm trong :values.',
'required_with' => 'Trường :attribute là bắt buộc khi :values có mặt.',
'required_with_all' => 'Trường :attribute là bắt buộc khi :values có mặt.',
'required_without' => 'Trường :attribute là bắt buộc khi :values vắng mặt.',
'required_without_all' => 'Trường :attribute là bắt buộc khi không có :values nào có mặt.',
'same' => 'Các trường :attribute và :other phải khớp.',
'size' => [
'array' => 'Trường :attribute phải chứa :size phần tử.',
'file' => 'Trường :attribute phải là :size kilobyte.',
'numeric' => 'Trường :attribute phải là :size.',
'string' => 'Trường :attribute phải có :size ký tự.',
],
'starts_with' => 'Trường :attribute phải bắt đầu bằng: :values.',
'string' => 'Trường :attribute phải là chuỗi.',
'timezone' => 'Trường :attribute phải là múi giờ hợp lệ.',
'unique' => ':attribute đã được sử dụng.',
'uploaded' => 'Tải lên :attribute thất bại.',
'uppercase' => 'Trường :attribute phải viết hoa.',
'url' => 'Trường :attribute phải là URL hợp lệ.',
'ulid' => 'Trường :attribute phải là ULID hợp lệ.',
'uuid' => 'Trường :attribute phải là UUID hợp lệ.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
@@ -0,0 +1,180 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| 验证提示语
|--------------------------------------------------------------------------
*/
'accepted' => ':attribute 必须接受。',
'accepted_if' => '当 :other 为 :value 时 :attribute 必须接受。',
'active_url' => ':attribute 必须是一个有效的 URL。',
'after' => ':attribute 必须是 :date 之后的日期。',
'after_or_equal' => ':attribute 必须是 :date 之后或相同的日期。',
'alpha' => ':attribute 只能包含字母。',
'alpha_dash' => ':attribute 只能包含字母、数字、短横线和下划线。',
'alpha_num' => ':attribute 只能包含字母和数字。',
'any_of' => ':attribute 无效。',
'array' => ':attribute 必须是数组。',
'ascii' => ':attribute 只能包含 ASCII 字符。',
'before' => ':attribute 必须是 :date 之前的日期。',
'before_or_equal' => ':attribute 必须是 :date 之前或相同的日期。',
'between' => [
'array' => ':attribute 必须在 :min 到 :max 个之间。',
'file' => ':attribute 必须在 :min 到 :max KB 之间。',
'numeric' => ':attribute 必须在 :min 到 :max 之间。',
'string' => ':attribute 必须在 :min 到 :max 个字符之间。',
],
'boolean' => ':attribute 必须是布尔值。',
'can' => ':attribute 包含未授权的值。',
'confirmed' => ':attribute 两次输入不一致。',
'contains' => ':attribute 缺少必需值。',
'current_password' => '密码不正确。',
'date' => ':attribute 必须是有效日期。',
'date_equals' => ':attribute 必须等于 :date。',
'date_format' => ':attribute 格式必须为 :format。',
'decimal' => ':attribute 必须有 :decimal 位小数。',
'declined' => ':attribute 必须拒绝。',
'declined_if' => '当 :other 为 :value 时 :attribute 必须拒绝。',
'different' => ':attribute 与 :other 必须不同。',
'digits' => ':attribute 必须是 :digits 位数字。',
'digits_between' => ':attribute 必须在 :min 到 :max 位数字之间。',
'dimensions' => ':attribute 图片尺寸无效。',
'distinct' => ':attribute 存在重复值。',
'doesnt_contain' => ':attribute 不能包含以下内容::values。',
'doesnt_end_with' => ':attribute 不能以下列之一结尾::values。',
'doesnt_start_with' => ':attribute 不能以下列之一开头::values。',
'email' => ':attribute 必须是有效的邮箱地址。',
'encoding' => ':attribute 必须使用 :encoding 编码。',
'ends_with' => ':attribute 必须以下列之一结尾::values。',
'enum' => '所选的 :attribute 无效。',
'exists' => '所选的 :attribute 无效。',
'extensions' => ':attribute 必须具有以下扩展名之一::values。',
'file' => ':attribute 必须是文件。',
'filled' => ':attribute 必须有值。',
'gt' => [
'array' => ':attribute 必须多于 :value 个元素。',
'file' => ':attribute 必须大于 :value KB。',
'numeric' => ':attribute 必须大于 :value。',
'string' => ':attribute 必须大于 :value 个字符。',
],
'gte' => [
'array' => ':attribute 必须不少于 :value 个元素。',
'file' => ':attribute 必须大于等于 :value KB。',
'numeric' => ':attribute 必须大于等于 :value。',
'string' => ':attribute 必须大于等于 :value 个字符。',
],
'hex_color' => ':attribute 必须是有效的十六进制颜色值。',
'image' => ':attribute 必须是图片。',
'in' => '所选的 :attribute 无效。',
'in_array' => ':attribute 必须存在于 :other 中。',
'in_array_keys' => ':attribute 必须包含以下键之一::values。',
'integer' => ':attribute 必须是整数。',
'ip' => ':attribute 必须是有效的 IP 地址。',
'ipv4' => ':attribute 必须是有效的 IPv4 地址。',
'ipv6' => ':attribute 必须是有效的 IPv6 地址。',
'json' => ':attribute 必须是有效的 JSON 字符串。',
'list' => ':attribute 必须是列表。',
'lowercase' => ':attribute 必须是小写。',
'lt' => [
'array' => ':attribute 必须少于 :value 个元素。',
'file' => ':attribute 必须小于 :value KB。',
'numeric' => ':attribute 必须小于 :value。',
'string' => ':attribute 必须小于 :value 个字符。',
],
'lte' => [
'array' => ':attribute 不能多于 :value 个元素。',
'file' => ':attribute 必须小于等于 :value KB。',
'numeric' => ':attribute 必须小于等于 :value。',
'string' => ':attribute 必须小于等于 :value 个字符。',
],
'mac_address' => ':attribute 必须是有效的 MAC 地址。',
'max' => [
'array' => ':attribute 不能超过 :max 个元素。',
'file' => ':attribute 不能大于 :max KB。',
'numeric' => ':attribute 不能大于 :max。',
'string' => ':attribute 不能大于 :max 个字符。',
],
'max_digits' => ':attribute 不能超过 :max 位数字。',
'mimes' => ':attribute 必须是 :values 类型的文件。',
'mimetypes' => ':attribute 必须是 :values 类型的文件。',
'min' => [
'array' => ':attribute 至少有 :min 个元素。',
'file' => ':attribute 至少 :min KB。',
'numeric' => ':attribute 至少 :min。',
'string' => ':attribute 至少 :min 个字符。',
],
'min_digits' => ':attribute 至少 :min 位数字。',
'missing' => ':attribute 必须不存在。',
'missing_if' => '当 :other 为 :value 时 :attribute 必须不存在。',
'missing_unless' => '除非 :other 为 :value,否则 :attribute 必须不存在。',
'missing_with' => '当 :values 存在时 :attribute 必须不存在。',
'missing_with_all' => '当 :values 都存在时 :attribute 必须不存在。',
'multiple_of' => ':attribute 必须是 :value 的倍数。',
'not_in' => '所选的 :attribute 无效。',
'not_regex' => ':attribute 格式无效。',
'numeric' => ':attribute 必须是数字。',
'password' => [
'letters' => ':attribute 必须包含至少一个字母。',
'mixed' => ':attribute 必须包含至少一个大写和一个小写字母。',
'numbers' => ':attribute 必须包含至少一个数字。',
'symbols' => ':attribute 必须包含至少一个符号。',
'uncompromised' => ':attribute 已出现在数据泄露中,请更换。',
],
'present' => ':attribute 必须存在。',
'present_if' => '当 :other 为 :value 时 :attribute 必须存在。',
'present_unless' => '除非 :other 为 :value,否则 :attribute 必须存在。',
'present_with' => '当 :values 存在时 :attribute 必须存在。',
'present_with_all' => '当 :values 都存在时 :attribute 必须存在。',
'prohibited' => ':attribute 被禁止。',
'prohibited_if' => '当 :other 为 :value 时 :attribute 被禁止。',
'prohibited_if_accepted' => '当 :other 被接受时 :attribute 被禁止。',
'prohibited_if_declined' => '当 :other 被拒绝时 :attribute 被禁止。',
'prohibited_unless' => '除非 :other 在 :values 中,否则 :attribute 被禁止。',
'prohibits' => ':attribute 禁止 :other 出现。',
'regex' => ':attribute 格式无效。',
'required' => ':attribute 为必填项。',
'required_array_keys' => ':attribute 必须包含以下键::values。',
'required_if' => '当 :other 为 :value 时 :attribute 为必填项。',
'required_if_accepted' => '当 :other 被接受时 :attribute 为必填项。',
'required_if_declined' => '当 :other 被拒绝时 :attribute 为必填项。',
'required_unless' => '除非 :other 在 :values 中,否则 :attribute 为必填项。',
'required_with' => '当 :values 存在时 :attribute 为必填项。',
'required_with_all' => '当 :values 都存在时 :attribute 为必填项。',
'required_without' => '当 :values 不存在时 :attribute 为必填项。',
'required_without_all' => '当 :values 都不存在时 :attribute 为必填项。',
'same' => ':attribute 与 :other 必须相同。',
'size' => [
'array' => ':attribute 必须包含 :size 个元素。',
'file' => ':attribute 必须为 :size KB。',
'numeric' => ':attribute 必须为 :size。',
'string' => ':attribute 必须为 :size 个字符。',
],
'starts_with' => ':attribute 必须以下列之一开头::values。',
'string' => ':attribute 必须是字符串。',
'timezone' => ':attribute 必须是有效的时区。',
'unique' => ':attribute 已存在。',
'uploaded' => ':attribute 上传失败。',
'uppercase' => ':attribute 必须是大写。',
'url' => ':attribute 必须是有效的 URL。',
'ulid' => ':attribute 必须是有效的 ULID。',
'uuid' => ':attribute 必须是有效的 UUID。',
/*
|--------------------------------------------------------------------------
| 自定义验证语言行
|--------------------------------------------------------------------------
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| 自定义属性名称
|--------------------------------------------------------------------------
*/
'attributes' => [],
];
@@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| 驗證提示語
|--------------------------------------------------------------------------
*/
'accepted' => ':attribute 必須接受。',
'accepted_if' => '當 :other 為 :value 時 :attribute 必須接受。',
'active_url' => ':attribute 必須是一個有效的 URL。',
'after' => ':attribute 必須是 :date 之後的日期。',
'after_or_equal' => ':attribute 必須是 :date 之後或相同的日期。',
'alpha' => ':attribute 只能包含字母。',
'alpha_dash' => ':attribute 只能包含字母、數字、短橫線和底線。',
'alpha_num' => ':attribute 只能包含字母和數字。',
'any_of' => ':attribute 無效。',
'array' => ':attribute 必須是陣列。',
'ascii' => ':attribute 只能包含 ASCII 字元。',
'before' => ':attribute 必須是 :date 之前的日期。',
'before_or_equal' => ':attribute 必須是 :date 之前或相同的日期。',
'between' => [
'array' => ':attribute 必須在 :min 到 :max 個之間。',
'file' => ':attribute 必須在 :min 到 :max KB 之間。',
'numeric' => ':attribute 必須在 :min 到 :max 之間。',
'string' => ':attribute 必須在 :min 到 :max 個字元之間。',
],
'boolean' => ':attribute 必須是布林值。',
'can' => ':attribute 包含未授權的值。',
'confirmed' => ':attribute 兩次輸入不一致。',
'contains' => ':attribute 缺少必需值。',
'current_password' => '密碼不正確。',
'date' => ':attribute 必須是有效日期。',
'date_equals' => ':attribute 必須等於 :date。',
'date_format' => ':attribute 格式必須為 :format。',
'decimal' => ':attribute 必須有 :decimal 位小數。',
'declined' => ':attribute 必須拒絕。',
'declined_if' => '當 :other 為 :value 時 :attribute 必須拒絕。',
'different' => ':attribute 與 :other 必須不同。',
'digits' => ':attribute 必須是 :digits 位數字。',
'digits_between' => ':attribute 必須在 :min 到 :max 位數字之間。',
'dimensions' => ':attribute 圖片尺寸無效。',
'distinct' => ':attribute 存在重複值。',
'doesnt_contain' => ':attribute 不能包含以下內容::values。',
'doesnt_end_with' => ':attribute 不能以下列之一結尾::values。',
'doesnt_start_with' => ':attribute 不能以下列之一開頭::values。',
'email' => ':attribute 必須是有效的電子郵件地址。',
'encoding' => ':attribute 必須使用 :encoding 編碼。',
'ends_with' => ':attribute 必須以下列之一結尾::values。',
'enum' => '所選的 :attribute 無效。',
'exists' => '所選的 :attribute 無效。',
'extensions' => ':attribute 必須具有以下副檔名之一::values。',
'file' => ':attribute 必須是檔案。',
'filled' => ':attribute 必須有值。',
'gt' => [
'array' => ':attribute 必須多於 :value 個元素。',
'file' => ':attribute 必須大於 :value KB。',
'numeric' => ':attribute 必須大於 :value。',
'string' => ':attribute 必須大於 :value 個字元。',
],
'gte' => [
'array' => ':attribute 必須不少於 :value 個元素。',
'file' => ':attribute 必須大於等於 :value KB。',
'numeric' => ':attribute 必須大於等於 :value。',
'string' => ':attribute 必須大於等於 :value 個字元。',
],
'hex_color' => ':attribute 必須是有效的十六進位顏色值。',
'image' => ':attribute 必須是圖片。',
'in' => '所選的 :attribute 無效。',
'in_array' => ':attribute 必須存在於 :other 中。',
'in_array_keys' => ':attribute 必須包含以下鍵之一::values。',
'integer' => ':attribute 必須是整數。',
'ip' => ':attribute 必須是有效的 IP 位址。',
'ipv4' => ':attribute 必須是有效的 IPv4 位址。',
'ipv6' => ':attribute 必須是有效的 IPv6 位址。',
'json' => ':attribute 必須是有效的 JSON 字串。',
'list' => ':attribute 必須是列表。',
'lowercase' => ':attribute 必須是小寫。',
'lt' => [
'array' => ':attribute 必須少於 :value 個元素。',
'file' => ':attribute 必須小於 :value KB。',
'numeric' => ':attribute 必須小於 :value。',
'string' => ':attribute 必須小於 :value 個字元。',
],
'lte' => [
'array' => ':attribute 不能多於 :value 個元素。',
'file' => ':attribute 必須小於等於 :value KB。',
'numeric' => ':attribute 必須小於等於 :value。',
'string' => ':attribute 必須小於等於 :value 個字元。',
],
'mac_address' => ':attribute 必須是有效的 MAC 位址。',
'max' => [
'array' => ':attribute 不能超過 :max 個元素。',
'file' => ':attribute 不能大於 :max KB。',
'numeric' => ':attribute 不能大於 :max。',
'string' => ':attribute 不能大於 :max 個字元。',
],
'max_digits' => ':attribute 不能超過 :max 位數字。',
'mimes' => ':attribute 必須是 :values 類型的檔案。',
'mimetypes' => ':attribute 必須是 :values 類型的檔案。',
'min' => [
'array' => ':attribute 至少有 :min 個元素。',
'file' => ':attribute 至少 :min KB。',
'numeric' => ':attribute 至少 :min。',
'string' => ':attribute 至少 :min 個字元。',
],
'min_digits' => ':attribute 至少 :min 位數字。',
'missing' => ':attribute 必須不存在。',
'missing_if' => '當 :other 為 :value 時 :attribute 必須不存在。',
'missing_unless' => '除非 :other 為 :value,否則 :attribute 必須不存在。',
'missing_with' => '當 :values 存在時 :attribute 必須不存在。',
'missing_with_all' => '當 :values 都存在時 :attribute 必須不存在。',
'multiple_of' => ':attribute 必須是 :value 的倍數。',
'not_in' => '所選的 :attribute 無效。',
'not_regex' => ':attribute 格式無效。',
'numeric' => ':attribute 必須是數字。',
'password' => [
'letters' => ':attribute 必須包含至少一個字母。',
'mixed' => ':attribute 必須包含至少一個大寫和一個小寫字母。',
'numbers' => ':attribute 必須包含至少一個數字。',
'symbols' => ':attribute 必須包含至少一個符號。',
'uncompromised' => ':attribute 已出現在資料外洩中,請更換。',
],
'present' => ':attribute 必須存在。',
'present_if' => '當 :other 為 :value 時 :attribute 必須存在。',
'present_unless' => '除非 :other 為 :value,否則 :attribute 必須存在。',
'present_with' => '當 :values 存在時 :attribute 必須存在。',
'present_with_all' => '當 :values 都存在時 :attribute 必須存在。',
'prohibited' => ':attribute 被禁止。',
'prohibited_if' => '當 :other 為 :value 時 :attribute 被禁止。',
'prohibited_if_accepted' => '當 :other 被接受時 :attribute 被禁止。',
'prohibited_if_declined' => '當 :other 被拒絕時 :attribute 被禁止。',
'prohibited_unless' => '除非 :other 在 :values 中,否則 :attribute 被禁止。',
'prohibits' => ':attribute 禁止 :other 出現。',
'regex' => ':attribute 格式無效。',
'required' => ':attribute 為必填項。',
'required_array_keys' => ':attribute 必須包含以下鍵::values。',
'required_if' => '當 :other 為 :value 時 :attribute 為必填項。',
'required_if_accepted' => '當 :other 被接受時 :attribute 為必填項。',
'required_if_declined' => '當 :other 被拒絕時 :attribute 為必填項。',
'required_unless' => '除非 :other 在 :values 中,否則 :attribute 為必填項。',
'required_with' => '當 :values 存在時 :attribute 為必填項。',
'required_with_all' => '當 :values 都存在時 :attribute 為必填項。',
'required_without' => '當 :values 不存在時 :attribute 為必填項。',
'required_without_all' => '當 :values 都不存在時 :attribute 為必填項。',
'same' => ':attribute 與 :other 必須相同。',
'size' => [
'array' => ':attribute 必須包含 :size 個元素。',
'file' => ':attribute 必須為 :size KB。',
'numeric' => ':attribute 必須為 :size。',
'string' => ':attribute 必須為 :size 個字元。',
],
'starts_with' => ':attribute 必須以下列之一開頭::values。',
'string' => ':attribute 必須是字串。',
'timezone' => ':attribute 必須是有效的時區。',
'unique' => ':attribute 已存在。',
'uploaded' => ':attribute 上傳失敗。',
'uppercase' => ':attribute 必須是大寫。',
'url' => ':attribute 必須是有效的 URL。',
'ulid' => ':attribute 必須是有效的 ULID。',
'uuid' => ':attribute 必須是有效的 UUID。',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
'attributes' => [],
];
+18
View File
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Annotation;
use Attribute;
#[Attribute(Attribute::TARGET_PARAMETER)]
class Param
{
public function __construct(
public string|array $rules = '',
public array $messages = [],
public string $attribute = '',
public string|array|null $in = null
) {
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Annotation;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Validate
{
public function __construct(
public array $rules = [],
public array $messages = [],
public array $attributes = [],
public ?string $validator = null,
public ?string $scene = null,
public string|array|null $in = null
) {
}
}
@@ -0,0 +1,710 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Webman\Validation\Command\ValidatorGenerator\Illuminate\IlluminateConnectionResolver;
use Webman\Validation\Command\ValidatorGenerator\Rules\DefaultRuleInferrer;
use Webman\Validation\Command\ValidatorGenerator\Support\ExcludedColumns;
use Webman\Validation\Command\ValidatorGenerator\Support\OrmDetector;
use Webman\Validation\Command\ValidatorGenerator\Support\SchemaIntrospectorFactory;
use Webman\Validation\Command\ValidatorGenerator\ThinkOrm\ThinkOrmConnectionResolver;
use Webman\Validation\Command\ValidatorGenerator\Support\ValidatorClassRenderer;
use Webman\Validation\Command\ValidatorGenerator\Support\ValidatorFileWriter;
use Webman\Validation\Command\Messages;
#[AsCommand('make:validator', 'Make validation validator class.')]
final class MakeValidatorCommand extends Command
{
protected function configure(): void
{
$this->setDescription($this->selectByLocale(Messages::getDescription()));
$this->addArgument(
'name',
InputArgument::REQUIRED,
$this->selectByLocale(Messages::getArgumentName())
);
$this->addOption(
'plugin',
'p',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionPlugin())
);
$this->addOption(
'path',
'P',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionPath())
);
$this->addOption(
'force',
'f',
InputOption::VALUE_NONE,
$this->selectByLocale(Messages::getOptionForce())
);
$this->addOption(
'table',
't',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionTable())
);
$this->addOption(
'database',
'd',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionDatabase())
);
$this->addOption(
'scenes',
's',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionScenes())
);
$this->addOption(
'orm',
'o',
InputOption::VALUE_REQUIRED,
$this->selectByLocale(Messages::getOptionOrm())
);
$this->setHelp($this->buildHelpText());
$this->addUsage('UserValidator');
$this->addUsage('admin/UserValidator');
$this->addUsage('UserValidator -p admin');
$this->addUsage('UserValidator -P plugin/admin/app/validation');
$this->addUsage('UserValidator -t users -d default');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$rawName = (string)$input->getArgument('name');
$name = $this->nameToClass($rawName);
$name = str_replace('\\', '/', $name);
$name = trim($name, '/');
$plugin = $this->normalizeOptionValue($input->getOption('plugin'));
$path = $this->normalizeOptionValue($input->getOption('path'));
$force = (bool)$input->getOption('force');
$table = $input->getOption('table');
$table = is_string($table) ? trim($table) : '';
// Some Symfony Console versions parse `-t=foo` as `=foo` for short options.
$table = ltrim($table, '=');
$databaseOptionRaw = $input->getOption('database');
$databaseOptionRaw = is_string($databaseOptionRaw) ? trim($databaseOptionRaw) : '';
// Some Symfony Console versions parse `-d=foo` as `=foo` for short options.
$databaseOptionRaw = ltrim($databaseOptionRaw, '=');
$connectionName = $databaseOptionRaw !== '' ? $databaseOptionRaw : null;
$scenesOption = $input->getOption('scenes');
$scenesOption = is_string($scenesOption) ? trim($scenesOption) : '';
// Some Symfony Console versions parse `-s=crud` as `=crud` for short options.
$scenesOption = ltrim($scenesOption, '=');
$ormOption = $input->getOption('orm');
$ormOption = is_string($ormOption) ? trim($ormOption) : OrmDetector::ORM_AUTO;
// Some Symfony Console versions parse `-o=xxx` as `=xxx` for short options.
$ormOption = ltrim($ormOption, '=');
if ($ormOption === '') {
$ormOption = OrmDetector::ORM_AUTO;
}
if ($name === '') {
$output->writeln($this->msg('invalid_name_empty'));
return self::FAILURE;
}
if ($plugin && (str_contains($plugin, '/') || str_contains($plugin, '\\'))) {
$output->writeln($this->msg('invalid_plugin', ['{plugin}' => $plugin]));
return self::FAILURE;
}
if ($plugin && !$this->pluginExists($plugin)) {
$output->writeln($this->msg('plugin_not_found', ['{plugin}' => $plugin]));
return self::FAILURE;
}
try {
if ($plugin || $path) {
$resolved = $this->resolveTargetByPluginOrPath(
$name,
$plugin,
$path,
$output,
fn(string $p): string => $this->getPluginValidationRelativePath($p),
fn(string $key, array $replace = []): string => $this->msg($key, $replace)
);
if ($resolved === null) {
return self::FAILURE;
}
[$class, $namespace, $file] = $resolved;
} else {
[$namespace, $class, $file] = $this->resolveAppValidationTarget($name);
}
} catch (\Throwable $e) {
$output->writeln($this->msg('invalid_name', ['{name}' => $rawName]));
$output->writeln($this->msg('reason', ['{reason}' => $e->getMessage()]));
return self::FAILURE;
}
if (is_file($file)) {
// Ask for confirmation when overwriting in interactive mode.
// If the environment is non-interactive, do not block on prompting.
if ($input->isInteractive()) {
$helper = $this->getHelper('question');
$relative = $this->toRelativePath($file);
$prompt = $this->msg('override_prompt', ['{path}' => $relative]);
$question = new ConfirmationQuestion($prompt, true);
if (!$helper->ask($input, $output, $question)) {
return self::SUCCESS;
}
} elseif (!$force) {
// Non-interactive mode and no --force: refuse to overwrite.
$output->writeln($this->msg('file_exists', ['{path}' => $this->toRelativePath($file)]));
$output->writeln($this->msg('use_force'));
return self::FAILURE;
}
}
$rules = [];
$messages = [];
$attributes = [];
$scenes = [];
if ($scenesOption !== '' && $table === '') {
$output->writeln($this->msg('scenes_requires_table'));
return self::FAILURE;
}
if ($table !== '') {
try {
$detector = new OrmDetector();
$orm = $detector->resolve($ormOption);
if (!in_array($orm, [OrmDetector::ORM_LARAVEL, OrmDetector::ORM_THINKORM], true)) {
$output->writeln($this->msg('unsupported_orm', ['{orm}' => (string)$orm]));
return self::FAILURE;
}
$resolver = $orm === OrmDetector::ORM_THINKORM
? new ThinkOrmConnectionResolver()
: new IlluminateConnectionResolver();
$resolvedConnectionName = $this->resolveDatabaseConnectionNameForTable(
$plugin,
$connectionName,
$databaseOptionRaw,
$orm,
$output
);
if ($resolvedConnectionName === null) {
return self::FAILURE;
}
$connection = $resolver->resolve($resolvedConnectionName);
$factory = new SchemaIntrospectorFactory();
$introspector = $factory->createForDriver($connection->driverName());
$tableDef = $introspector->introspect($connection, $table);
$excludeColumns = match ($orm) {
OrmDetector::ORM_THINKORM => ExcludedColumns::defaultForThinkOrm(),
default => ExcludedColumns::defaultForIlluminate(),
};
$inferrer = new DefaultRuleInferrer();
$result = $inferrer->infer($tableDef, [
'exclude_columns' => $excludeColumns,
'with_scenes' => $scenesOption !== '',
'scenes' => $scenesOption,
]);
$rules = $result['rules'] ?? [];
$attributes = $result['attributes'] ?? [];
$scenes = $result['scenes'] ?? [];
if ($rules === []) {
$output->writeln($this->msg('no_rules_from_table', ['{table}' => $table]));
return self::FAILURE;
}
} catch (\Throwable $e) {
$output->writeln($this->msg('failed_generate_from_table', ['{table}' => $table]));
$output->writeln($this->msg('reason', ['{reason}' => $e->getMessage()]));
return self::FAILURE;
}
}
$renderer = new ValidatorClassRenderer();
$content = $renderer->render($namespace, $class, $rules, $messages, $attributes, $scenes);
try {
(new ValidatorFileWriter())->write($file, $content);
} catch (\Throwable $e) {
$output->writeln($this->msg('failed_write_file', ['{path}' => $this->toRelativePath($file)]));
$output->writeln($this->msg('reason', ['{reason}' => $e->getMessage()]));
return self::FAILURE;
}
$output->writeln($this->msg('created', ['{path}' => $this->toRelativePath($file)]));
$output->writeln($this->msg('class', ['{class}' => $namespace . '\\' . $class]));
if ($table !== '') {
$output->writeln($this->msg('table', ['{table}' => $table]));
$output->writeln($this->msg('rules_count', ['{count}' => (string)count($rules)]));
if ($scenesOption !== '') {
$output->writeln($this->msg('scenes_count', ['{count}' => (string)count($scenes)]));
}
}
return self::SUCCESS;
}
/**
* @return array{0:string,1:string,2:string} [namespace, class, file]
*/
private function resolveAppValidationTarget(string $name): array
{
$name = trim($name);
if ($name === '') {
throw new \InvalidArgumentException($this->plain('invalid_name_empty_plain'));
}
// Normalize separators for Windows/Unix inputs.
$normalized = str_replace('\\', '/', $name);
$normalized = trim($normalized, '/');
$segments = array_values(array_filter(explode('/', $normalized), static fn (string $s): bool => $s !== ''));
if ($segments === []) {
throw new \InvalidArgumentException($this->plain('invalid_name_empty_plain'));
}
$classSegment = array_pop($segments);
// Convert to PSR-friendly StudlyCase for both directory segments and class name.
$dirSegments = array_map([$this, 'toStudly'], $segments);
$class = $this->toStudly($classSegment);
$namespace = 'app\\validation';
if ($dirSegments !== []) {
$namespace .= '\\' . implode('\\', $dirSegments);
}
$validationDirName = $this->guessPath(app_path(), 'validation') ?: 'validation';
$baseDir = app_path() . DIRECTORY_SEPARATOR . $validationDirName;
$dir = $dirSegments === []
? $baseDir
: ($baseDir . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $dirSegments));
$file = $dir . DIRECTORY_SEPARATOR . $class . '.php';
return [$namespace, $class, $file];
}
private function toStudly(string $name): string
{
$name = trim($name);
if ($name === '') {
throw new \InvalidArgumentException($this->plain('invalid_segment_empty_plain'));
}
$studly = $this->nameToClass($name);
if (str_contains($studly, '/')) {
// Should never happen because we pass a single segment.
$studly = basename(str_replace('/', DIRECTORY_SEPARATOR, $studly));
}
if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $studly)) {
throw new \InvalidArgumentException($this->plain('invalid_segment_plain', ['{name}' => $name]));
}
return $studly;
}
// Rendering moved to ValidatorGenerator\Support\ValidatorClassRenderer
/**
* @param string $plugin
* @return string relative path
*/
private function getPluginValidationRelativePath(string $plugin): string
{
$plugin = trim($plugin);
$appDir = base_path('plugin' . DIRECTORY_SEPARATOR . $plugin . DIRECTORY_SEPARATOR . 'app');
$validationDir = $this->guessPath($appDir, 'validation') ?: 'validation';
return $this->normalizeRelativePath("plugin/{$plugin}/app/{$validationDir}");
}
/**
* CLI messages (bilingual).
*
* @param string $key
* @param array $replace
* @return string
*/
private function msg(string $key, array $replace = []): string
{
$map = $this->selectMessageMap(Messages::getCliMessages());
$text = $map[$key] ?? $key;
return $replace ? strtr($text, $replace) : $text;
}
/**
* Plain (no console tags) bilingual messages for exception messages.
*
* @param string $key
* @param array $replace
* @return string
*/
private function plain(string $key, array $replace = []): string
{
$map = $this->selectMessageMap(Messages::getPlainMessages());
$text = $map[$key] ?? $key;
return $replace ? strtr($text, $replace) : $text;
}
/**
* Command help text (bilingual).
*
* @return string
*/
private function buildHelpText(): string
{
return $this->selectByLocale(Messages::getHelpText());
}
private function getLocale(): string
{
$locale = 'en';
if (function_exists('config')) {
$value = config('translation.locale', 'en');
$value = is_string($value) ? trim($value) : '';
if ($value !== '') {
$locale = $value;
}
}
return $locale;
}
/**
* @param array<string, string> $localeToValue
*/
private function selectByLocale(array $localeToValue): string
{
$locale = $this->getLocale();
if (isset($localeToValue[$locale])) {
return $localeToValue[$locale];
}
$lang = explode('_', $locale)[0] ?? '';
if ($lang !== '' && isset($localeToValue[$lang])) {
return $localeToValue[$lang];
}
if (isset($localeToValue['en'])) {
return $localeToValue['en'];
}
if (isset($localeToValue['zh_CN'])) {
return $localeToValue['zh_CN'];
}
$first = reset($localeToValue);
return is_string($first) ? $first : '';
}
/**
* @param array<string, array<string, string>> $localeToMessages
* @return array<string, string>
*/
private function selectMessageMap(array $localeToMessages): array
{
$locale = $this->getLocale();
if (isset($localeToMessages[$locale])) {
return $localeToMessages[$locale];
}
$lang = explode('_', $locale)[0] ?? '';
if ($lang !== '' && isset($localeToMessages[$lang])) {
return $localeToMessages[$lang];
}
if (isset($localeToMessages['en'])) {
return $localeToMessages['en'];
}
if (isset($localeToMessages['zh_CN'])) {
return $localeToMessages['zh_CN'];
}
$first = reset($localeToMessages);
return is_array($first) ? $first : [];
}
private function normalizeOptionValue(mixed $value): ?string
{
if ($value === null) {
return null;
}
$value = trim((string)$value);
$value = ltrim($value, '=');
return $value === '' ? null : $value;
}
private function normalizeRelativePath(string $path): string
{
$path = trim($path);
$path = str_replace('\\', '/', $path);
$path = preg_replace('#^\\./+#', '', $path);
$path = trim($path, '/');
return $path;
}
private function isAbsolutePath(string $path): bool
{
$path = trim($path);
if ($path === '') {
return false;
}
if (preg_match('/^[a-zA-Z]:[\\\\\\/]/', $path)) {
return true;
}
if (str_starts_with($path, '\\\\') || str_starts_with($path, '//')) {
return true;
}
return str_starts_with($path, '/') || str_starts_with($path, '\\');
}
private function pathsEqual(string $a, string $b): bool
{
$a = strtolower($this->normalizeRelativePath($a));
$b = strtolower($this->normalizeRelativePath($b));
return $a === $b;
}
private function pluginExists(string $plugin): bool
{
if (!function_exists('config')) {
return false;
}
$cfg = config("plugin.$plugin");
return !empty($cfg);
}
private function resolveDatabaseConnectionNameForTable(
?string $plugin,
?string $explicitConnection,
string $explicitConnectionRaw,
string $orm,
OutputInterface $output
): ?string {
if (!function_exists('config')) {
throw new \RuntimeException($this->plain('config_not_available'));
}
if ($orm === OrmDetector::ORM_THINKORM) {
$main = config('think-orm');
if (!is_array($main) || $main === []) {
$alt = config('thinkorm');
$main = is_array($alt) ? $alt : [];
}
$mainConnections = $main['connections'] ?? null;
$mainConnections = is_array($mainConnections) ? $mainConnections : [];
$mainDefault = $main['default'] ?? null;
$mainDefault = is_string($mainDefault) ? trim($mainDefault) : '';
$usePlugin = false;
$connections = $mainConnections;
$defaultConnection = $mainDefault;
if ($plugin) {
$pluginCfg = config("plugin.$plugin.thinkorm");
if (!is_array($pluginCfg) || $pluginCfg === []) {
$alt = config("plugin.$plugin.think-orm");
$pluginCfg = is_array($alt) ? $alt : [];
}
$pluginConnections = $pluginCfg['connections'] ?? null;
if (is_array($pluginConnections) && $pluginConnections !== []) {
$usePlugin = true;
$connections = $pluginConnections;
$pluginDefault = config("plugin.$plugin.thinkorm.default");
if (!is_string($pluginDefault) || trim($pluginDefault) === '') {
$pluginDefault = config("plugin.$plugin.think-orm.default");
}
$defaultConnection = is_string($pluginDefault) ? trim($pluginDefault) : '';
}
}
$name = $explicitConnection !== null ? trim($explicitConnection) : '';
if ($name === '') {
$name = trim((string)$defaultConnection);
}
if ($name === '') {
throw new \RuntimeException($this->plain('database_connection_not_provided'));
}
if (!array_key_exists($name, $connections)) {
if ($explicitConnection !== null && $explicitConnectionRaw !== '') {
$output->writeln($this->msg('database_connection_not_found', ['{connection}' => $explicitConnectionRaw]));
return null;
}
$available = implode(', ', array_keys($connections));
throw new \RuntimeException($this->plain('thinkorm_connection_not_found', ['{name}' => $name, '{available}' => $available]));
}
return ($usePlugin && $plugin) ? "plugin.$plugin.$name" : $name;
}
$dbConfig = config('database', []);
if (!is_array($dbConfig)) {
throw new \RuntimeException($this->plain('database_config_invalid'));
}
$mainConnections = $dbConfig['connections'] ?? null;
$mainConnections = is_array($mainConnections) ? $mainConnections : [];
$mainDefault = $dbConfig['default'] ?? null;
$mainDefault = is_string($mainDefault) ? trim($mainDefault) : '';
$usePlugin = false;
$connections = $mainConnections;
$defaultConnection = $mainDefault;
if ($plugin) {
$pluginDb = config("plugin.$plugin.database");
if (is_array($pluginDb)) {
$pluginConnections = $pluginDb['connections'] ?? null;
if (is_array($pluginConnections) && $pluginConnections !== []) {
$usePlugin = true;
$connections = $pluginConnections;
$pluginDefault = config("plugin.$plugin.database.default");
$defaultConnection = is_string($pluginDefault) ? trim($pluginDefault) : '';
}
}
}
$name = $explicitConnection !== null ? trim($explicitConnection) : '';
if ($name === '') {
$name = trim((string)$defaultConnection);
}
if ($name === '') {
throw new \RuntimeException($this->plain('database_connection_not_provided'));
}
if (!array_key_exists($name, $connections)) {
if ($explicitConnection !== null && $explicitConnectionRaw !== '') {
$output->writeln($this->msg('database_connection_not_found', ['{connection}' => $explicitConnectionRaw]));
return null;
}
$available = implode(', ', array_keys($connections));
throw new \RuntimeException($this->plain('database_connection_not_found_available', ['{name}' => $name, '{available}' => $available]));
}
return ($usePlugin && $plugin) ? "plugin.$plugin.$name" : $name;
}
private function toRelativePath(string $path): string
{
$base = base_path();
$baseNorm = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $base), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$pathNorm = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
if (str_starts_with(strtolower($pathNorm), strtolower($baseNorm))) {
$rel = substr($pathNorm, strlen($baseNorm));
} else {
$rel = $pathNorm;
}
return str_replace(DIRECTORY_SEPARATOR, '/', $rel);
}
/**
* @return array{0:string,1:string,2:string}|null [class, namespace, file]
*/
private function resolveTargetByPluginOrPath(
string $name,
?string $plugin,
?string $path,
OutputInterface $output,
callable $pluginDefaultPathResolver,
callable $msg
): ?array {
$pathNorm = $path ? $this->normalizeRelativePath($path) : null;
if ($pathNorm !== null && $this->isAbsolutePath($pathNorm)) {
$output->writeln($msg('invalid_path', ['{path}' => (string)$path]));
return null;
}
$expected = null;
if ($plugin) {
$expected = $pluginDefaultPathResolver($plugin);
}
if ($plugin && $pathNorm) {
$pluginPrefix = 'plugin/' . $plugin . '/';
if (!str_starts_with(strtolower($pathNorm), strtolower($pluginPrefix))) {
$output->writeln($msg('plugin_path_conflict', [
'{plugin}' => $plugin,
'{path}' => $pathNorm,
]));
return null;
}
}
$targetRel = $pathNorm ?: $expected;
if (!$targetRel) {
return null;
}
$targetDir = base_path($targetRel);
$namespaceRoot = trim(str_replace('/', '\\', $targetRel), '\\');
if (!($pos = strrpos($name, '/'))) {
$class = ucfirst($name);
$subPath = '';
} else {
$subPath = substr($name, 0, $pos);
$class = ucfirst(substr($name, $pos + 1));
}
$subDir = $subPath ? str_replace('/', DIRECTORY_SEPARATOR, $subPath) . DIRECTORY_SEPARATOR : '';
$file = $targetDir . DIRECTORY_SEPARATOR . $subDir . $class . '.php';
$namespace = $namespaceRoot . ($subPath ? '\\' . str_replace('/', '\\', $subPath) : '');
return [$class, $namespace, $file];
}
private function nameToClass(string $class): string
{
$class = preg_replace_callback(['/-([a-zA-Z])/', '/_([a-zA-Z])/'], function ($matches) {
return strtoupper($matches[1]);
}, $class);
if (!($pos = strrpos($class, '/'))) {
$class = ucfirst($class);
} else {
$path = substr($class, 0, $pos);
$class = ucfirst(substr($class, $pos + 1));
$class = "$path/$class";
}
return $class;
}
private function guessPath(string $basePath, string $name, bool $returnFullPath = false)
{
if (!is_dir($basePath)) {
return false;
}
$names = explode('/', trim(strtolower($name), '/'));
$realname = [];
$path = $basePath;
foreach ($names as $n) {
$found = false;
foreach (scandir($path) ?: [] as $tmpName) {
if (strtolower($tmpName) === $n && is_dir($path . DIRECTORY_SEPARATOR . $tmpName)) {
$path = $path . DIRECTORY_SEPARATOR . $tmpName;
$realname[] = $tmpName;
$found = true;
break;
}
}
if (!$found) {
return false;
}
}
$realname = implode(DIRECTORY_SEPARATOR, $realname);
return $returnFullPath ? realpath($basePath . DIRECTORY_SEPARATOR . $realname) : $realname;
}
}
+936
View File
@@ -0,0 +1,936 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command;
/**
* Multi-language messages for make:validator command.
*/
final class Messages
{
/**
* @return array<string, string> locale => description
*/
public static function getDescription(): array
{
return [
'zh_CN' => '生成验证器类',
'zh_TW' => '產生驗證器類',
'en' => 'Create a validator class',
'ja' => 'バリデータークラスを作成',
'ko' => '유효성 검사 클래스 생성',
'fr' => 'Créer une classe de validation',
'de' => 'Validierungsklasse erstellen',
'es' => 'Crear clase de validación',
'pt_BR' => 'Criar classe de validação',
'ru' => 'Создать класс валидации',
'vi' => 'Tạo lớp xác thực',
'tr' => 'Doğrulama sınıfı oluştur',
'id' => 'Buat kelas validasi',
'th' => 'สร้างคลาสตรวจสอบความถูกต้อง',
];
}
/**
* @return array<string, string> locale => argument description
*/
public static function getArgumentName(): array
{
return [
'zh_CN' => '验证器类名(例如:UserValidator、admin/UserValidator',
'zh_TW' => '驗證器類名(例如:UserValidator、admin/UserValidator',
'en' => 'Validator class name (e.g. UserValidator, admin/UserValidator)',
'ja' => 'バリデータークラス名(例:UserValidator、admin/UserValidator',
'ko' => '유효성 검사 클래스 이름 (예: UserValidator, admin/UserValidator)',
'fr' => 'Nom de la classe de validation (ex. UserValidator, admin/UserValidator)',
'de' => 'Name der Validierungsklasse (z. B. UserValidator, admin/UserValidator)',
'es' => 'Nombre de la clase de validación (ej. UserValidator, admin/UserValidator)',
'pt_BR' => 'Nome da classe de validação (ex.: UserValidator, admin/UserValidator)',
'ru' => 'Имя класса валидации (напр. UserValidator, admin/UserValidator)',
'vi' => 'Tên lớp xác thực (vd: UserValidator, admin/UserValidator)',
'tr' => 'Doğrulayıcı sınıf adı (örn. UserValidator, admin/UserValidator)',
'id' => 'Nama kelas validasi (mis. UserValidator, admin/UserValidator)',
'th' => 'ชื่อคลาสตรวจสอบ (เช่น UserValidator, admin/UserValidator)',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionPlugin(): array
{
return [
'zh_CN' => '插件名(plugin/ 下的目录名),例如:admin',
'zh_TW' => '外掛名稱(plugin/ 下的目錄名),例如:admin',
'en' => 'Plugin name (directory under plugin/). e.g. admin',
'ja' => 'プラグイン名(plugin/ 以下のディレクトリ名)。例:admin',
'ko' => '플러그인 이름 (plugin/ 하위 디렉터리). 예: admin',
'fr' => 'Nom du plugin (répertoire sous plugin/). Ex. : admin',
'de' => 'Plugin-Name (Unterverzeichnis von plugin/). z. B. admin',
'es' => 'Nombre del plugin (directorio bajo plugin/). Ej.: admin',
'pt_BR' => 'Nome do plugin (diretório em plugin/). Ex.: admin',
'ru' => 'Имя плагина (каталог в plugin/). Напр.: admin',
'vi' => 'Tên plugin (thư mục trong plugin/). VD: admin',
'tr' => 'Eklenti adı (plugin/ altındaki dizin). Örn.: admin',
'id' => 'Nama plugin (direktori di bawah plugin/). Mis.: admin',
'th' => 'ชื่อปลั๊กอิน (โฟลเดอร์ใต้ plugin/) เช่น admin',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionPath(): array
{
return [
'zh_CN' => '目标目录(相对项目根目录),例如:plugin/admin/app/validation',
'zh_TW' => '目標目錄(相對專案根目錄),例如:plugin/admin/app/validation',
'en' => 'Target directory (relative to project root). e.g. plugin/admin/app/validation',
'ja' => '出力先ディレクトリ(プロジェクトルートからの相対パス)。例:plugin/admin/app/validation',
'ko' => '대상 디렉터리 (프로젝트 루트 기준 상대 경로). 예: plugin/admin/app/validation',
'fr' => 'Répertoire cible (relatif à la racine du projet). Ex. : plugin/admin/app/validation',
'de' => 'Zielverzeichnis (relativ zum Projektstamm). z. B. plugin/admin/app/validation',
'es' => 'Directorio destino (relativo a la raíz del proyecto). Ej.: plugin/admin/app/validation',
'pt_BR' => 'Diretório de destino (relativo à raiz do projeto). Ex.: plugin/admin/app/validation',
'ru' => 'Целевой каталог (относительно корня проекта). Напр.: plugin/admin/app/validation',
'vi' => 'Thư mục đích (tương đối so với thư mục gốc dự án). VD: plugin/admin/app/validation',
'tr' => 'Hedef dizin (proje köküne göre). Örn.: plugin/admin/app/validation',
'id' => 'Direktori tujuan (relatif ke root proyek). Mis.: plugin/admin/app/validation',
'th' => 'โฟลเดอร์ปลายทาง (สัมพันธ์กับรากโปรเจกต์) เช่น plugin/admin/app/validation',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionForce(): array
{
return [
'zh_CN' => '文件已存在时强制覆盖',
'zh_TW' => '檔案已存在時強制覆蓋',
'en' => 'Overwrite if file already exists',
'ja' => 'ファイルが既に存在する場合に上書き',
'ko' => '파일이 이미 있으면 덮어쓰기',
'fr' => 'Écraser si le fichier existe déjà',
'de' => 'Überschreiben, wenn die Datei bereits existiert',
'es' => 'Sobrescribir si el archivo ya existe',
'pt_BR' => 'Sobrescrever se o arquivo já existir',
'ru' => 'Перезаписать, если файл уже существует',
'vi' => 'Ghi đè nếu tệp đã tồn tại',
'tr' => 'Dosya zaten varsa üzerine yaz',
'id' => 'Timpa jika berkas sudah ada',
'th' => 'เขียนทับถ้ามีไฟล์อยู่แล้ว',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionTable(): array
{
return [
'zh_CN' => '从数据库表推断并生成规则(例如:users)',
'zh_TW' => '從資料庫資料表推斷並產生規則(例如:users)',
'en' => 'Generate rules from database table (e.g. users)',
'ja' => 'データベーステーブルからルールを推論して生成(例:users)',
'ko' => '데이터베이스 테이블에서 규칙 생성 (예: users)',
'fr' => 'Générer les règles à partir d\'une table (ex. : users)',
'de' => 'Regeln aus Datenbanktabelle ableiten (z. B. users)',
'es' => 'Generar reglas desde la tabla de base de datos (ej.: users)',
'pt_BR' => 'Gerar regras a partir da tabela do banco (ex.: users)',
'ru' => 'Сформировать правила по таблице БД (напр. users)',
'vi' => 'Sinh quy tắc từ bảng cơ sở dữ liệu (vd: users)',
'tr' => 'Veritabanı tablosundan kurallar üret (örn.: users)',
'id' => 'Hasilkan aturan dari tabel database (mis. users)',
'th' => 'สร้างกฎจากตารางฐานข้อมูล (เช่น users)',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionDatabase(): array
{
return [
'zh_CN' => '数据库连接名',
'zh_TW' => '資料庫連線名稱',
'en' => 'Database connection name',
'ja' => 'データベース接続名',
'ko' => '데이터베이스 연결 이름',
'fr' => 'Nom de la connexion à la base de données',
'de' => 'Datenbankverbindungsname',
'es' => 'Nombre de la conexión a la base de datos',
'pt_BR' => 'Nome da conexão do banco de dados',
'ru' => 'Имя подключения к БД',
'vi' => 'Tên kết nối cơ sở dữ liệu',
'tr' => 'Veritabanı bağlantı adı',
'id' => 'Nama koneksi database',
'th' => 'ชื่อการเชื่อมต่อฐานข้อมูล',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionScenes(): array
{
return [
'zh_CN' => '生成场景(支持:crud',
'zh_TW' => '產生場景(支援:crud',
'en' => 'Generate scenes (supported: crud)',
'ja' => 'シーンを生成(対応:crud',
'ko' => '장면 생성 (지원: crud)',
'fr' => 'Générer des scènes (supporté : crud)',
'de' => 'Szenen erzeugen (unterstützt: crud)',
'es' => 'Generar escenas (soportado: crud)',
'pt_BR' => 'Gerar cenas (suportado: crud)',
'ru' => 'Создать сцены (поддержка: crud)',
'vi' => 'Tạo cảnh (hỗ trợ: crud)',
'tr' => 'Sahneleri oluştur (desteklenen: crud)',
'id' => 'Hasilkan adegan (didukung: crud)',
'th' => 'สร้างฉาก (รองรับ: crud)',
];
}
/**
* @return array<string, string> locale => option description
*/
public static function getOptionOrm(): array
{
return [
'zh_CN' => '使用的 ORMauto|laravel|thinkorm(默认:auto',
'zh_TW' => '使用的 ORMauto|laravel|thinkorm(預設:auto',
'en' => 'ORM to use: auto|laravel|thinkorm (default: auto)',
'ja' => '使用する ORMauto|laravel|thinkorm(既定:auto',
'ko' => '사용할 ORM: auto|laravel|thinkorm (기본: auto)',
'fr' => 'ORM à utiliser : auto|laravel|thinkorm (défaut : auto)',
'de' => 'Zu verwendende ORM: auto|laravel|thinkorm (Standard: auto)',
'es' => 'ORM a usar: auto|laravel|thinkorm (por defecto: auto)',
'pt_BR' => 'ORM a usar: auto|laravel|thinkorm (padrão: auto)',
'ru' => 'ORM: auto|laravel|thinkorm (по умолчанию: auto)',
'vi' => 'ORM sử dụng: auto|laravel|thinkorm (mặc định: auto)',
'tr' => 'Kullanılacak ORM: auto|laravel|thinkorm (varsayılan: auto)',
'id' => 'ORM yang digunakan: auto|laravel|thinkorm (baku: auto)',
'th' => 'ORM ที่ใช้: auto|laravel|thinkorm (ค่าเริ่มต้น: auto)',
];
}
/**
* CLI messages (with Symfony console tags). locale => [ key => text ]
*
* @return array<string, array<string, string>>
*/
public static function getCliMessages(): array
{
return [
'zh_CN' => [
'invalid_name_empty' => '<error>验证器类名不能为空。</error>',
'invalid_name' => '<error>验证器类名无效:{name}</error>',
'invalid_plugin' => '<error>插件名无效:{plugin}。`--plugin/-p` 只能是 plugin/ 目录下的目录名,不能包含 / 或 \\。</error>',
'plugin_not_found' => "<error>插件不存在:</error> <comment>{plugin}</comment>\n请检查插件名是否输入正确,或确认插件已正确安装/启用。",
'plugin_path_conflict' => "<error>`--path/-P` 指定的路径不在 plugin/{plugin}/ 目录下。\n同时使用 `--plugin/-p` 时,`--path/-P` 必须是 plugin/{plugin}/ 下的路径。</error>",
'invalid_path' => '<error>路径无效:{path}。`--path/-P` 必须是相对路径(相对于项目根目录),不能是绝对路径。</error>',
'file_exists' => '<error>文件已存在:</error> {path}',
'override_prompt' => "<question>文件已存在:{path}</question>\n<question>是否覆盖?[Y/n](回车=Y</question>\n",
'use_force' => '使用 <comment>--force/-f</comment> 强制覆盖。',
'scenes_requires_table' => '<error>选项 --scenes 需要同时指定 --table。</error>',
'unsupported_orm' => '<error>不支持的 ORM{orm}(支持:auto/laravel/thinkorm)。</error>',
'database_connection_not_found' => '<error>数据库连接不存在:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>无法从数据表推断出规则:</error> {table}',
'failed_generate_from_table' => '<error>从数据表生成验证器失败:</error> {table}',
'failed_write_file' => '<error>写入文件失败:</error> {path}',
'reason' => '<comment>原因:</comment> {reason}',
'created' => '<info>已创建:</info> {path}',
'class' => '<info>类:</info> {class}',
'table' => '<info>数据表:</info> {table}',
'rules_count' => '<info>规则数:</info> {count}',
'scenes_count' => '<info>场景数:</info> {count}',
],
'zh_TW' => [
'invalid_name_empty' => '<error>驗證器類名不能為空。</error>',
'invalid_name' => '<error>驗證器類名無效:{name}</error>',
'invalid_plugin' => '<error>外掛名稱無效:{plugin}。`--plugin/-p` 只能是 plugin/ 目錄下的目錄名,不能包含 / 或 \\。</error>',
'plugin_not_found' => "<error>外掛不存在:</error> <comment>{plugin}</comment>\n請檢查外掛名稱是否正確,或確認外掛已正確安裝/啟用。",
'plugin_path_conflict' => "<error>`--path/-P` 指定的路徑不在 plugin/{plugin}/ 目錄下。\n同時使用 `--plugin/-p` 時,`--path/-P` 必須是 plugin/{plugin}/ 下的路徑。</error>",
'invalid_path' => '<error>路徑無效:{path}。`--path/-P` 必須是相對路徑(相對於專案根目錄),不能是絕對路徑。</error>',
'file_exists' => '<error>檔案已存在:</error> {path}',
'override_prompt' => "<question>檔案已存在:{path}</question>\n<question>是否覆蓋?[Y/n]Enter=Y</question>\n",
'use_force' => '使用 <comment>--force/-f</comment> 強制覆蓋。',
'scenes_requires_table' => '<error>選項 --scenes 需要同時指定 --table。</error>',
'unsupported_orm' => '<error>不支援的 ORM{orm}(支援:auto/laravel/thinkorm)。</error>',
'database_connection_not_found' => '<error>資料庫連線不存在:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>無法從資料表推斷出規則:</error> {table}',
'failed_generate_from_table' => '<error>從資料表產生驗證器失敗:</error> {table}',
'failed_write_file' => '<error>寫入檔案失敗:</error> {path}',
'reason' => '<comment>原因:</comment> {reason}',
'created' => '<info>已建立:</info> {path}',
'class' => '<info>類別:</info> {class}',
'table' => '<info>資料表:</info> {table}',
'rules_count' => '<info>規則數:</info> {count}',
'scenes_count' => '<info>場景數:</info> {count}',
],
'en' => [
'invalid_name_empty' => '<error>Validator name cannot be empty.</error>',
'invalid_name' => '<error>Invalid validator name: {name}</error>',
'invalid_plugin' => '<error>Invalid plugin name: {plugin}. `--plugin/-p` must be a directory name under plugin/ and must not contain / or \\.</error>',
'plugin_not_found' => "<error>Plugin not found:</error> <comment>{plugin}</comment>\nPlease check the plugin name, or ensure the plugin is properly installed/enabled.",
'plugin_path_conflict' => "<error>`--path/-P` is not under plugin/{plugin}/.\nWhen `--plugin/-p` is specified, `--path/-P` must be under plugin/{plugin}/.</error>",
'invalid_path' => '<error>Invalid path: {path}. `--path/-P` must be a relative path (to project root) and must not be an absolute path.</error>',
'file_exists' => '<error>File already exists:</error> {path}',
'override_prompt' => "<question>File already exists: {path}</question>\n<question>Overwrite? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Use <comment>--force/-f</comment> to overwrite.',
'scenes_requires_table' => '<error>Option --scenes requires --table.</error>',
'unsupported_orm' => '<error>Unsupported ORM: {orm} (supported: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Database connection not found:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>No rules inferred from table:</error> {table}',
'failed_generate_from_table' => '<error>Failed to generate validator from table:</error> {table}',
'failed_write_file' => '<error>Failed to write file:</error> {path}',
'reason' => '<comment>Reason:</comment> {reason}',
'created' => '<info>Created:</info> {path}',
'class' => '<info>Class:</info> {class}',
'table' => '<info>Table:</info> {table}',
'rules_count' => '<info>Rules:</info> {count}',
'scenes_count' => '<info>Scenes:</info> {count}',
],
'ja' => [
'invalid_name_empty' => '<error>バリデーター名を空にできません。</error>',
'invalid_name' => '<error>無効なバリデーター名:{name}</error>',
'invalid_plugin' => '<error>無効なプラグイン名:{plugin}。`--plugin/-p` は plugin/ 以下のディレクトリ名のみ指定でき、/ または \\ を含めません。</error>',
'plugin_not_found' => "<error>プラグインが見つかりません:</error> <comment>{plugin}</comment>\nプラグイン名を確認するか、正しくインストール/有効化されているか確認してください。",
'plugin_path_conflict' => "<error>`--path/-P` が plugin/{plugin}/ 配下にありません。\n`--plugin/-p` 指定時、`--path/-P` は plugin/{plugin}/ 配下のパスでなければなりません。</error>",
'invalid_path' => '<error>無効なパス:{path}。`--path/-P` はプロジェクトルートからの相対パスで、絶対パスは指定できません。</error>',
'file_exists' => '<error>ファイルは既に存在します:</error> {path}',
'override_prompt' => "<question>ファイルは既に存在します:{path}</question>\n<question>上書きしますか?[Y/n]Enter=Y</question>\n",
'use_force' => '<comment>--force/-f</comment> で上書きしてください。',
'scenes_requires_table' => '<error>オプション --scenes には --table の指定が必要です。</error>',
'unsupported_orm' => '<error>サポートされていない ORM{orm}(対応:auto/laravel/thinkorm)。</error>',
'database_connection_not_found' => '<error>データベース接続が見つかりません:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>テーブルからルールを推論できません:</error> {table}',
'failed_generate_from_table' => '<error>テーブルからバリデーターの生成に失敗しました:</error> {table}',
'failed_write_file' => '<error>ファイルの書き込みに失敗しました:</error> {path}',
'reason' => '<comment>理由:</comment> {reason}',
'created' => '<info>作成しました:</info> {path}',
'class' => '<info>クラス:</info> {class}',
'table' => '<info>テーブル:</info> {table}',
'rules_count' => '<info>ルール数:</info> {count}',
'scenes_count' => '<info>シーン数:</info> {count}',
],
'ko' => [
'invalid_name_empty' => '<error>유효성 검사 클래스 이름을 비워 둘 수 없습니다.</error>',
'invalid_name' => '<error>잘못된 유효성 검사 클래스 이름: {name}</error>',
'invalid_plugin' => '<error>잘못된 플러그인 이름: {plugin}. `--plugin/-p`는 plugin/ 아래의 디렉터리 이름만 가능하며 / 또는 \\를 포함할 수 없습니다.</error>',
'plugin_not_found' => "<error>플러그인을 찾을 수 없습니다:</error> <comment>{plugin}</comment>\n플러그인 이름을 확인하거나, 올바르게 설치/활성화되었는지 확인하세요.",
'plugin_path_conflict' => "<error>`--path/-P`가 plugin/{plugin}/ 아래에 없습니다.\n`--plugin/-p` 지정 시 `--path/-P`는 plugin/{plugin}/ 하위 경로여야 합니다.</error>",
'invalid_path' => '<error>잘못된 경로: {path}. `--path/-P`는 프로젝트 루트 기준 상대 경로여야 하며 절대 경로는 사용할 수 없습니다.</error>',
'file_exists' => '<error>파일이 이미 존재합니다:</error> {path}',
'override_prompt' => "<question>파일이 이미 존재합니다: {path}</question>\n<question>덮어쓰시겠습니까? [Y/n] (Enter=Y)</question>\n",
'use_force' => '<comment>--force/-f</comment>로 덮어쓰세요.',
'scenes_requires_table' => '<error>--scenes 옵션에는 --table 지정이 필요합니다.</error>',
'unsupported_orm' => '<error>지원하지 않는 ORM: {orm} (지원: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>데이터베이스 연결을 찾을 수 없습니다:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>테이블에서 규칙을 추론할 수 없습니다:</error> {table}',
'failed_generate_from_table' => '<error>테이블에서 유효성 검사 클래스 생성에 실패했습니다:</error> {table}',
'failed_write_file' => '<error>파일 쓰기에 실패했습니다:</error> {path}',
'reason' => '<comment>사유:</comment> {reason}',
'created' => '<info>생성됨:</info> {path}',
'class' => '<info>클래스:</info> {class}',
'table' => '<info>테이블:</info> {table}',
'rules_count' => '<info>규칙 수:</info> {count}',
'scenes_count' => '<info>장면 수:</info> {count}',
],
'fr' => [
'invalid_name_empty' => '<error>Le nom du validateur ne peut pas être vide.</error>',
'invalid_name' => '<error>Nom de validateur invalide : {name}</error>',
'invalid_plugin' => '<error>Nom de plugin invalide : {plugin}. `--plugin/-p` doit être un nom de répertoire sous plugin/ et ne doit pas contenir / ou \\.</error>',
'plugin_not_found' => "<error>Plugin introuvable :</error> <comment>{plugin}</comment>\nVérifiez le nom du plugin ou assurez-vous qu'il est correctement installé/activé.",
'plugin_path_conflict' => "<error>`--path/-P` n'est pas sous plugin/{plugin}/.\nAvec `--plugin/-p`, `--path/-P` doit être un chemin sous plugin/{plugin}/.</error>",
'invalid_path' => '<error>Chemin invalide : {path}. `--path/-P` doit être un chemin relatif (à la racine du projet), pas un chemin absolu.</error>',
'file_exists' => '<error>Le fichier existe déjà :</error> {path}',
'override_prompt' => "<question>Le fichier existe déjà : {path}</question>\n<question>Écraser ? [Y/n] (Entrée = Y)</question>\n",
'use_force' => 'Utilisez <comment>--force/-f</comment> pour écraser.',
'scenes_requires_table' => '<error>L\'option --scenes nécessite --table.</error>',
'unsupported_orm' => '<error>ORM non pris en charge : {orm} (pris en charge : auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Connexion à la base de données introuvable :</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Aucune règle déduite de la table :</error> {table}',
'failed_generate_from_table' => '<error>Échec de la génération du validateur à partir de la table :</error> {table}',
'failed_write_file' => '<error>Échec de l\'écriture du fichier :</error> {path}',
'reason' => '<comment>Raison :</comment> {reason}',
'created' => '<info>Créé :</info> {path}',
'class' => '<info>Classe :</info> {class}',
'table' => '<info>Table :</info> {table}',
'rules_count' => '<info>Règles :</info> {count}',
'scenes_count' => '<info>Scènes :</info> {count}',
],
'de' => [
'invalid_name_empty' => '<error>Der Name des Validators darf nicht leer sein.</error>',
'invalid_name' => '<error>Ungültiger Validator-Name: {name}</error>',
'invalid_plugin' => '<error>Ungültiger Plugin-Name: {plugin}. `--plugin/-p` muss ein Verzeichnisname unter plugin/ sein und darf / oder \\ nicht enthalten.</error>',
'plugin_not_found' => "<error>Plugin nicht gefunden:</error> <comment>{plugin}</comment>\nBitte prüfen Sie den Plugin-Namen oder stellen Sie sicher, dass das Plugin korrekt installiert/aktiviert ist.",
'plugin_path_conflict' => "<error>`--path/-P` liegt nicht unter plugin/{plugin}/.\nBei Angabe von `--plugin/-p` muss `--path/-P` unter plugin/{plugin}/ liegen.</error>",
'invalid_path' => '<error>Ungültiger Pfad: {path}. `--path/-P` muss ein relativer Pfad (zum Projektstamm) sein, kein absoluter Pfad.</error>',
'file_exists' => '<error>Datei existiert bereits:</error> {path}',
'override_prompt' => "<question>Datei existiert bereits: {path}</question>\n<question>Überschreiben? [Y/n] (Eingabe = Y)</question>\n",
'use_force' => 'Mit <comment>--force/-f</comment> überschreiben.',
'scenes_requires_table' => '<error>Option --scenes erfordert --table.</error>',
'unsupported_orm' => '<error>Nicht unterstützte ORM: {orm} (unterstützt: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Datenbankverbindung nicht gefunden:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Keine Regeln aus Tabelle abgeleitet:</error> {table}',
'failed_generate_from_table' => '<error>Validator konnte aus Tabelle nicht erzeugt werden:</error> {table}',
'failed_write_file' => '<error>Datei konnte nicht geschrieben werden:</error> {path}',
'reason' => '<comment>Grund:</comment> {reason}',
'created' => '<info>Erstellt:</info> {path}',
'class' => '<info>Klasse:</info> {class}',
'table' => '<info>Tabelle:</info> {table}',
'rules_count' => '<info>Regeln:</info> {count}',
'scenes_count' => '<info>Szenen:</info> {count}',
],
'es' => [
'invalid_name_empty' => '<error>El nombre del validador no puede estar vacío.</error>',
'invalid_name' => '<error>Nombre de validador no válido: {name}</error>',
'invalid_plugin' => '<error>Nombre de plugin no válido: {plugin}. `--plugin/-p` debe ser un nombre de directorio bajo plugin/ y no puede contener / ni \\.</error>',
'plugin_not_found' => "<error>Plugin no encontrado:</error> <comment>{plugin}</comment>\nCompruebe el nombre del plugin o asegúrese de que está correctamente instalado/habilitado.",
'plugin_path_conflict' => "<error>`--path/-P` no está bajo plugin/{plugin}/.\nAl usar `--plugin/-p`, `--path/-P` debe ser una ruta bajo plugin/{plugin}/.</error>",
'invalid_path' => '<error>Ruta no válida: {path}. `--path/-P` debe ser una ruta relativa (a la raíz del proyecto), no absoluta.</error>',
'file_exists' => '<error>El archivo ya existe:</error> {path}',
'override_prompt' => "<question>El archivo ya existe: {path}</question>\n<question>¿Sobrescribir? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Use <comment>--force/-f</comment> para sobrescribir.',
'scenes_requires_table' => '<error>La opción --scenes requiere --table.</error>',
'unsupported_orm' => '<error>ORM no soportada: {orm} (soportadas: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Conexión a la base de datos no encontrada:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>No se pudieron inferir reglas de la tabla:</error> {table}',
'failed_generate_from_table' => '<error>Error al generar el validador desde la tabla:</error> {table}',
'failed_write_file' => '<error>Error al escribir el archivo:</error> {path}',
'reason' => '<comment>Motivo:</comment> {reason}',
'created' => '<info>Creado:</info> {path}',
'class' => '<info>Clase:</info> {class}',
'table' => '<info>Tabla:</info> {table}',
'rules_count' => '<info>Reglas:</info> {count}',
'scenes_count' => '<info>Escenas:</info> {count}',
],
'pt_BR' => [
'invalid_name_empty' => '<error>O nome do validador não pode estar vazio.</error>',
'invalid_name' => '<error>Nome de validador inválido: {name}</error>',
'invalid_plugin' => '<error>Nome de plugin inválido: {plugin}. `--plugin/-p` deve ser um nome de diretório em plugin/ e não pode conter / ou \\.</error>',
'plugin_not_found' => "<error>Plugin não encontrado:</error> <comment>{plugin}</comment>\nVerifique o nome do plugin ou confira se está instalado/ativado corretamente.",
'plugin_path_conflict' => "<error>`--path/-P` não está em plugin/{plugin}/.\nAo usar `--plugin/-p`, `--path/-P` deve estar sob plugin/{plugin}/.</error>",
'invalid_path' => '<error>Caminho inválido: {path}. `--path/-P` deve ser um caminho relativo (à raiz do projeto), não absoluto.</error>',
'file_exists' => '<error>O arquivo já existe:</error> {path}',
'override_prompt' => "<question>O arquivo já existe: {path}</question>\n<question>Sobrescrever? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Use <comment>--force/-f</comment> para sobrescrever.',
'scenes_requires_table' => '<error>A opção --scenes exige --table.</error>',
'unsupported_orm' => '<error>ORM não suportada: {orm} (suportadas: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Conexão com o banco não encontrada:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Nenhuma regra inferida da tabela:</error> {table}',
'failed_generate_from_table' => '<error>Falha ao gerar validador a partir da tabela:</error> {table}',
'failed_write_file' => '<error>Falha ao escrever o arquivo:</error> {path}',
'reason' => '<comment>Motivo:</comment> {reason}',
'created' => '<info>Criado:</info> {path}',
'class' => '<info>Classe:</info> {class}',
'table' => '<info>Tabela:</info> {table}',
'rules_count' => '<info>Regras:</info> {count}',
'scenes_count' => '<info>Cenas:</info> {count}',
],
'ru' => [
'invalid_name_empty' => '<error>Имя класса валидации не может быть пустым.</error>',
'invalid_name' => '<error>Недопустимое имя валидатора: {name}</error>',
'invalid_plugin' => '<error>Недопустимое имя плагина: {plugin}. Для `--plugin/-p` допустимо только имя каталога в plugin/, без / и \\.</error>',
'plugin_not_found' => "<error>Плагин не найден:</error> <comment>{plugin}</comment>\nПроверьте имя плагина или убедитесь, что он установлен и включён.",
'plugin_path_conflict' => "<error>`--path/-P` не находится в plugin/{plugin}/.\nПри использовании `--plugin/-p` `--path/-P` должен быть путём внутри plugin/{plugin}/.</error>",
'invalid_path' => '<error>Недопустимый путь: {path}. Для `--path/-P` нужен относительный путь (от корня проекта), не абсолютный.</error>',
'file_exists' => '<error>Файл уже существует:</error> {path}',
'override_prompt' => "<question>Файл уже существует: {path}</question>\n<question>Перезаписать? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Используйте <comment>--force/-f</comment> для перезаписи.',
'scenes_requires_table' => '<error>Для опции --scenes необходимо указать --table.</error>',
'unsupported_orm' => '<error>Неподдерживаемая ORM: {orm} (поддерживаются: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Подключение к БД не найдено:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Не удалось вывести правила по таблице:</error> {table}',
'failed_generate_from_table' => '<error>Не удалось сформировать валидатор по таблице:</error> {table}',
'failed_write_file' => '<error>Не удалось записать файл:</error> {path}',
'reason' => '<comment>Причина:</comment> {reason}',
'created' => '<info>Создано:</info> {path}',
'class' => '<info>Класс:</info> {class}',
'table' => '<info>Таблица:</info> {table}',
'rules_count' => '<info>Правил:</info> {count}',
'scenes_count' => '<info>Сцен:</info> {count}',
],
'vi' => [
'invalid_name_empty' => '<error>Tên lớp xác thực không được để trống.</error>',
'invalid_name' => '<error>Tên lớp xác thực không hợp lệ: {name}</error>',
'invalid_plugin' => '<error>Tên plugin không hợp lệ: {plugin}. `--plugin/-p` phải là tên thư mục trong plugin/ và không được chứa / hoặc \\.</error>',
'plugin_not_found' => "<error>Không tìm thấy plugin:</error> <comment>{plugin}</comment>\nVui lòng kiểm tra tên plugin hoặc đảm bảo plugin đã được cài đặt/bật đúng cách.",
'plugin_path_conflict' => "<error>`--path/-P` không nằm trong plugin/{plugin}/.\nKhi dùng `--plugin/-p`, `--path/-P` phải là đường dẫn trong plugin/{plugin}/.</error>",
'invalid_path' => '<error>Đường dẫn không hợp lệ: {path}. `--path/-P` phải là đường dẫn tương đối (so với thư mục gốc dự án), không phải đường dẫn tuyệt đối.</error>',
'file_exists' => '<error>Tệp đã tồn tại:</error> {path}',
'override_prompt' => "<question>Tệp đã tồn tại: {path}</question>\n<question>Ghi đè? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Dùng <comment>--force/-f</comment> để ghi đè.',
'scenes_requires_table' => '<error>Tùy chọn --scenes yêu cầu --table.</error>',
'unsupported_orm' => '<error>ORM không được hỗ trợ: {orm} (hỗ trợ: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Không tìm thấy kết nối cơ sở dữ liệu:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Không suy ra được quy tắc từ bảng:</error> {table}',
'failed_generate_from_table' => '<error>Không thể tạo lớp xác thực từ bảng:</error> {table}',
'failed_write_file' => '<error>Không thể ghi tệp:</error> {path}',
'reason' => '<comment>Lý do:</comment> {reason}',
'created' => '<info>Đã tạo:</info> {path}',
'class' => '<info>Lớp:</info> {class}',
'table' => '<info>Bảng:</info> {table}',
'rules_count' => '<info>Số quy tắc:</info> {count}',
'scenes_count' => '<info>Số cảnh:</info> {count}',
],
'tr' => [
'invalid_name_empty' => '<error>Doğrulayıcı adı boş bırakılamaz.</error>',
'invalid_name' => '<error>Geçersiz doğrulayıcı adı: {name}</error>',
'invalid_plugin' => '<error>Geçersiz eklenti adı: {plugin}. `--plugin/-p` yalnızca plugin/ altındaki bir dizin adı olmalı, / veya \\ içeremez.</error>',
'plugin_not_found' => "<error>Eklenti bulunamadı:</error> <comment>{plugin}</comment>\nEklenti adını kontrol edin veya doğru kurulduğundan/etkinleştirildiğinden emin olun.",
'plugin_path_conflict' => "<error>`--path/-P` plugin/{plugin}/ altında değil.\n`--plugin/-p` belirtildiğinde `--path/-P` plugin/{plugin}/ altında bir yol olmalıdır.</error>",
'invalid_path' => '<error>Geçersiz yol: {path}. `--path/-P` proje köküne göre göreli yol olmalı, mutlak yol olmamalı.</error>',
'file_exists' => '<error>Dosya zaten mevcut:</error> {path}',
'override_prompt' => "<question>Dosya zaten mevcut: {path}</question>\n<question>Üzerine yazılsın mı? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Üzerine yazmak için <comment>--force/-f</comment> kullanın.',
'scenes_requires_table' => '<error>--scenes seçeneği --table gerektirir.</error>',
'unsupported_orm' => '<error>Desteklenmeyen ORM: {orm} (desteklenen: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Veritabanı bağlantısı bulunamadı:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Tablodan kural çıkarılamadı:</error> {table}',
'failed_generate_from_table' => '<error>Tablodan doğrulayıcı oluşturulamadı:</error> {table}',
'failed_write_file' => '<error>Dosya yazılamadı:</error> {path}',
'reason' => '<comment>Neden:</comment> {reason}',
'created' => '<info>Oluşturuldu:</info> {path}',
'class' => '<info>Sınıf:</info> {class}',
'table' => '<info>Tablo:</info> {table}',
'rules_count' => '<info>Kural sayısı:</info> {count}',
'scenes_count' => '<info>Sahne sayısı:</info> {count}',
],
'id' => [
'invalid_name_empty' => '<error>Nama validator tidak boleh kosong.</error>',
'invalid_name' => '<error>Nama validator tidak valid: {name}</error>',
'invalid_plugin' => '<error>Nama plugin tidak valid: {plugin}. `--plugin/-p` harus nama direktori di bawah plugin/ dan tidak boleh berisi / atau \\.</error>',
'plugin_not_found' => "<error>Plugin tidak ditemukan:</error> <comment>{plugin}</comment>\nPeriksa nama plugin atau pastikan plugin terpasang/diaktifkan dengan benar.",
'plugin_path_conflict' => "<error>`--path/-P` tidak berada di bawah plugin/{plugin}/.\nSaat `--plugin/-p` ditentukan, `--path/-P` harus di bawah plugin/{plugin}/.</error>",
'invalid_path' => '<error>Path tidak valid: {path}. `--path/-P` harus path relatif (ke root proyek), bukan path absolut.</error>',
'file_exists' => '<error>Berkas sudah ada:</error> {path}',
'override_prompt' => "<question>Berkas sudah ada: {path}</question>\n<question>Timpa? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'Gunakan <comment>--force/-f</comment> untuk menimpa.',
'scenes_requires_table' => '<error>Opsi --scenes memerlukan --table.</error>',
'unsupported_orm' => '<error>ORM tidak didukung: {orm} (didukung: auto/laravel/thinkorm).</error>',
'database_connection_not_found' => '<error>Koneksi database tidak ditemukan:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>Tidak ada aturan yang disimpulkan dari tabel:</error> {table}',
'failed_generate_from_table' => '<error>Gagal membuat validator dari tabel:</error> {table}',
'failed_write_file' => '<error>Gagal menulis berkas:</error> {path}',
'reason' => '<comment>Alasan:</comment> {reason}',
'created' => '<info>Dibuat:</info> {path}',
'class' => '<info>Kelas:</info> {class}',
'table' => '<info>Tabel:</info> {table}',
'rules_count' => '<info>Jumlah aturan:</info> {count}',
'scenes_count' => '<info>Jumlah adegan:</info> {count}',
],
'th' => [
'invalid_name_empty' => '<error>ชื่อคลาสตรวจสอบไม่สามารถเว้นว่างได้</error>',
'invalid_name' => '<error>ชื่อคลาสตรวจสอบไม่ถูกต้อง: {name}</error>',
'invalid_plugin' => '<error>ชื่อปลั๊กอินไม่ถูกต้อง: {plugin} สำหรับ `--plugin/-p` ต้องเป็นชื่อโฟลเดอร์ภายใต้ plugin/ และห้ามมี / หรือ \\</error>',
'plugin_not_found' => "<error>ไม่พบปลั๊กอิน:</error> <comment>{plugin}</comment>\nกรุณาตรวจสอบชื่อปลั๊กอิน หรือตรวจสอบว่าติดตั้ง/เปิดใช้แล้วอย่างถูกต้อง",
'plugin_path_conflict' => "<error>`--path/-P` ไม่อยู่ภายใต้ plugin/{plugin}/\nเมื่อระบุ `--plugin/-p` `--path/-P` ต้องเป็นเส้นทางภายใต้ plugin/{plugin}/</error>",
'invalid_path' => '<error>เส้นทางไม่ถูกต้อง: {path} สำหรับ `--path/-P` ต้องเป็นเส้นทางสัมพัทธ์ (จากรากโปรเจกต์) ไม่ใช่เส้นทางสัมบูรณ์</error>',
'file_exists' => '<error>มีไฟล์อยู่แล้ว:</error> {path}',
'override_prompt' => "<question>มีไฟล์อยู่แล้ว: {path}</question>\n<question>เขียนทับหรือไม่? [Y/n] (Enter = Y)</question>\n",
'use_force' => 'ใช้ <comment>--force/-f</comment> เพื่อเขียนทับ',
'scenes_requires_table' => '<error>ตัวเลือก --scenes ต้องใช้ร่วมกับ --table</error>',
'unsupported_orm' => '<error>ไม่รองรับ ORM: {orm} (รองรับ: auto/laravel/thinkorm)</error>',
'database_connection_not_found' => '<error>ไม่พบการเชื่อมต่อฐานข้อมูล:</error> <comment>{connection}</comment>',
'no_rules_from_table' => '<error>ไม่สามารถสรุปกฎจากตารางได้:</error> {table}',
'failed_generate_from_table' => '<error>สร้างคลาสตรวจสอบจากตารางไม่สำเร็จ:</error> {table}',
'failed_write_file' => '<error>เขียนไฟล์ไม่สำเร็จ:</error> {path}',
'reason' => '<comment>สาเหตุ:</comment> {reason}',
'created' => '<info>สร้างแล้ว:</info> {path}',
'class' => '<info>คลาส:</info> {class}',
'table' => '<info>ตาราง:</info> {table}',
'rules_count' => '<info>จำนวนกฎ:</info> {count}',
'scenes_count' => '<info>จำนวนฉาก:</info> {count}',
],
];
}
/**
* Plain (no console tags) messages for exception messages. locale => [ key => text ]
*
* @return array<string, array<string, string>>
*/
public static function getPlainMessages(): array
{
return [
'zh_CN' => [
'invalid_name_empty_plain' => '验证器类名不能为空。',
'invalid_segment_empty_plain' => '类名段不能为空。',
'invalid_segment_plain' => '类名段无效:{name}',
'config_not_available' => '配置不可用。',
'database_connection_not_provided' => '未提供数据库连接名。',
'thinkorm_connection_not_found' => 'ThinkORM 连接不存在:{name}(可用:{available})。',
'database_config_invalid' => '数据库配置无效。',
'database_connection_not_found_available' => '数据库连接不存在:{name}(可用:{available})。',
],
'zh_TW' => [
'invalid_name_empty_plain' => '驗證器類名不能為空。',
'invalid_segment_empty_plain' => '類名段不能為空。',
'invalid_segment_plain' => '類名段無效:{name}',
'config_not_available' => '配置不可用。',
'database_connection_not_provided' => '未提供資料庫連線名稱。',
'thinkorm_connection_not_found' => 'ThinkORM 連線不存在:{name}(可用:{available})。',
'database_config_invalid' => '資料庫配置無效。',
'database_connection_not_found_available' => '資料庫連線不存在:{name}(可用:{available})。',
],
'en' => [
'invalid_name_empty_plain' => 'Validator class name cannot be empty.',
'invalid_segment_empty_plain' => 'Class name segment cannot be empty.',
'invalid_segment_plain' => 'Invalid class name segment: {name}',
'config_not_available' => 'Configuration is not available.',
'database_connection_not_provided' => 'Database connection name not provided.',
'thinkorm_connection_not_found' => 'ThinkORM connection not found: {name} (available: {available}).',
'database_config_invalid' => 'Database configuration is invalid.',
'database_connection_not_found_available' => 'Database connection not found: {name} (available: {available}).',
],
'ja' => [
'invalid_name_empty_plain' => 'バリデータークラス名を空にできません。',
'invalid_segment_empty_plain' => 'クラス名セグメントを空にできません。',
'invalid_segment_plain' => '無効なクラス名セグメント:{name}',
'config_not_available' => '設定が利用できません。',
'database_connection_not_provided' => 'データベース接続名が提供されていません。',
'thinkorm_connection_not_found' => 'ThinkORM 接続が見つかりません:{name}(利用可能:{available})。',
'database_config_invalid' => 'データベース設定が無効です。',
'database_connection_not_found_available' => 'データベース接続が見つかりません:{name}(利用可能:{available})。',
],
'ko' => [
'invalid_name_empty_plain' => '유효성 검사 클래스 이름을 비워 둘 수 없습니다.',
'invalid_segment_empty_plain' => '클래스 이름 세그먼트를 비워 둘 수 없습니다.',
'invalid_segment_plain' => '잘못된 클래스 이름 세그먼트: {name}',
'config_not_available' => '구성이 사용할 수 없습니다.',
'database_connection_not_provided' => '데이터베이스 연결 이름이 제공되지 않았습니다.',
'thinkorm_connection_not_found' => 'ThinkORM 연결을 찾을 수 없습니다: {name} (사용 가능: {available}).',
'database_config_invalid' => '데이터베이스 구성이 유효하지 않습니다.',
'database_connection_not_found_available' => '데이터베이스 연결을 찾을 수 없습니다: {name} (사용 가능: {available}).',
],
'fr' => [
'invalid_name_empty_plain' => 'Le nom de la classe de validation ne peut pas être vide.',
'invalid_segment_empty_plain' => 'Le segment du nom de classe ne peut pas être vide.',
'invalid_segment_plain' => 'Segment de nom de classe invalide : {name}',
'config_not_available' => 'La configuration n\'est pas disponible.',
'database_connection_not_provided' => 'Nom de connexion à la base de données non fourni.',
'thinkorm_connection_not_found' => 'Connexion ThinkORM introuvable : {name} (disponible : {available}).',
'database_config_invalid' => 'La configuration de la base de données est invalide.',
'database_connection_not_found_available' => 'Connexion à la base de données introuvable : {name} (disponible : {available}).',
],
'de' => [
'invalid_name_empty_plain' => 'Der Name der Validierungsklasse darf nicht leer sein.',
'invalid_segment_empty_plain' => 'Klassennamensegment darf nicht leer sein.',
'invalid_segment_plain' => 'Ungültiges Klassennamensegment: {name}',
'config_not_available' => 'Konfiguration ist nicht verfügbar.',
'database_connection_not_provided' => 'Datenbankverbindungsname nicht angegeben.',
'thinkorm_connection_not_found' => 'ThinkORM-Verbindung nicht gefunden: {name} (verfügbar: {available}).',
'database_config_invalid' => 'Datenbankkonfiguration ist ungültig.',
'database_connection_not_found_available' => 'Datenbankverbindung nicht gefunden: {name} (verfügbar: {available}).',
],
'es' => [
'invalid_name_empty_plain' => 'El nombre de la clase de validación no puede estar vacío.',
'invalid_segment_empty_plain' => 'El segmento del nombre de clase no puede estar vacío.',
'invalid_segment_plain' => 'Segmento de nombre de clase no válido: {name}',
'config_not_available' => 'La configuración no está disponible.',
'database_connection_not_provided' => 'Nombre de conexión a la base de datos no proporcionado.',
'thinkorm_connection_not_found' => 'Conexión ThinkORM no encontrada: {name} (disponible: {available}).',
'database_config_invalid' => 'La configuración de la base de datos es inválida.',
'database_connection_not_found_available' => 'Conexión a la base de datos no encontrada: {name} (disponible: {available}).',
],
'pt_BR' => [
'invalid_name_empty_plain' => 'O nome da classe de validação não pode estar vazio.',
'invalid_segment_empty_plain' => 'O segmento do nome da classe não pode estar vazio.',
'invalid_segment_plain' => 'Segmento de nome de classe inválido: {name}',
'config_not_available' => 'A configuração não está disponível.',
'database_connection_not_provided' => 'Nome da conexão do banco de dados não fornecido.',
'thinkorm_connection_not_found' => 'Conexão ThinkORM não encontrada: {name} (disponível: {available}).',
'database_config_invalid' => 'A configuração do banco de dados é inválida.',
'database_connection_not_found_available' => 'Conexão com o banco de dados não encontrada: {name} (disponível: {available}).',
],
'ru' => [
'invalid_name_empty_plain' => 'Имя класса валидации не может быть пустым.',
'invalid_segment_empty_plain' => 'Сегмент имени класса не может быть пустым.',
'invalid_segment_plain' => 'Недопустимый сегмент имени класса: {name}',
'config_not_available' => 'Конфигурация недоступна.',
'database_connection_not_provided' => 'Имя подключения к БД не указано.',
'thinkorm_connection_not_found' => 'Подключение ThinkORM не найдено: {name} (доступно: {available}).',
'database_config_invalid' => 'Конфигурация БД недействительна.',
'database_connection_not_found_available' => 'Подключение к БД не найдено: {name} (доступно: {available}).',
],
'vi' => [
'invalid_name_empty_plain' => 'Tên lớp xác thực không được để trống.',
'invalid_segment_empty_plain' => 'Đoạn tên lớp không được để trống.',
'invalid_segment_plain' => 'Đoạn tên lớp không hợp lệ: {name}',
'config_not_available' => 'Cấu hình không khả dụng.',
'database_connection_not_provided' => 'Tên kết nối cơ sở dữ liệu chưa được cung cấp.',
'thinkorm_connection_not_found' => 'Không tìm thấy kết nối ThinkORM: {name} (có sẵn: {available}).',
'database_config_invalid' => 'Cấu hình cơ sở dữ liệu không hợp lệ.',
'database_connection_not_found_available' => 'Không tìm thấy kết nối cơ sở dữ liệu: {name} (có sẵn: {available}).',
],
'tr' => [
'invalid_name_empty_plain' => 'Doğrulama sınıf adı boş bırakılamaz.',
'invalid_segment_empty_plain' => 'Sınıf adı segmenti boş bırakılamaz.',
'invalid_segment_plain' => 'Geçersiz sınıf adı segmenti: {name}',
'config_not_available' => 'Yapılandırma kullanılamıyor.',
'database_connection_not_provided' => 'Veritabanı bağlantı adı sağlanmadı.',
'thinkorm_connection_not_found' => 'ThinkORM bağlantısı bulunamadı: {name} (mevcut: {available}).',
'database_config_invalid' => 'Veritabanı yapılandırması geçersiz.',
'database_connection_not_found_available' => 'Veritabanı bağlantısı bulunamadı: {name} (mevcut: {available}).',
],
'id' => [
'invalid_name_empty_plain' => 'Nama kelas validasi tidak boleh kosong.',
'invalid_segment_empty_plain' => 'Segmen nama kelas tidak boleh kosong.',
'invalid_segment_plain' => 'Segmen nama kelas tidak valid: {name}',
'config_not_available' => 'Konfigurasi tidak tersedia.',
'database_connection_not_provided' => 'Nama koneksi database tidak diberikan.',
'thinkorm_connection_not_found' => 'Koneksi ThinkORM tidak ditemukan: {name} (tersedia: {available}).',
'database_config_invalid' => 'Konfigurasi database tidak valid.',
'database_connection_not_found_available' => 'Koneksi database tidak ditemukan: {name} (tersedia: {available}).',
],
'th' => [
'invalid_name_empty_plain' => 'ชื่อคลาสตรวจสอบไม่สามารถเว้นว่างได้',
'invalid_segment_empty_plain' => 'ส่วนชื่อคลาสไม่สามารถเว้นว่างได้',
'invalid_segment_plain' => 'ส่วนชื่อคลาสไม่ถูกต้อง: {name}',
'config_not_available' => 'การกำหนดค่าไม่พร้อมใช้งาน',
'database_connection_not_provided' => 'ไม่ได้ระบุชื่อการเชื่อมต่อฐานข้อมูล',
'thinkorm_connection_not_found' => 'ไม่พบการเชื่อมต่อ ThinkORM: {name} (มีให้: {available})',
'database_config_invalid' => 'การกำหนดค่าฐานข้อมูลไม่ถูกต้อง',
'database_connection_not_found_available' => 'ไม่พบการเชื่อมต่อฐานข้อมูล: {name} (มีให้: {available})',
],
];
}
/**
* Command help text (bilingual).
*
* @return array<string, string> locale => help text
*/
public static function getHelpText(): array
{
return [
'zh_CN' => <<<'EOF'
生成验证器类文件(默认在 app/validation 下)。
推荐用法:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
说明:
- 默认生成到 app/validation(大小写以现有目录为准)。
- 使用 -p/--plugin 时默认生成到 plugin/<plugin>/app/validation。
- 使用 -P/--path 时生成到指定相对目录(相对于项目根目录)。
- 文件已存在时默认拒绝覆盖;使用 -f/--force 可强制覆盖。
- 使用 -t/--table 可从数据库数据表推断规则;如需生成场景请同时指定 -s/--scenes(例如 crud)。
EOF,
'zh_TW' => <<<'EOF'
產生驗證器類檔案(預設在 app/validation 下)。
推薦用法:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
說明:
- 預設產生到 app/validation(大小寫以現有目錄為準)。
- 使用 -p/--plugin 時預設產生到 plugin/<plugin>/app/validation。
- 使用 -P/--path 時產生到指定相對目錄(相對於專案根目錄)。
- 檔案已存在時預設拒絕覆蓋;使用 -f/--force 可強制覆蓋。
- 使用 -t/--table 可從資料庫資料表推斷規則;如需產生場景請同時指定 -s/--scenes(例如 crud)。
EOF,
'en' => <<<'EOF'
Generate a validator class file (default under app/validation).
Recommended:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Notes:
- By default, it generates under app/validation (case depends on existing directory).
- With -p/--plugin, it generates under plugin/<plugin>/app/validation by default.
- With -P/--path, it generates under the specified relative directory (to project root).
- If the file already exists, it refuses to overwrite by default; use -f/--force to overwrite.
- With -t/--table, it infers rules from a database table; to generate scenes, also provide -s/--scenes (e.g. crud).
EOF,
'ja' => <<<'EOF'
バリデータークラスファイルを生成します(デフォルトは app/validation 以下)。
推奨用法:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
説明:
- デフォルトでは app/validation 以下に生成(大文字小文字は既存ディレクトリに合わせます)。
- -p/--plugin 使用時は plugin/<plugin>/app/validation 以下に生成。
- -P/--path 使用時は指定した相対ディレクトリ(プロジェクトルート基準)に生成。
- ファイルが既に存在する場合はデフォルトで上書きしません。-f/--force で上書きできます。
- -t/--table でデータベーステーブルからルールを推論。シーンも生成する場合は -s/--scenes(例:crud)を指定してください。
EOF,
'ko' => <<<'EOF'
유효성 검사 클래스 파일을 생성합니다 (기본 위치: app/validation).
권장 사용법:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
설명:
- 기본적으로 app/validation 아래에 생성합니다 (대소문자는 기존 디렉터리에 맞춤).
- -p/--plugin 사용 plugin/<plugin>/app/validation 아래에 생성합니다.
- -P/--path 사용 지정한 상대 디렉터리(프로젝트 루트 기준) 생성합니다.
- 파일이 이미 있으면 기본적으로 덮어쓰지 않습니다. -f/--force로 덮어쓸 있습니다.
- -t/--table로 데이터베이스 테이블에서 규칙을 추론합니다. 장면도 생성하려면 -s/--scenes(: crud) 함께 지정하세요.
EOF,
'fr' => <<<'EOF'
Génère un fichier de classe de validation (par défaut sous app/validation).
Recommandé :
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Notes :
- Par défaut, génération sous app/validation (casse selon le répertoire existant).
- Avec -p/--plugin, génération sous plugin/<plugin>/app/validation par défaut.
- Avec -P/--path, génération dans le répertoire relatif indiqué (par rapport à la racine du projet).
- Si le fichier existe déjà, refus d'écraser par défaut ; utilisez -f/--force pour écraser.
- Avec -t/--table, déduction des règles à partir d'une table ; pour générer des scènes, indiquez aussi -s/--scenes (ex. crud).
EOF,
'de' => <<<'EOF'
Erstellt eine Validierungsklassen-Datei (Standard: unter app/validation).
Empfohlen:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Hinweise:
- Standardmäßig wird unter app/validation erzeugt (Groß-/Kleinschreibung nach vorhandenem Verzeichnis).
- Mit -p/--plugin wird standardmäßig unter plugin/<plugin>/app/validation erzeugt.
- Mit -P/--path wird im angegebenen relativen Verzeichnis (zum Projektstamm) erzeugt.
- Wenn die Datei bereits existiert, wird standardmäßig nicht überschrieben; mit -f/--force erzwingen.
- Mit -t/--table werden Regeln aus einer Datenbanktabelle abgeleitet; für Szenen zusätzlich -s/--scenes angeben (z. B. crud).
EOF,
'es' => <<<'EOF'
Genera un archivo de clase de validación (por defecto bajo app/validation).
Recomendado:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Notas:
- Por defecto genera bajo app/validation (mayúsculas/minúsculas según el directorio existente).
- Con -p/--plugin genera bajo plugin/<plugin>/app/validation por defecto.
- Con -P/--path genera en el directorio relativo indicado (respecto a la raíz del proyecto).
- Si el archivo ya existe, por defecto no se sobrescribe; use -f/--force para sobrescribir.
- Con -t/--table infiere reglas desde una tabla; para generar escenas, indique también -s/--scenes (ej. crud).
EOF,
'pt_BR' => <<<'EOF'
Gera um arquivo de classe de validação (padrão em app/validation).
Recomendado:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Notas:
- Por padrão gera em app/validation (maiúsculas/minúsculas conforme o diretório existente).
- Com -p/--plugin gera em plugin/<plugin>/app/validation por padrão.
- Com -P/--path gera no diretório relativo informado (em relação à raiz do projeto).
- Se o arquivo existir, por padrão não sobrescreve; use -f/--force para sobrescrever.
- Com -t/--table infere regras a partir de uma tabela; para gerar cenas, informe também -s/--scenes (ex.: crud).
EOF,
'ru' => <<<'EOF'
Создаёт файл класса валидации (по умолчанию в app/validation).
Рекомендуется:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Примечания:
- По умолчанию создаётся в app/validation (регистр по существующей папке).
- С -p/--plugin по умолчанию создаётся в plugin/<plugin>/app/validation.
- С -P/--path создаётся в указанной относительной директории (от корня проекта).
- Если файл уже есть, по умолчанию не перезаписывается; используйте -f/--force для перезаписи.
- С -t/--table правила выводятся из таблицы БД; для сцен укажите также -s/--scenes (напр. crud).
EOF,
'vi' => <<<'EOF'
Tạo tệp lớp xác thực (mặc định trong app/validation).
Khuyến nghị:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Ghi chú:
- Mặc định tạo trong app/validation (chữ hoa/thường theo thư mục hiện ).
- Dùng -p/--plugin thì mặc định tạo trong plugin/<plugin>/app/validation.
- Dùng -P/--path thì tạo vào thư mục tương đối chỉ định (so với thư mục gốc dự án).
- Nếu tệp đã tồn tại thì mặc định không ghi đè; dùng -f/--force để ghi đè.
- Dùng -t/--table để suy ra quy tắc từ bảng sở dữ liệu; để tạo cảnh thì thêm -s/--scenes (vd: crud).
EOF,
'tr' => <<<'EOF'
Doğrulama sınıf dosyası oluşturur (varsayılan: app/validation altında).
Önerilen:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Notlar:
- Varsayılan olarak app/validation altında oluşturulur (büyük/küçük harf mevcut dizine göre).
- -p/--plugin ile varsayılan olarak plugin/<plugin>/app/validation altında oluşturulur.
- -P/--path ile belirtilen göreli dizinde (proje köküne göre) oluşturulur.
- Dosya zaten varsa varsayılan olarak üzerine yazılmaz; üzerine yazmak için -f/--force kullanın.
- -t/--table ile veritabanı tablosundan kurallar çıkarılır; sahneleri de oluşturmak için -s/--scenes (örn. crud) belirtin.
EOF,
'id' => <<<'EOF'
Membuat berkas kelas validasi (baku: di bawah app/validation).
Disarankan:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
Catatan:
- Secara baku dibuat di bawah app/validation (huruf besar/kecil mengikuti direktori yang ada).
- Dengan -p/--plugin secara baku dibuat di bawah plugin/<plugin>/app/validation.
- Dengan -P/--path dibuat di direktori relatif yang ditentukan (terhadap root proyek).
- Jika berkas sudah ada, secara baku tidak menimpa; gunakan -f/--force untuk menimpa.
- Dengan -t/--table aturan disimpulkan dari tabel database; untuk membuat adegan, berikan juga -s/--scenes (mis. crud).
EOF,
'th' => <<<'EOF'
สร้างไฟล์คลาสตรวจสอบความถูกต้อง (ค่าเริ่มต้นอยู่ใต้ app/validation)
แนะนำ:
php webman make:validator UserValidator
php webman make:validator admin/UserValidator
php webman make:validator UserValidator -p admin
php webman make:validator UserValidator -P plugin/admin/app/validation
หมายเหตุ:
- ค่าเริ่มต้นจะสร้างใต้ app/validation (ตัวพิมพ์ตามโฟลเดอร์ที่มีอยู่)
- ใช้ -p/--plugin จะสร้างใต้ plugin/<plugin>/app/validation โดยค่าเริ่มต้น
- ใช้ -P/--path จะสร้างในโฟลเดอร์สัมพัทธ์ที่กำหนด (เทียบกับรากโปรเจกต์)
- ถ้ามีไฟล์อยู่แล้วโดยค่าเริ่มต้นจะไม่เขียนทับ ใช้ -f/--force เพื่อเขียนทับ
- ใช้ -t/--table จะสรุปกฎจากตารางฐานข้อมูล เพื่อสร้างฉากให้ระบุ -s/--scenes ด้วย (เช่น crud)
EOF,
];
}
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Contracts;
interface ConnectionResolverInterface
{
/**
* @throws \RuntimeException When connection cannot be resolved.
*/
public function resolve(?string $connectionName = null): SchemaConnectionInterface;
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Contracts;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
interface RuleInferrerInterface
{
/**
* @param array{exclude_columns?: list<string>} $options
* @return array{rules: array<string, string>, attributes: array<string, string>, scenes?: array<string, list<string>>}
*/
public function infer(TableDefinition $table, array $options = []): array;
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Contracts;
/**
* A minimal DB connection abstraction for schema introspection.
*
* This intentionally avoids binding to a specific ORM (Illuminate/ThinkOrm).
*/
interface SchemaConnectionInterface
{
public function driverName(): string;
public function databaseName(): ?string;
/**
* @param array<int, mixed> $bindings
* @return array<int, array<string, mixed>>
*/
public function select(string $sql, array $bindings = []): array;
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Contracts;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
interface SchemaIntrospectorInterface
{
/**
* @throws \RuntimeException When schema cannot be read.
*/
public function introspect(SchemaConnectionInterface $connection, string $table): TableDefinition;
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\DTO;
final class ColumnDefinition
{
/**
* @param list<string> $enumValues
*/
public function __construct(
public readonly string $name,
public readonly string $dataType,
public readonly string $columnType,
public readonly bool $nullable,
public readonly mixed $defaultValue,
public readonly ?int $characterMaximumLength,
public readonly ?int $numericPrecision,
public readonly ?int $numericScale,
public readonly bool $unsigned,
public readonly bool $autoIncrement,
public readonly array $enumValues,
public readonly string $comment,
) {
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\DTO;
final class TableDefinition
{
/**
* @param list<ColumnDefinition> $columns
* @param list<string> $primaryKeyColumns
*/
public function __construct(
public readonly string $table,
public readonly array $columns,
public readonly array $primaryKeyColumns = [],
) {
}
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Illuminate\Database\ConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\ConnectionResolverInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
final class IlluminateConnectionResolver implements ConnectionResolverInterface
{
public function resolve(?string $connectionName = null): SchemaConnectionInterface
{
if (!class_exists(\support\Db::class)) {
throw new \RuntimeException('Database support not found. Please install/enable webman/database plugin.');
}
$dbConfig = config('database', []);
if (!is_array($dbConfig)) {
throw new \RuntimeException('Invalid database config: config("database") must be an array.');
}
$connections = $dbConfig['connections'] ?? null;
if (!is_array($connections) || $connections === []) {
throw new \RuntimeException('Invalid database config: database.connections must be a non-empty array.');
}
$name = $connectionName;
if ($name === null || trim($name) === '') {
$default = $dbConfig['default'] ?? null;
if (!is_string($default) || trim($default) === '') {
throw new \RuntimeException('Database connection name not provided and database.default is not set.');
}
$name = trim($default);
}
$name = trim((string)$name);
$connKey = $name;
if (str_starts_with($name, 'plugin.')) {
// plugin.<plugin>.<connection>
$parts = explode('.', $name, 3);
$plugin = $parts[1] ?? '';
$conn = $parts[2] ?? '';
$plugin = is_string($plugin) ? trim($plugin) : '';
$conn = is_string($conn) ? trim($conn) : '';
if ($plugin === '' || $conn === '') {
throw new \RuntimeException("Invalid plugin connection name: {$name}");
}
$pluginDb = config("plugin.$plugin.database", []);
$pluginConnections = (is_array($pluginDb) ? ($pluginDb['connections'] ?? null) : null);
if (is_array($pluginConnections) && $pluginConnections !== []) {
if (!array_key_exists($conn, $pluginConnections)) {
$available = implode(', ', array_keys($pluginConnections));
throw new \RuntimeException("Database connection not found: {$name}. Available connections: {$available}");
}
$connKey = "plugin.$plugin.$conn";
} else {
// No plugin database connections: fallback to main project config.
$connKey = $conn;
if (!array_key_exists($connKey, $connections)) {
$available = implode(', ', array_keys($connections));
throw new \RuntimeException("Database connection not found: {$connKey}. Available connections: {$available}");
}
}
} else {
if (!array_key_exists($connKey, $connections)) {
$available = implode(', ', array_keys($connections));
throw new \RuntimeException("Database connection not found: {$connKey}. Available connections: {$available}");
}
}
/** @var ConnectionInterface $connection */
$connection = \support\Db::connection($connKey);
return new IlluminateSchemaConnection($connection);
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Illuminate\Database\ConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
final class IlluminateSchemaConnection implements SchemaConnectionInterface
{
public function __construct(private readonly ConnectionInterface $connection)
{
}
public function driverName(): string
{
return (string)$this->connection->getDriverName();
}
public function databaseName(): ?string
{
$name = (string)$this->connection->getDatabaseName();
return $name !== '' ? $name : null;
}
public function select(string $sql, array $bindings = []): array
{
/** @var array<int, mixed> $rows */
$rows = $this->connection->select($sql, $bindings);
$result = [];
foreach ($rows as $row) {
if (is_array($row)) {
$result[] = $row;
continue;
}
if (is_object($row)) {
/** @var array<string, mixed> $arr */
$arr = get_object_vars($row);
$result[] = $arr;
continue;
}
$result[] = ['value' => $row];
}
return $result;
}
}
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaIntrospectorInterface;
use Webman\Validation\Command\ValidatorGenerator\DTO\ColumnDefinition;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
final class MySqlInformationSchemaIntrospector implements SchemaIntrospectorInterface
{
public function introspect(SchemaConnectionInterface $connection, string $table): TableDefinition
{
$table = trim($table);
if ($table === '') {
throw new \InvalidArgumentException('Table name cannot be empty.');
}
$database = $connection->databaseName();
if (!$database) {
throw new \RuntimeException('Database name is empty for current connection.');
}
$rows = $connection->select(
"SELECT
COLUMN_NAME AS column_name,
DATA_TYPE AS data_type,
COLUMN_TYPE AS column_type,
IS_NULLABLE AS is_nullable,
COLUMN_DEFAULT AS column_default,
CHARACTER_MAXIMUM_LENGTH AS character_maximum_length,
NUMERIC_PRECISION AS numeric_precision,
NUMERIC_SCALE AS numeric_scale,
EXTRA AS extra,
COLUMN_COMMENT AS column_comment
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = ?
AND TABLE_NAME = ?
ORDER BY ORDINAL_POSITION",
[$database, $table]
);
if ($rows === []) {
throw new \RuntimeException("Table not found or has no columns: {$database}.{$table}");
}
$pkRows = $connection->select(
"SELECT
COLUMN_NAME AS column_name
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = ?
AND TABLE_NAME = ?
AND CONSTRAINT_NAME = 'PRIMARY'
ORDER BY ORDINAL_POSITION",
[$database, $table]
);
$primaryKeyColumns = [];
foreach ($pkRows as $pkRow) {
$pkName = (string)($pkRow['column_name'] ?? '');
if ($pkName !== '') {
$primaryKeyColumns[] = $pkName;
}
}
$columns = [];
foreach ($rows as $row) {
$name = (string)($row['column_name'] ?? '');
if ($name === '') {
continue;
}
$dataType = strtolower((string)($row['data_type'] ?? ''));
$columnType = strtolower((string)($row['column_type'] ?? $dataType));
$nullable = strtoupper((string)($row['is_nullable'] ?? 'NO')) === 'YES';
$defaultValue = $row['column_default'] ?? null;
$charLen = $row['character_maximum_length'] ?? null;
$characterMaximumLength = $charLen === null ? null : (int)$charLen;
$precision = $row['numeric_precision'] ?? null;
$numericPrecision = $precision === null ? null : (int)$precision;
$scale = $row['numeric_scale'] ?? null;
$numericScale = $scale === null ? null : (int)$scale;
$extra = strtolower((string)($row['extra'] ?? ''));
$autoIncrement = str_contains($extra, 'auto_increment');
$unsigned = str_contains($columnType, 'unsigned');
$comment = (string)($row['column_comment'] ?? '');
$enumValues = $this->parseEnumValues($dataType, $columnType);
$columns[] = new ColumnDefinition(
name: $name,
dataType: $dataType,
columnType: $columnType,
nullable: $nullable,
defaultValue: $defaultValue,
characterMaximumLength: $characterMaximumLength,
numericPrecision: $numericPrecision,
numericScale: $numericScale,
unsigned: $unsigned,
autoIncrement: $autoIncrement,
enumValues: $enumValues,
comment: $comment,
);
}
return new TableDefinition($table, $columns, $primaryKeyColumns);
}
/**
* @return list<string>
*/
private function parseEnumValues(string $dataType, string $columnType): array
{
if ($dataType !== 'enum' && !str_starts_with($columnType, 'enum(')) {
return [];
}
// column_type example: enum('a','b','c')
if (!preg_match('/^enum\((.*)\)$/i', $columnType, $m)) {
return [];
}
$inner = $m[1];
// Match MySQL quoted strings inside enum(...)
if (!preg_match_all("/'((?:\\\\'|[^'])*)'/", $inner, $matches)) {
return [];
}
$values = [];
foreach ($matches[1] as $v) {
$values[] = str_replace("\\'", "'", (string)$v);
}
return $values;
}
}
@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaIntrospectorInterface;
use Webman\Validation\Command\ValidatorGenerator\DTO\ColumnDefinition;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
final class PostgresInformationSchemaIntrospector implements SchemaIntrospectorInterface
{
public function introspect(SchemaConnectionInterface $connection, string $table): TableDefinition
{
$table = trim($table);
if ($table === '') {
throw new \InvalidArgumentException('Table name cannot be empty.');
}
[$schema, $tableName] = $this->splitSchemaTable($table);
$rows = $connection->select(
"SELECT
c.column_name AS column_name,
c.data_type AS data_type,
c.udt_name AS udt_name,
c.is_nullable AS is_nullable,
c.column_default AS column_default,
c.character_maximum_length AS character_maximum_length,
c.numeric_precision AS numeric_precision,
c.numeric_scale AS numeric_scale
FROM information_schema.columns c
WHERE c.table_schema = ?
AND c.table_name = ?
ORDER BY c.ordinal_position",
[$schema, $tableName]
);
if ($rows === []) {
throw new \RuntimeException("Table not found or has no columns: {$schema}.{$tableName}");
}
$pkRows = $connection->select(
"SELECT kcu.column_name AS column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
AND tc.table_name = kcu.table_name
WHERE tc.constraint_type = 'PRIMARY KEY'
AND tc.table_schema = ?
AND tc.table_name = ?
ORDER BY kcu.ordinal_position",
[$schema, $tableName]
);
$primaryKeyColumns = [];
foreach ($pkRows as $pkRow) {
$pkName = (string)($pkRow['column_name'] ?? '');
if ($pkName !== '') {
$primaryKeyColumns[] = $pkName;
}
}
// Fetch column comments (best-effort).
$commentRows = $connection->select(
"SELECT
a.attname AS column_name,
COALESCE(col_description(a.attrelid, a.attnum), '') AS column_comment
FROM pg_attribute a
JOIN pg_class t ON t.oid = a.attrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE n.nspname = ?
AND t.relname = ?
AND a.attnum > 0
AND NOT a.attisdropped",
[$schema, $tableName]
);
$commentsByColumn = [];
foreach ($commentRows as $cr) {
$cn = (string)($cr['column_name'] ?? '');
if ($cn !== '') {
$commentsByColumn[$cn] = (string)($cr['column_comment'] ?? '');
}
}
// Fetch enum values (best-effort) for user-defined enum types.
$enumValuesByType = $this->loadEnumValuesByType($connection);
$columns = [];
foreach ($rows as $row) {
$name = (string)($row['column_name'] ?? '');
if ($name === '') {
continue;
}
$dataType = strtolower((string)($row['data_type'] ?? ''));
$udtName = strtolower((string)($row['udt_name'] ?? ''));
$nullable = strtoupper((string)($row['is_nullable'] ?? 'NO')) === 'YES';
$defaultValue = $row['column_default'] ?? null;
$charLen = $row['character_maximum_length'] ?? null;
$characterMaximumLength = $charLen === null ? null : (int)$charLen;
$precision = $row['numeric_precision'] ?? null;
$numericPrecision = $precision === null ? null : (int)$precision;
$scale = $row['numeric_scale'] ?? null;
$numericScale = $scale === null ? null : (int)$scale;
$autoIncrement = false;
if (is_string($defaultValue) && str_contains($defaultValue, 'nextval(')) {
$autoIncrement = true;
}
$comment = (string)($commentsByColumn[$name] ?? '');
$enumValues = [];
if ($dataType === 'user-defined' && isset($enumValuesByType[$udtName])) {
$enumValues = $enumValuesByType[$udtName];
$dataType = 'enum';
}
$columns[] = new ColumnDefinition(
name: $name,
dataType: $dataType,
columnType: $udtName !== '' ? $udtName : $dataType,
nullable: $nullable,
defaultValue: $defaultValue,
characterMaximumLength: $characterMaximumLength,
numericPrecision: $numericPrecision,
numericScale: $numericScale,
unsigned: false,
autoIncrement: $autoIncrement,
enumValues: $enumValues,
comment: $comment,
);
}
return new TableDefinition($tableName, $columns, $primaryKeyColumns);
}
/**
* @return array{0:string,1:string} [schema, table]
*/
private function splitSchemaTable(string $table): array
{
$parts = array_values(array_filter(explode('.', $table), static fn(string $p): bool => $p !== ''));
if (count($parts) === 1) {
return ['public', $parts[0]];
}
if (count($parts) === 2) {
return [$parts[0], $parts[1]];
}
throw new \InvalidArgumentException("Invalid table name for Postgres: {$table}");
}
/**
* @return array<string, list<string>> map udt_name => enum labels
*/
private function loadEnumValuesByType(SchemaConnectionInterface $connection): array
{
$rows = $connection->select(
"SELECT
t.typname AS type_name,
e.enumlabel AS enum_label
FROM pg_type t
JOIN pg_enum e ON e.enumtypid = t.oid
ORDER BY t.typname, e.enumsortorder"
);
$map = [];
foreach ($rows as $row) {
$type = strtolower((string)($row['type_name'] ?? ''));
$label = (string)($row['enum_label'] ?? '');
if ($type === '' || $label === '') {
continue;
}
$map[$type] ??= [];
$map[$type][] = $label;
}
return $map;
}
}
@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaIntrospectorInterface;
use Webman\Validation\Command\ValidatorGenerator\DTO\ColumnDefinition;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
final class SqlServerIntrospector implements SchemaIntrospectorInterface
{
public function introspect(SchemaConnectionInterface $connection, string $table): TableDefinition
{
$table = trim($table);
if ($table === '') {
throw new \InvalidArgumentException('Table name cannot be empty.');
}
[$schema, $tableName] = $this->splitSchemaTable($table);
$rows = $connection->select(
"SELECT
c.name AS column_name,
t.name AS data_type,
CASE
WHEN t.name IN ('nvarchar','nchar') THEN c.max_length / 2
ELSE c.max_length
END AS character_maximum_length,
c.precision AS numeric_precision,
c.scale AS numeric_scale,
c.is_nullable AS is_nullable,
dc.definition AS column_default,
c.is_identity AS is_identity,
ep.value AS column_comment
FROM sys.columns c
JOIN sys.tables tb ON tb.object_id = c.object_id
JOIN sys.schemas s ON s.schema_id = tb.schema_id
JOIN sys.types t ON t.user_type_id = c.user_type_id
LEFT JOIN sys.default_constraints dc ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id AND ep.minor_id = c.column_id AND ep.name = 'MS_Description'
WHERE s.name = ?
AND tb.name = ?
ORDER BY c.column_id",
[$schema, $tableName]
);
if ($rows === []) {
throw new \RuntimeException("Table not found or has no columns: {$schema}.{$tableName}");
}
$pkRows = $connection->select(
"SELECT c.name AS column_name
FROM sys.indexes i
JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
JOIN sys.tables tb ON tb.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = tb.schema_id
WHERE i.is_primary_key = 1
AND s.name = ?
AND tb.name = ?
ORDER BY ic.key_ordinal",
[$schema, $tableName]
);
$primaryKeyColumns = [];
foreach ($pkRows as $pkRow) {
$pkName = (string)($pkRow['column_name'] ?? '');
if ($pkName !== '') {
$primaryKeyColumns[] = $pkName;
}
}
$columns = [];
foreach ($rows as $row) {
$name = (string)($row['column_name'] ?? '');
if ($name === '') {
continue;
}
$dataType = strtolower((string)($row['data_type'] ?? ''));
$nullable = (int)($row['is_nullable'] ?? 0) === 1;
$defaultValue = $row['column_default'] ?? null;
$charLen = $row['character_maximum_length'] ?? null;
$characterMaximumLength = $charLen === null ? null : (int)$charLen;
$precision = $row['numeric_precision'] ?? null;
$numericPrecision = $precision === null ? null : (int)$precision;
$scale = $row['numeric_scale'] ?? null;
$numericScale = $scale === null ? null : (int)$scale;
$autoIncrement = (int)($row['is_identity'] ?? 0) === 1;
$comment = is_string($row['column_comment'] ?? null) ? (string)$row['column_comment'] : '';
$columns[] = new ColumnDefinition(
name: $name,
dataType: $dataType,
columnType: $dataType,
nullable: $nullable,
defaultValue: $defaultValue,
characterMaximumLength: $characterMaximumLength,
numericPrecision: $numericPrecision,
numericScale: $numericScale,
unsigned: false,
autoIncrement: $autoIncrement,
enumValues: [],
comment: $comment,
);
}
return new TableDefinition($tableName, $columns, $primaryKeyColumns);
}
/**
* @return array{0:string,1:string} [schema, table]
*/
private function splitSchemaTable(string $table): array
{
$parts = array_values(array_filter(explode('.', $table), static fn(string $p): bool => $p !== ''));
if (count($parts) === 1) {
return ['dbo', $parts[0]];
}
if (count($parts) === 2) {
return [$parts[0], $parts[1]];
}
throw new \InvalidArgumentException("Invalid table name for SQL Server: {$table}");
}
}
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Illuminate;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaIntrospectorInterface;
use Webman\Validation\Command\ValidatorGenerator\DTO\ColumnDefinition;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
final class SqlitePragmaIntrospector implements SchemaIntrospectorInterface
{
public function introspect(SchemaConnectionInterface $connection, string $table): TableDefinition
{
$table = trim($table);
if ($table === '') {
throw new \InvalidArgumentException('Table name cannot be empty.');
}
if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $table)) {
throw new \InvalidArgumentException("Invalid table name for SQLite: {$table}");
}
$rows = $connection->select("PRAGMA table_info('{$table}')");
if ($rows === []) {
throw new \RuntimeException("Table not found or has no columns: {$table}");
}
$primaryKeyColumns = [];
$columns = [];
foreach ($rows as $row) {
$name = (string)($row['name'] ?? '');
if ($name === '') {
continue;
}
$type = strtolower((string)($row['type'] ?? ''));
$notNull = (int)($row['notnull'] ?? 0) === 1;
$nullable = !$notNull;
$defaultValue = $row['dflt_value'] ?? null;
$pk = (int)($row['pk'] ?? 0);
if ($pk > 0) {
$primaryKeyColumns[] = $name;
}
[$dataType, $charLen, $precision, $scale] = $this->parseType($type);
$autoIncrement = false;
// Best-effort: INTEGER PRIMARY KEY behaves like auto-increment rowid.
if ($pk > 0 && $dataType === 'integer') {
$autoIncrement = true;
}
$columns[] = new ColumnDefinition(
name: $name,
dataType: $dataType,
columnType: $type !== '' ? $type : $dataType,
nullable: $nullable,
defaultValue: $defaultValue,
characterMaximumLength: $charLen,
numericPrecision: $precision,
numericScale: $scale,
unsigned: false,
autoIncrement: $autoIncrement,
enumValues: [],
comment: '',
);
}
return new TableDefinition($table, $columns, $primaryKeyColumns);
}
/**
* @return array{0:string,1:?int,2:?int,3:?int} [dataType, charLen, precision, scale]
*/
private function parseType(string $type): array
{
$type = strtolower(trim($type));
if ($type === '') {
return ['string', null, null, null];
}
if (str_contains($type, 'int')) {
return ['integer', null, null, null];
}
if (str_contains($type, 'char') || str_contains($type, 'clob') || str_contains($type, 'text')) {
$len = null;
if (preg_match('/\((\d+)\)/', $type, $m)) {
$len = (int)$m[1];
}
return ['varchar', $len, null, null];
}
if (str_contains($type, 'blob')) {
return ['string', null, null, null];
}
if (str_contains($type, 'real') || str_contains($type, 'floa') || str_contains($type, 'doub')) {
return ['double', null, null, null];
}
if (str_contains($type, 'dec') || str_contains($type, 'num')) {
$precision = null;
$scale = null;
if (preg_match('/\((\d+)\s*,\s*(\d+)\)/', $type, $m)) {
$precision = (int)$m[1];
$scale = (int)$m[2];
} elseif (preg_match('/\((\d+)\)/', $type, $m)) {
$precision = (int)$m[1];
}
return ['decimal', null, $precision, $scale];
}
if (str_contains($type, 'bool')) {
return ['boolean', null, null, null];
}
if (str_contains($type, 'date') || str_contains($type, 'time')) {
return ['datetime', null, null, null];
}
return ['string', null, null, null];
}
}
@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Rules;
use Webman\Validation\Command\ValidatorGenerator\Contracts\RuleInferrerInterface;
use Webman\Validation\Command\ValidatorGenerator\DTO\ColumnDefinition;
use Webman\Validation\Command\ValidatorGenerator\DTO\TableDefinition;
final class DefaultRuleInferrer implements RuleInferrerInterface
{
public function infer(TableDefinition $table, array $options = []): array
{
$exclude = array_map('strtolower', $options['exclude_columns'] ?? []);
$excludeMap = array_fill_keys($exclude, true);
$withScenes = (bool)($options['with_scenes'] ?? false);
$rules = [];
$attributes = [];
foreach ($table->columns as $column) {
if (isset($excludeMap[strtolower($column->name)])) {
continue;
}
if ($column->autoIncrement && !$withScenes) {
// For create/update validators we generally don't validate auto-increment columns (e.g. id).
continue;
}
$ruleParts = $this->inferRuleParts($column);
if ($ruleParts === []) {
continue;
}
$rules[$column->name] = implode('|', $ruleParts);
$comment = trim($column->comment);
if ($comment !== '') {
$attributes[$column->name] = $comment;
}
}
$result = ['rules' => $rules, 'attributes' => $attributes];
if ($withScenes) {
$scenesType = strtolower(trim((string)($options['scenes'] ?? 'crud')));
if ($scenesType !== 'crud') {
throw new \InvalidArgumentException("Unsupported scenes type: {$scenesType}");
}
$result['scenes'] = $this->buildCrudScenes($table, $rules);
}
return $result;
}
/**
* @param array<string, string> $rules
* @return array<string, list<string>>
*/
private function buildCrudScenes(TableDefinition $table, array $rules): array
{
$ruleKeys = array_keys($rules);
$pk = array_values(array_intersect($table->primaryKeyColumns, $ruleKeys));
if ($pk === []) {
throw new \RuntimeException("Cannot generate CRUD scenes: primary key columns not found in rules for table {$table->table}");
}
$nonPk = array_values(array_diff($ruleKeys, $pk));
return [
'create' => $nonPk,
// Full update (PUT semantics) generally requires primary key + full payload.
'update' => array_values(array_merge($pk, $nonPk)),
'delete' => $pk,
'detail' => $pk,
];
}
/**
* @return list<string>
*/
private function inferRuleParts(ColumnDefinition $column): array
{
$parts = [];
if ($this->shouldBeRequired($column)) {
$parts[] = 'required';
} elseif ($column->nullable) {
$parts[] = 'nullable';
}
$typeParts = $this->inferTypeParts($column);
foreach ($typeParts as $p) {
$parts[] = $p;
}
return $parts;
}
private function shouldBeRequired(ColumnDefinition $column): bool
{
if ($column->nullable) {
return false;
}
// If default exists, allow omitting the field.
if ($column->defaultValue !== null) {
return false;
}
return true;
}
/**
* @return list<string>
*/
private function inferTypeParts(ColumnDefinition $column): array
{
$type = strtolower($column->dataType);
if ($column->enumValues !== []) {
// Values containing commas are not representable; keep best-effort.
return ['in:' . implode(',', $column->enumValues)];
}
return match ($type) {
'varchar', 'char' => $this->stringRules($column),
'text', 'tinytext', 'mediumtext', 'longtext' => ['string'],
'int', 'integer', 'tinyint', 'smallint', 'mediumint', 'bigint' => $this->integerRules($column),
'decimal', 'numeric', 'float', 'double' => $this->numericRules($column),
'date', 'datetime', 'timestamp', 'time' => ['date'],
'json' => ['json'],
'bool', 'boolean' => ['boolean'],
default => [],
};
}
/**
* @return list<string>
*/
private function stringRules(ColumnDefinition $column): array
{
$rules = ['string'];
if ($column->characterMaximumLength !== null && $column->characterMaximumLength > 0) {
$rules[] = 'max:' . $column->characterMaximumLength;
}
return $rules;
}
/**
* @return list<string>
*/
private function integerRules(ColumnDefinition $column): array
{
$rules = ['integer'];
if ($column->unsigned) {
$rules[] = 'min:0';
}
return $rules;
}
/**
* @return list<string>
*/
private function numericRules(ColumnDefinition $column): array
{
$rules = ['numeric'];
if ($column->unsigned) {
$rules[] = 'min:0';
}
return $rules;
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
final class ExcludedColumns
{
/**
* @return list<string>
*/
public static function defaultForIlluminate(): array
{
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return list<string>
*/
public static function defaultForThinkOrm(): array
{
// ThinkORM (ThinkPHP) commonly uses *_time fields for timestamps/soft delete.
return ['create_time', 'update_time', 'delete_time'];
}
}
@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
final class OrmDetector
{
public const ORM_AUTO = 'auto';
public const ORM_LARAVEL = 'laravel';
public const ORM_THINKORM = 'thinkorm';
/**
* @return array{laravel: bool, thinkorm: bool}
*/
public function availability(): array
{
return [
'laravel' => $this->isIlluminateAvailable(),
'thinkorm' => $this->isThinkOrmAvailable(),
];
}
public function resolve(string $requestedOrm): string
{
$requestedOrm = strtolower(trim($requestedOrm));
if ($requestedOrm === '' || $requestedOrm === self::ORM_AUTO) {
$availability = $this->availability();
if ($availability['laravel']) {
return self::ORM_LARAVEL;
}
if ($availability['thinkorm']) {
return self::ORM_THINKORM;
}
throw new \RuntimeException('No ORM available. Please install/configure illuminate/database or think-orm.');
}
// Backward compatible alias: `illuminate` => `laravel`
if ($requestedOrm === 'illuminate') {
$requestedOrm = self::ORM_LARAVEL;
}
if ($requestedOrm === self::ORM_LARAVEL) {
if (!$this->isIlluminateAvailable()) {
throw new \RuntimeException('Requested orm=laravel but illuminate/database is not available.');
}
return self::ORM_LARAVEL;
}
if ($requestedOrm === self::ORM_THINKORM) {
if (!$this->isThinkOrmAvailable()) {
throw new \RuntimeException('Requested orm=thinkorm but think-orm is not available.');
}
return self::ORM_THINKORM;
}
throw new \InvalidArgumentException("Unsupported orm value: {$requestedOrm}");
}
private function isIlluminateAvailable(): bool
{
if (!class_exists(\support\Db::class)) {
return false;
}
$database = config('database');
if (!is_array($database)) {
return false;
}
// Follow webman make:model behavior: plugin.* defaults are not treated as local database config.
$default = $database['default'] ?? null;
if (is_string($default) && str_starts_with($default, 'plugin.')) {
return false;
}
$connections = $database['connections'] ?? null;
return is_array($connections) && $connections !== [];
}
private function isThinkOrmAvailable(): bool
{
$thinkorm = config('think-orm') ?: config('thinkorm');
if (!is_array($thinkorm)) {
return false;
}
$default = $thinkorm['default'] ?? null;
if (is_string($default) && str_starts_with($default, 'plugin.')) {
return false;
}
$connections = $thinkorm['connections'] ?? null;
if (!is_array($connections) || $connections === []) {
return false;
}
// Think-orm v2 uses support\think\Db; v1 uses think\facade\Db.
$hasRuntime = class_exists(\support\think\Db::class) || class_exists(\think\facade\Db::class);
if (!$hasRuntime) {
return false;
}
return true;
}
}
@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
final class PhpArrayExporter
{
public static function export(array $value, int $indentLevel = 1): string
{
return self::exportArray($value, $indentLevel);
}
/**
* Export an array for use as a class property initializer.
*
* Expected output style:
* - Empty: []
* - Non-empty:
* [
* 'k' => 'v',
* ]
*/
public static function exportForProperty(array $value, int $propertyIndentLevel = 1): string
{
if ($value === []) {
return '[]';
}
$propertyIndent = str_repeat(' ', $propertyIndentLevel);
$childIndent = str_repeat(' ', $propertyIndentLevel + 1);
$lines = ['['];
foreach ($value as $k => $v) {
$key = is_int($k) ? null : self::exportScalar($k);
if (is_array($v)) {
$exported = self::exportArrayValue($v, $propertyIndentLevel + 1);
} else {
$exported = self::exportScalar($v);
}
if ($key === null) {
$lines[] = "{$childIndent}{$exported},";
} else {
$lines[] = "{$childIndent}{$key} => {$exported},";
}
}
$lines[] = "{$propertyIndent}]";
return implode("\n", $lines);
}
private static function exportArray(array $value, int $indentLevel): string
{
if ($value === []) {
return '[]';
}
$indent = str_repeat(' ', $indentLevel);
$childIndent = str_repeat(' ', $indentLevel + 1);
$lines = ["{$indent}["];
foreach ($value as $k => $v) {
$key = is_int($k) ? null : self::exportScalar($k);
if (is_array($v)) {
$exported = self::exportArray($v, $indentLevel + 1);
} else {
$exported = self::exportScalar($v);
}
if ($key === null) {
$lines[] = "{$childIndent}{$exported},";
} else {
$lines[] = "{$childIndent}{$key} => {$exported},";
}
}
$lines[] = "{$indent}]";
return implode("\n", $lines);
}
/**
* Export an array as a value after `=> `, i.e. first line has no leading indent.
*/
private static function exportArrayValue(array $value, int $closingIndentLevel): string
{
if ($value === []) {
return '[]';
}
$closingIndent = str_repeat(' ', $closingIndentLevel);
$childIndent = str_repeat(' ', $closingIndentLevel + 1);
$lines = ['['];
foreach ($value as $k => $v) {
$key = is_int($k) ? null : self::exportScalar($k);
if (is_array($v)) {
$exported = self::exportArrayValue($v, $closingIndentLevel + 1);
} else {
$exported = self::exportScalar($v);
}
if ($key === null) {
$lines[] = "{$childIndent}{$exported},";
} else {
$lines[] = "{$childIndent}{$key} => {$exported},";
}
}
$lines[] = "{$closingIndent}]";
return implode("\n", $lines);
}
private static function exportScalar(mixed $value): string
{
if ($value === null) {
return 'null';
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
if (is_int($value) || is_float($value)) {
return (string)$value;
}
if (is_string($value)) {
return "'" . str_replace(["\\", "'"], ["\\\\", "\\'"], $value) . "'";
}
// Fallback to string casting for unexpected scalars.
return "'" . str_replace(["\\", "'"], ["\\\\", "\\'"], (string)$value) . "'";
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaIntrospectorInterface;
use Webman\Validation\Command\ValidatorGenerator\Illuminate\MySqlInformationSchemaIntrospector;
use Webman\Validation\Command\ValidatorGenerator\Illuminate\PostgresInformationSchemaIntrospector;
use Webman\Validation\Command\ValidatorGenerator\Illuminate\SqlitePragmaIntrospector;
use Webman\Validation\Command\ValidatorGenerator\Illuminate\SqlServerIntrospector;
final class SchemaIntrospectorFactory
{
public function createForDriver(string $driver): SchemaIntrospectorInterface
{
$driver = strtolower(trim($driver));
return match ($driver) {
'mysql', 'mariadb' => new MySqlInformationSchemaIntrospector(),
'pgsql', 'postgres', 'postgresql' => new PostgresInformationSchemaIntrospector(),
'sqlite' => new SqlitePragmaIntrospector(),
'sqlsrv' => new SqlServerIntrospector(),
default => throw new \RuntimeException("Unsupported database driver: {$driver}"),
};
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
final class ValidatorClassRenderer
{
/**
* @param array<string, string> $rules
* @param array<string, string> $messages
* @param array<string, string> $attributes
* @param array<string, list<string>> $scenes
*/
public function render(
string $namespace,
string $class,
array $rules,
array $messages = [],
array $attributes = [],
array $scenes = [],
): string {
$rulesCode = PhpArrayExporter::exportForProperty($rules, 1);
$messagesCode = PhpArrayExporter::exportForProperty($messages, 1);
$attributesCode = PhpArrayExporter::exportForProperty($attributes, 1);
$scenesCode = PhpArrayExporter::exportForProperty($scenes, 1);
return <<<PHP
<?php
declare(strict_types=1);
namespace {$namespace};
use support\\validation\\Validator;
class {$class} extends Validator
{
protected array \$rules = {$rulesCode};
protected array \$messages = {$messagesCode};
protected array \$attributes = {$attributesCode};
protected array \$scenes = {$scenesCode};
}
PHP;
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\Support;
final class ValidatorFileWriter
{
public function write(string $file, string $content): void
{
$dir = \dirname($file);
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new \RuntimeException("Failed to create directory: {$dir}");
}
if (file_put_contents($file, $content) === false) {
throw new \RuntimeException("Failed to write file: {$file}");
}
}
}
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\ThinkOrm;
use Webman\Validation\Command\ValidatorGenerator\Contracts\ConnectionResolverInterface;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
final class ThinkOrmConnectionResolver implements ConnectionResolverInterface
{
public function resolve(?string $connectionName = null): SchemaConnectionInterface
{
$thinkorm = config('think-orm');
if (!is_array($thinkorm) || $thinkorm === []) {
$alt = config('thinkorm');
$thinkorm = is_array($alt) ? $alt : null;
}
if (!is_array($thinkorm)) {
throw new \RuntimeException('Think-orm config not found: config("think-orm") or config("thinkorm").');
}
$mainConnections = $thinkorm['connections'] ?? null;
if (!is_array($mainConnections) || $mainConnections === []) {
throw new \RuntimeException('Invalid think-orm config: connections must be a non-empty array.');
}
$name = $connectionName;
if ($name === null || trim($name) === '') {
$default = $thinkorm['default'] ?? null;
if (!is_string($default) || trim($default) === '') {
throw new \RuntimeException('Think-orm connection name not provided and think-orm.default is not set.');
}
$name = trim($default);
}
$name = trim((string)$name);
$connKey = $name;
$connections = $mainConnections;
if (str_starts_with($name, 'plugin.')) {
// plugin.<plugin>.<connection>
$parts = explode('.', $name, 3);
$plugin = $parts[1] ?? '';
$conn = $parts[2] ?? '';
$plugin = is_string($plugin) ? trim($plugin) : '';
$conn = is_string($conn) ? trim($conn) : '';
if ($plugin === '' || $conn === '') {
throw new \RuntimeException("Invalid plugin connection name: {$name}");
}
$pluginCfg = config("plugin.$plugin.thinkorm");
if (!is_array($pluginCfg) || $pluginCfg === []) {
$alt = config("plugin.$plugin.think-orm");
$pluginCfg = is_array($alt) ? $alt : [];
}
$pluginConnections = $pluginCfg['connections'] ?? null;
if (is_array($pluginConnections) && $pluginConnections !== []) {
$connections = $pluginConnections;
$connKey = "plugin.$plugin.$conn";
$name = $conn;
} else {
// No plugin think-orm connections: fallback to main project config.
$connections = $mainConnections;
$connKey = $conn;
$name = $conn;
}
}
if (!array_key_exists($name, $connections)) {
$available = implode(', ', array_keys($connections));
throw new \RuntimeException("Think-orm connection not found: {$name}. Available connections: {$available}");
}
/** @var array<string, mixed> $cfg */
$cfg = is_array($connections[$name]) ? $connections[$name] : [];
$driver = (string)($cfg['type'] ?? 'mysql');
$database = isset($cfg['database']) ? (string)$cfg['database'] : null;
$connection = $this->connect($connKey);
return new ThinkOrmSchemaConnection($connection, strtolower($driver), $database);
}
private function connect(string $name): object
{
// Think-orm v2
if (class_exists(\support\think\Db::class)) {
/** @var object $conn */
$conn = \support\think\Db::connect($name);
return $conn;
}
// Think-orm v1
if (class_exists(\think\facade\Db::class)) {
/** @var object $conn */
$conn = \think\facade\Db::connect($name);
return $conn;
}
throw new \RuntimeException('Think-orm is not installed. Missing support\\think\\Db or think\\facade\\Db.');
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Command\ValidatorGenerator\ThinkOrm;
use Webman\Validation\Command\ValidatorGenerator\Contracts\SchemaConnectionInterface;
final class ThinkOrmSchemaConnection implements SchemaConnectionInterface
{
/**
* @param object $connection Think-orm connection instance.
*/
public function __construct(
private readonly object $connection,
private readonly string $driver,
private readonly ?string $database,
) {
}
public function driverName(): string
{
return $this->driver;
}
public function databaseName(): ?string
{
return $this->database;
}
public function select(string $sql, array $bindings = []): array
{
// Think-orm uses `query` for SELECT statements.
if (!method_exists($this->connection, 'query')) {
throw new \RuntimeException('Think-orm connection does not support query().');
}
/** @var mixed $rows */
$rows = $this->connection->query($sql, $bindings);
if (!is_array($rows)) {
return [];
}
$result = [];
foreach ($rows as $row) {
if (is_array($row)) {
$result[] = $row;
continue;
}
if (is_object($row)) {
/** @var array<string, mixed> $arr */
$arr = get_object_vars($row);
$result[] = $arr;
continue;
}
$result[] = ['value' => $row];
}
return $result;
}
}
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Exception;
use Webman\Exception\BusinessException;
class ValidationException extends BusinessException
{
}
@@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Factory;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\FileLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\Factory;
use Illuminate\Validation\DatabasePresenceVerifier;
use Illuminate\Validation\DatabasePresenceVerifierInterface;
use Illuminate\Support\Facades\Facade;
final class ValidationFactory
{
private static ?Factory $factory = null;
private static bool $presenceVerifierBound = false;
public static function getFactory(): Factory
{
if (self::$factory === null) {
self::$factory = self::createFactory();
}
if (!self::$presenceVerifierBound) {
self::bindPresenceVerifier(self::$factory);
}
return self::$factory;
}
private static function createFactory(): Factory
{
$loader = self::createLoader();
$translator = new Translator($loader, self::getLocale());
$fallback = self::getFallbackLocale();
if ($fallback !== '') {
$translator->setFallback($fallback);
}
$factory = new Factory($translator);
self::bindFacadeRoot($factory);
self::bindPresenceVerifier($factory);
return $factory;
}
private static function bindFacadeRoot(Factory $factory): void
{
if (Facade::getFacadeApplication() !== null) {
return;
}
$container = new Container();
$container->instance('validator', $factory);
Facade::setFacadeApplication($container);
}
private static function bindPresenceVerifier(Factory $factory): void
{
try {
if ($factory->getPresenceVerifier() instanceof DatabasePresenceVerifierInterface) {
self::$presenceVerifierBound = true;
return;
}
} catch (\Throwable) {
// Verifier not yet set; continue to try binding.
}
if (!class_exists(DatabasePresenceVerifier::class) || !class_exists(Model::class)) {
self::$presenceVerifierBound = true;
return;
}
$resolver = Model::getConnectionResolver();
if ($resolver === null && class_exists('support\\Model')) {
$resolver = Model::getConnectionResolver();
}
if ($resolver === null) {
return;
}
$factory->setPresenceVerifier(new DatabasePresenceVerifier($resolver));
self::$presenceVerifierBound = true;
}
private static function createLoader(): FileLoader
{
$filesystem = new Filesystem();
$packagePath = self::getPackageTranslationsPath();
$illuminatePath = self::getIlluminateTranslationsPath();
$projectPaths = self::getProjectTranslationsPaths();
$orderedPaths = array_values(array_filter([
$packagePath,
$illuminatePath,
...$projectPaths,
], fn ($path) => $path !== ''));
$basePath = $orderedPaths[0] ?? '';
$loader = new FileLoader($filesystem, $basePath);
foreach ($orderedPaths as $path) {
if ($path !== $basePath) {
$loader->addPath($path);
}
}
return $loader;
}
private static function getLocale(): string
{
if (self::canUseLocaleFunction() && function_exists('locale')) {
$locale = locale();
if (is_string($locale) && $locale !== '') {
return $locale;
}
}
$locale = config('translation.locale', 'en');
return is_string($locale) && $locale !== '' ? $locale : 'en';
}
private static function getFallbackLocale(): string
{
$fallback = config('translation.fallback_locale', []);
if (is_array($fallback)) {
$fallback = $fallback[0] ?? '';
}
return is_string($fallback) ? $fallback : '';
}
private static function getProjectTranslationsPaths(): array
{
$path = config('translation.path', '');
$paths = is_array($path) ? $path : [$path];
$result = [];
foreach ($paths as $item) {
if (is_string($item) && $item !== '' && is_dir($item)) {
$result[] = $item;
}
}
return $result;
}
private static function canUseLocaleFunction(): bool
{
$path = config('translation.path', '');
return is_string($path) && $path !== '' && is_dir($path);
}
private static function getPackageTranslationsPath(): string
{
$path = dirname(__DIR__, 2) . '/resources/lang';
return is_dir($path) ? $path : '';
}
private static function getIlluminateTranslationsPath(): string
{
$path = dirname(__DIR__, 2) . '/vendor/illuminate/translation/lang';
return is_dir($path) ? $path : '';
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace Webman\Validation;
class Install
{
const WEBMAN_PLUGIN = true;
/**
* @var array
*/
protected static array $pathRelation = [
'config/plugin/webman/validation' => 'config/plugin/webman/validation',
];
/**
* Install
* @return void
*/
public static function install()
{
static::installByRelation();
}
/**
* Uninstall
* @return void
*/
public static function uninstall()
{
self::uninstallByRelation();
}
/**
* installByRelation
* @return void
*/
public static function installByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
if ($pos = strrpos($dest, '/')) {
$parent_dir = base_path().'/'.substr($dest, 0, $pos);
if (!is_dir($parent_dir)) {
mkdir($parent_dir, 0777, true);
}
}
//symlink(__DIR__ . "/$source", base_path()."/$dest");
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
echo "Create $dest
";
}
}
/**
* uninstallByRelation
* @return void
*/
public static function uninstallByRelation()
{
foreach (static::$pathRelation as $source => $dest) {
$path = base_path()."/$dest";
if (!is_dir($path) && !is_file($path)) {
continue;
}
echo "Remove $dest
";
if (is_file($path) || is_link($path)) {
unlink($path);
continue;
}
remove_dir($path);
}
}
}
+417
View File
@@ -0,0 +1,417 @@
<?php
declare(strict_types=1);
namespace Webman\Validation;
use Closure;
use InvalidArgumentException;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionUnionType;
use Webman\Http\Request;
use Webman\Validation\Annotation\Param;
use Webman\Validation\Annotation\Validate;
final class Middleware
{
private static array $metadataCache = [];
public function process(Request $request, callable $handler)
{
$metadata = $this->resolveMetadata($request);
if ($metadata === null || !$metadata['has']) {
return $handler($request);
}
$defaultData = $this->getRequestData($request);
$this->handleMethodValidation($request, $metadata['methods'], $defaultData);
$this->handleParamValidation($request, $metadata['params'], $defaultData);
return $handler($request);
}
private function resolveMetadata(Request $request): ?array
{
$controller = $request->controller ?: '';
$action = $request->action ?: '';
if ($controller !== '' && $action !== '' && class_exists($controller)) {
return $this->getMethodMetadata($controller, $action);
}
return $this->getCallableMetadata($request);
}
private function handleMethodValidation(Request $request, array $methods, array $defaultData): void
{
if ($methods === []) {
return;
}
foreach ($methods as $config) {
$data = $this->resolveRequestData($request, $config->in, $defaultData);
$this->validateMethod($config, $data);
}
}
private function handleParamValidation(Request $request, array $params, array $defaultData): void
{
if ($params === []) {
return;
}
$allData = [];
$allRules = [];
$allMessages = [];
$allAttributes = [];
foreach ($params as $item) {
$name = $item['name'];
/** @var \Webman\Validation\Annotation\Param $config */
$config = $item['config'];
$dataForParam = $this->resolveRequestData($request, $config->in, $defaultData);
$value = $dataForParam[$name] ?? null;
if ($value === null && $item['hasDefault']) {
$value = $item['default'];
}
$allData[$name] = $value;
$allRules[$name] = $config->rules;
// 处理 messages,确保 key 带有字段前缀,避免冲突
foreach ($config->messages as $key => $message) {
if (!str_contains($key, '.')) {
// 没有点号的 key 自动添加字段名前缀
$key = $name . '.' . $key;
}
$allMessages[$key] = $message;
}
if ($config->attribute !== '') {
$allAttributes[$name] = $config->attribute;
}
}
Validator::make($allData, $allRules, $allMessages, $allAttributes)->validate();
}
private function validateMethod(Validate $config, array $data): void
{
if ($config->validator !== null) {
if ($config->rules !== []) {
throw new InvalidArgumentException('Validate cannot set both validator and rules.');
}
if (!class_exists($config->validator)) {
throw new InvalidArgumentException("Validator class not found: {$config->validator}");
}
if (!is_subclass_of($config->validator, \Webman\Validation\Validator::class)) {
throw new InvalidArgumentException("Validator must extend Webman\\Validation\\Validator (or support\\validation\\Validator): {$config->validator}");
}
$validator = $config->validator::make($data);
if ($config->scene !== null) {
$validator = $validator->withScene($config->scene);
}
$validator->validate();
return;
}
if ($config->rules === []) {
return;
}
Validator::make($data, $config->rules, $config->messages, $config->attributes)->validate();
}
private function getRequestData(Request $request): array
{
$routeParams = $request->route ? $request->route->param() : [];
if (!is_array($routeParams)) {
$routeParams = [];
}
return array_merge($request->all() ?: [], $routeParams);
}
private function resolveRequestData(Request $request, string|array|null $in, array $defaultData): array
{
if ($in === null || $in === []) {
return $defaultData;
}
$parts = is_array($in) ? $in : [$in];
$data = [];
foreach ($parts as $part) {
$data = array_merge($data, $this->getRequestPartData($request, $part));
}
return $data;
}
private function getRequestPartData(Request $request, mixed $part): array
{
if (!is_string($part) || $part === '') {
throw new InvalidArgumentException('Validate/Param in must be a non-empty string or string array.');
}
return match ($part) {
'query' => $request->get() ?: [],
'body' => $request->post() ?: [],
'path' => $this->getPathParams($request),
default => throw new InvalidArgumentException("Unsupported in value: {$part}. Only query|body|path are supported."),
};
}
private function getPathParams(Request $request): array
{
$routeParams = $request->route ? $request->route->param() : [];
return is_array($routeParams) ? $routeParams : [];
}
private function getMethodMetadata(string $controller, string $action): ?array
{
$key = $controller . '::' . $action;
if (isset(self::$metadataCache[$key])) {
return self::$metadataCache[$key];
}
if (!method_exists($controller, $action)) {
return self::$metadataCache[$key] = null;
}
$reflection = new ReflectionMethod($controller, $action);
return self::$metadataCache[$key] = $this->buildMetadataFromReflection($reflection, true);
}
/**
* Resolve metadata for closure / named-function route handlers.
*/
private function getCallableMetadata(Request $request): ?array
{
$route = $request->route;
if (!$route || !method_exists($route, 'getCallback')) {
return null;
}
$callback = $route->getCallback();
// Only handle closures and named function strings.
if (!$callback instanceof Closure && !is_string($callback)) {
return null;
}
if (is_string($callback) && !function_exists($callback)) {
return null;
}
$cacheKey = is_string($callback)
? 'func::' . $callback
: 'callable::' . $route->getPath();
if (isset(self::$metadataCache[$cacheKey])) {
return self::$metadataCache[$cacheKey];
}
$reflection = new ReflectionFunction($callback);
// Named functions support function-level #[Validate]; closures do not.
$supportsMethodAttributes = is_string($callback);
return self::$metadataCache[$cacheKey] = $this->buildMetadataFromReflection(
$reflection,
$supportsMethodAttributes
);
}
/**
* Build validation metadata from a ReflectionMethod or ReflectionFunction.
*/
private function buildMetadataFromReflection(
ReflectionFunctionAbstract $reflection,
bool $supportsMethodAttributes
): array {
$methods = [];
if ($supportsMethodAttributes) {
foreach ($reflection->getAttributes(Validate::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
$methods[] = $attribute->newInstance();
}
}
$parameters = $reflection->getParameters();
$hasAnyParamAttribute = false;
foreach ($parameters as $parameter) {
if ($parameter->getAttributes(Param::class, \ReflectionAttribute::IS_INSTANCEOF) !== []) {
$hasAnyParamAttribute = true;
break;
}
}
$inferWhenAnnotationsPresent = $methods !== [] || $hasAnyParamAttribute;
$params = [];
foreach ($parameters as $parameter) {
$paramConfig = $this->resolveParamConfig($parameter, $inferWhenAnnotationsPresent);
if ($paramConfig === null) {
continue;
}
$hasDefault = $parameter->isDefaultValueAvailable();
$params[] = [
'name' => $parameter->getName(),
'config' => $paramConfig,
'hasDefault' => $hasDefault,
'default' => $hasDefault ? $parameter->getDefaultValue() : null,
];
}
return [
'has' => $methods !== [] || $params !== [],
'methods' => $methods,
'params' => $params,
];
}
private function resolveParamConfig(ReflectionParameter $parameter, bool $inferWhenAnnotationsPresent): ?Param
{
$attributes = $parameter->getAttributes(Param::class, \ReflectionAttribute::IS_INSTANCEOF);
if ($attributes !== []) {
/** @var Param $config */
$config = $attributes[0]->newInstance();
// Auto-complete rules based on parameter signature.
$completedRules = $this->completeRulesFromParameter($parameter, $config->rules);
if ($completedRules !== $config->rules) {
return new Param(
rules: $completedRules,
messages: $config->messages,
attribute: $config->attribute,
in: $config->in
);
}
return $config;
}
if (!$inferWhenAnnotationsPresent) {
return null;
}
if ($this->shouldSkipParameter($parameter)) {
return null;
}
$rules = $this->inferRulesFromParameter($parameter);
return new Param(rules: $rules);
}
private function shouldSkipParameter(ReflectionParameter $parameter): bool
{
$type = $parameter->getType();
if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) {
return false;
}
$name = $type->getName();
if ($name === '') {
return true;
}
// Skip framework request injection.
if (is_a($name, Request::class, true)) {
return true;
}
// Skip other class-typed parameters by default (services/DTOs/etc).
return true;
}
private function inferRulesFromParameter(ReflectionParameter $parameter): string|array
{
$rules = [];
$type = $parameter->getType();
$isNullable = $type instanceof ReflectionNamedType && $type->allowsNull();
// Required when: no default value AND not nullable.
if (!$parameter->isDefaultValueAvailable() && !$isNullable) {
$rules[] = 'required';
}
if ($type instanceof ReflectionUnionType) {
// Union types are not inferred by default (developer can explicitly use #[Param]).
return implode('|', $rules);
}
if ($type instanceof ReflectionNamedType && $type->isBuiltin()) {
$mapped = $this->mapBuiltinTypeToRule($type->getName());
if ($mapped !== '') {
$rules[] = $mapped;
}
if ($isNullable) {
$rules[] = 'nullable';
}
}
return implode('|', $rules);
}
private function completeRulesFromParameter(ReflectionParameter $parameter, string|array $existingRules): string|array
{
$isArray = is_array($existingRules);
$rulesList = $isArray
? $existingRules
: ($existingRules !== '' ? explode('|', $existingRules) : []);
$type = $parameter->getType();
$isNullable = $type instanceof ReflectionNamedType && $type->allowsNull();
// Build rule name set for O(1) lookups instead of iterating per check.
$ruleNames = [];
foreach ($rulesList as $rule) {
$ruleNames[explode(':', $rule, 2)[0]] = true;
}
// Auto-complete 'required' if: no default value, not nullable, and not already present.
if (!$parameter->isDefaultValueAvailable() && !$isNullable && !isset($ruleNames['required'])) {
array_unshift($rulesList, 'required');
$ruleNames['required'] = true;
}
// Auto-complete type rule if: has builtin type and no type rule present.
if ($type instanceof ReflectionNamedType && $type->isBuiltin()) {
$mappedRule = $this->mapBuiltinTypeToRule($type->getName());
if ($mappedRule !== '' && !isset($ruleNames[$mappedRule])) {
// Insert type rule after 'required' if present, otherwise at the beginning.
$requiredIndex = array_search('required', $rulesList, true);
if ($requiredIndex !== false) {
array_splice($rulesList, $requiredIndex + 1, 0, $mappedRule);
} else {
array_unshift($rulesList, $mappedRule);
}
$ruleNames[$mappedRule] = true;
}
// Auto-complete 'nullable' if: type is nullable and not already present.
if ($isNullable && !isset($ruleNames['nullable'])) {
$rulesList[] = 'nullable';
}
}
return $isArray ? $rulesList : implode('|', $rulesList);
}
private function mapBuiltinTypeToRule(string $type): string
{
return match ($type) {
'string' => 'string',
'int' => 'integer',
'float' => 'numeric',
'bool' => 'boolean',
'array' => 'array',
default => '',
};
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Tests;
use Illuminate\Validation\Rule as IlluminateRule;
use Illuminate\Validation\Rules\In;
use PHPUnit\Framework\TestCase;
use support\validation\Rule;
use support\validation\Validator;
use support\validation\ValidationException;
final class RuleTest extends TestCase
{
public function testRuleExtendsIlluminateRule(): void
{
$this->assertTrue(is_subclass_of(Rule::class, IlluminateRule::class));
}
public function testRuleInProxy(): void
{
$rule = Rule::in(['a', 'b']);
$this->assertInstanceOf(In::class, $rule);
}
public function testRuleInPassesValidation(): void
{
$validated = Validator::make(
['status' => 'enabled'],
['status' => ['required', Rule::in(['enabled', 'disabled'])]]
)->validate();
$this->assertSame(['status' => 'enabled'], $validated);
}
public function testRuleInFailsValidation(): void
{
try {
Validator::make(
['status' => 'unknown'],
['status' => ['required', Rule::in(['enabled', 'disabled'])]],
['status.in' => 'Status invalid']
)->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('Status invalid', $exception->getMessage());
}
}
public function testRuleAnyOfPassesValidation(): void
{
$validated = Validator::make(
['username' => 'user@example.com'],
['username' => [
'required',
Rule::anyOf([
['string', 'email'],
['string', 'alpha_dash', 'min:6'],
]),
]]
)->validate();
$this->assertSame(['username' => 'user@example.com'], $validated);
}
public function testRuleAnyOfFailsValidation(): void
{
try {
Validator::make(
['username' => 'a?'],
['username' => [
'required',
Rule::anyOf([
['string', 'email'],
['string', 'alpha_dash', 'min:6'],
]),
]],
['username.any_of' => 'Username invalid']
)->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('Username invalid', $exception->getMessage());
}
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Tests;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
use Webman\Validation\Factory\ValidationFactory;
use support\validation\Validator;
use support\validation\ValidationException;
final class TranslationTest extends TestCase
{
/**
* @return array<string, array{string, string}>
*/
public static function localeEmailMessageProvider(): array
{
return [
'zh_CN' => ['zh_CN', '必须是有效的邮箱地址'],
'zh_TW' => ['zh_TW', '必須是有效的電子郵件地址'],
'en' => ['en', 'must be a valid email address'],
'ja' => ['ja', '有効なメールアドレスである必要があります'],
'ko' => ['ko', '유효한 이메일 주소여야 합니다'],
'fr' => ['fr', 'doit être une adresse e-mail valide'],
'de' => ['de', 'muss eine gültige E-Mail-Adresse sein'],
'es' => ['es', 'debe ser una dirección de correo electrónico válida'],
'pt_BR' => ['pt_BR', 'deve ser um endereço de e-mail válido'],
'ru' => ['ru', 'должно быть действительным адресом электронной почты'],
'vi' => ['vi', 'phải là địa chỉ email hợp lệ'],
'tr' => ['tr', 'geçerli bir e-posta adresi olmalıdır'],
'id' => ['id', 'harus berupa alamat email yang valid'],
'th' => ['th', 'ต้องเป็นที่อยู่อีเมลที่ถูกต้อง'],
];
}
#[DataProvider('localeEmailMessageProvider')]
public function testValidationMessageInLocale(string $locale, string $expectedSubstring): void
{
$packageLangPath = dirname(__DIR__, 2) . '/resources/lang';
if (!is_dir($packageLangPath) || !is_dir($packageLangPath . '/' . $locale)) {
$this->markTestSkipped("Locale {$locale} not found in package resources/lang.");
}
validation_test_set_config([
'translation' => [
'path' => $packageLangPath,
'locale' => $locale,
'fallback_locale' => [$locale],
],
]);
if (function_exists('locale')) {
locale($locale);
}
$this->resetTranslationInstance();
$this->resetFactory();
try {
Validator::make(['email' => 'bad-email'], ['email' => 'required|email'])->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertStringContainsString(
$expectedSubstring,
$exception->getMessage(),
"Locale {$locale}: expected message to contain '{$expectedSubstring}'"
);
}
}
public function testLocalTranslationsOverridePackage(): void
{
if (!class_exists(\Symfony\Component\Translation\Translator::class)) {
$this->markTestSkipped('symfony/translation is not installed.');
}
validation_test_set_config([
'translation' => [
'path' => $this->fixturePath('translations'),
'locale' => 'zh_CN',
'fallback_locale' => ['zh_CN'],
],
]);
if (function_exists('locale')) {
locale('zh_CN');
}
$this->resetTranslationInstance();
$this->resetFactory();
try {
Validator::make(['email' => 'bad-email'], ['email' => 'required|email'])->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('[LOCAL_ZH] email invalid.', $exception->getMessage());
}
}
public function testFallbackToPackageTranslations(): void
{
validation_test_set_config([
'translation' => [
'path' => $this->fixturePath('empty'),
'locale' => 'en',
'fallback_locale' => ['en'],
],
]);
if (function_exists('locale')) {
locale('en');
}
$this->resetTranslationInstance();
$this->resetFactory();
try {
Validator::make(['email' => 'bad-email'], ['email' => 'required|email'])->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('The email field must be a valid email address.', $exception->getMessage());
}
}
private function resetFactory(): void
{
$property = new ReflectionProperty(ValidationFactory::class, 'factory');
$property->setAccessible(true);
$property->setValue(null, null);
}
private function resetTranslationInstance(): void
{
if (!class_exists(\support\Translation::class)) {
return;
}
$property = new ReflectionProperty(\support\Translation::class, 'instance');
$property->setAccessible(true);
$property->setValue(null, []);
}
private function fixturePath(string $name): string
{
return __DIR__ . '/fixtures/' . $name;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Tests;
use PHPUnit\Framework\TestCase;
use support\validation\Validator as SupportValidator;
use support\validation\ValidationException as SupportValidationException;
use Webman\Validation\Validator as CoreValidator;
final class ValidatorExceptionTest extends TestCase
{
public function test_core_validator_uses_support_validation_exception_by_default(): void
{
$this->expectException(SupportValidationException::class);
CoreValidator::make(
['name' => null],
['name' => 'required']
)->validate();
}
public function test_support_validator_uses_support_validation_exception_by_default(): void
{
$this->expectException(SupportValidationException::class);
SupportValidator::make(
['name' => null],
['name' => 'required']
)->validate();
}
public function test_exception_override_uses_custom_exception(): void
{
$this->expectException(\RuntimeException::class);
CoreValidator::make(
['name' => null],
['name' => 'required']
)
->withException(\RuntimeException::class)
->validate();
}
}
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Tests;
use PHPUnit\Framework\TestCase;
use support\validation\ValidationException;
use support\validation\Validator as SupportValidator;
final class ValidatorPublicMethodsTest extends TestCase
{
public function testOverrideRulesMethodIsUsed(): void
{
$validator = OverrideRulesValidator::make(['email' => 'not-an-email']);
$this->expectException(ValidationException::class);
$validator->validate();
}
public function testOverrideMessagesMethodIsUsed(): void
{
$validator = OverrideMessagesValidator::make([]);
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('Custom required message');
$validator->validate();
}
public function testOverrideAttributesMethodIsUsed(): void
{
$validator = OverrideAttributesValidator::make([]);
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('E-mail is required');
$validator->validate();
}
public function testOverrideDataMethodIsUsed(): void
{
$validated = OverrideDataValidator::make(['email' => 'not-an-email'])->validate();
$this->assertSame(['email' => 'user@example.com'], $validated);
}
public function testReuseRulesFromAnotherValidatorViaPublicMethod(): void
{
$validated = ReuseRulesValidator::make(['email' => 'user@example.com'])->validate();
$this->assertSame(['email' => 'user@example.com'], $validated);
}
}
final class OverrideRulesValidator extends SupportValidator
{
protected array $rules = [
'dummy' => 'nullable',
];
public function rules(): array
{
return [
'email' => 'required|email',
];
}
}
final class OverrideMessagesValidator extends SupportValidator
{
protected array $rules = [
'email' => 'required',
];
public function messages(): array
{
return [
'email.required' => 'Custom required message',
];
}
}
final class OverrideAttributesValidator extends SupportValidator
{
protected array $rules = [
'email' => 'required',
];
public function messages(): array
{
return [
'email.required' => ':attribute is required',
];
}
public function attributes(): array
{
return [
'email' => 'E-mail',
];
}
}
final class OverrideDataValidator extends SupportValidator
{
protected array $rules = [
'email' => 'required|email',
];
public function data(): array
{
return [
'email' => 'user@example.com',
];
}
}
final class RulesSourceValidator extends SupportValidator
{
protected array $rules = [
'email' => 'required|email',
];
}
final class ReuseRulesValidator extends SupportValidator
{
protected array $rules = [
'dummy' => 'nullable',
];
public function rules(): array
{
return RulesSourceValidator::make($this->data)->rules();
}
}
+179
View File
@@ -0,0 +1,179 @@
<?php
declare(strict_types=1);
namespace Webman\Validation\Tests;
use PHPUnit\Framework\TestCase;
use support\validation\Validator;
use support\validation\ValidationException;
use Webman\Validation\Exception\ValidationException as BaseValidationException;
final class ValidatorTest extends TestCase
{
public function testValidatePassReturnsValidatedData(): void
{
$validated = Validator::make(
['email' => 'user@example.com'],
['email' => 'required|email']
)->validate();
$this->assertSame(['email' => 'user@example.com'], $validated);
}
public function testValidateFailThrowsConfiguredExceptionWithFirstMessage(): void
{
try {
Validator::make(
['email' => 'not-an-email'],
['email' => 'required|email'],
['email.email' => 'Email invalid']
)->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('Email invalid', $exception->getMessage());
}
}
public function testValidateFailUsesConfigExceptionClass(): void
{
$this->setValidationExceptionConfig(ConfigValidationException::class);
try {
Validator::make(
['email' => 'not-an-email'],
['email' => 'required|email']
)->validate();
$this->fail('Expected ConfigValidationException was not thrown.');
} catch (ConfigValidationException $exception) {
$this->assertSame('The email field must be a valid email address.', $exception->getMessage());
} finally {
$this->setValidationExceptionConfig(ValidationException::class);
}
}
public function testCustomValidatorWithoutScenesUsesAllRulesByDefault(): void
{
$validated = UserValidatorWithoutScenes::make([
'email' => 'user@example.com',
])->validate();
$this->assertSame(['email' => 'user@example.com'], $validated);
}
// ───── Closure as validation rule tests ─────
public function testClosureRuleValidationPasses(): void
{
$validated = Validator::make(
['age' => 20],
['age' => ['required', 'integer', function ($attribute, $value, $fail) {
if ($value < 18) {
$fail("The {$attribute} must be at least 18.");
}
}]]
)->validate();
$this->assertSame(['age' => 20], $validated);
}
public function testClosureRuleValidationFails(): void
{
try {
Validator::make(
['age' => 15],
['age' => ['required', 'integer', function ($attribute, $value, $fail) {
if ($value < 18) {
$fail("The {$attribute} must be at least 18.");
}
}]]
)->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertSame('The age must be at least 18.', $exception->getMessage());
}
}
public function testMultipleClosureRulesValidation(): void
{
$validated = Validator::make(
['code' => 'prefix_abc'],
['code' => ['required', 'string',
function ($attribute, $value, $fail) {
if (!str_starts_with($value, 'prefix_')) {
$fail("The {$attribute} must start with prefix_.");
}
},
function ($attribute, $value, $fail) {
if (strlen($value) < 5) {
$fail("The {$attribute} must be at least 5 characters.");
}
},
]]
)->validate();
$this->assertSame(['code' => 'prefix_abc'], $validated);
}
public function testClosureRuleInCustomValidator(): void
{
$validated = ClosureRuleValidator::make(['score' => 85])->validate();
$this->assertSame(['score' => 85], $validated);
}
public function testClosureRuleInCustomValidatorFails(): void
{
try {
ClosureRuleValidator::make(['score' => 150])->validate();
$this->fail('Expected ValidationException was not thrown.');
} catch (ValidationException $exception) {
$this->assertStringContainsString('score', $exception->getMessage());
}
}
// ───── Helpers ─────
private function setValidationExceptionConfig(string $exceptionClass): void
{
validation_test_set_config([
'plugin' => [
'webman' => [
'validation' => [
'app' => [
'exception' => $exceptionClass,
],
],
],
],
]);
}
}
final class ConfigValidationException extends BaseValidationException
{
}
final class UserValidatorWithoutScenes extends Validator
{
protected array $rules = [
'email' => 'required|email',
];
}
final class ClosureRuleValidator extends Validator
{
protected array $rules = [
'score' => 'required|integer',
];
public function rules(): array
{
return [
'score' => ['required', 'integer', function ($attribute, $value, $fail) {
if ($value < 0 || $value > 100) {
$fail("The {$attribute} must be between 0 and 100.");
}
}],
];
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
if (!function_exists('validation_test_set_config')) {
function validation_test_set_config(array $overrides = []): void
{
$defaults = [
'container' => new \Webman\Container(),
'translation' => [
'locale' => 'en',
'fallback_locale' => ['en'],
'path' => '',
],
'plugin' => [
'webman' => [
'validation' => [
'app' => [
'enable' => true,
'exception' => \support\validation\ValidationException::class,
],
],
],
],
];
$current = $GLOBALS['validation_test_config'] ?? [];
$merged = array_replace_recursive($defaults, $current, $overrides);
$GLOBALS['validation_test_config'] = $merged;
if (class_exists(\Webman\Config::class)) {
$property = new ReflectionProperty(\Webman\Config::class, 'config');
$property->setAccessible(true);
$property->setValue(null, $merged);
}
}
}
validation_test_set_config();
if (!function_exists('config')) {
function config(string $key, mixed $default = null): mixed
{
$config = $GLOBALS['validation_test_config'] ?? [];
if ($key === null || $key === '') {
return $config;
}
$parts = explode('.', $key);
$value = $config;
foreach ($parts as $part) {
if (!is_array($value) || !array_key_exists($part, $value)) {
return $default;
}
$value = $value[$part];
}
return $value;
}
}
@@ -0,0 +1 @@
@@ -0,0 +1,5 @@
<?php
return [
'email' => '[LOCAL_EN] :attribute invalid.',
];
@@ -0,0 +1,5 @@
<?php
return [
'email' => '[LOCAL_ZH] :attribute invalid.',
];
+216
View File
@@ -0,0 +1,216 @@
<?php
declare(strict_types=1);
namespace Webman\Validation;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\Validator as IlluminateValidator;
use InvalidArgumentException;
use support\Container;
use Throwable;
use Webman\Validation\Factory\ValidationFactory;
class Validator
{
public static function make(
array $data,
?array $rules = null,
?array $messages = null,
?array $attributes = null
): static {
/** @var static $instance */
$instance = Container::make(static::class);
$instance->data = $data;
$instance->rules = $rules ?? $instance->rules;
$instance->messages = $messages ?? $instance->messages;
$instance->attributes = $attributes ?? $instance->attributes;
if ($instance->rules === []) {
throw new InvalidArgumentException('Validation rules cannot be empty.');
}
return $instance;
}
protected array $rules = [];
protected array $messages = [];
protected array $attributes = [];
protected array $scenes = [];
protected array $data = [];
protected ?string $scene = null;
private ?IlluminateValidator $validator = null;
private ?string $exceptionClass = null;
private static array $validatedExceptionClasses = [];
public function withScene(string $scene): static
{
$clone = clone $this;
$clone->scene = $scene;
$clone->validator = null;
return $clone;
}
public function withException(string $exceptionClass): static
{
if ($exceptionClass === '') {
throw new InvalidArgumentException('Validation exception must be a non-empty class string.');
}
$clone = clone $this;
$clone->exceptionClass = $exceptionClass;
$clone->validator = null;
return $clone;
}
public function validate(): array
{
$validator = $this->toIlluminate();
if ($validator->fails()) {
$exceptionClass = $this->resolveExceptionClass();
$message = $validator->errors()->first() ?: 'Validation failed';
throw new $exceptionClass($message, 400);
}
return $validator->validated();
}
public function passes(): bool
{
return $this->toIlluminate()->passes();
}
public function fails(): bool
{
return $this->toIlluminate()->fails();
}
public function errors(): MessageBag
{
return $this->toIlluminate()->errors();
}
public function validated(): array
{
return $this->toIlluminate()->validated();
}
public function toIlluminate(): IlluminateValidator
{
if ($this->validator !== null) {
return $this->validator;
}
$factory = ValidationFactory::getFactory();
$this->validator = $factory->make(
$this->data(),
$this->rules(),
$this->messages(),
$this->attributes()
);
return $this->validator;
}
/**
* Override this in subclasses to build validation rules dynamically.
*/
public function rules(): array
{
return $this->resolveRules();
}
/**
* Override this in subclasses to build custom messages dynamically.
*/
public function messages(): array
{
return $this->messages;
}
/**
* Override this in subclasses to build custom attribute names dynamically.
*/
public function attributes(): array
{
return $this->attributes;
}
/**
* Expose incoming validation data for subclasses.
*/
public function data(): array
{
return $this->data;
}
/**
* Expose current scene for subclasses.
*/
protected function scene(): ?string
{
return $this->scene;
}
public function __call(string $name, array $arguments): mixed
{
$validator = $this->toIlluminate();
if (!method_exists($validator, $name)) {
throw new InvalidArgumentException("Validator method not found: {$name}");
}
return $validator->{$name}(...$arguments);
}
private function resolveRules(): array
{
$scene = $this->scene;
if ($scene === null) {
return $this->rules;
}
if (!array_key_exists($scene, $this->scenes)) {
throw new InvalidArgumentException("Validation scene not defined: {$scene}");
}
$fields = $this->scenes[$scene];
if (!is_array($fields) || $fields === []) {
throw new InvalidArgumentException("Validation scene has no fields: {$scene}");
}
$rules = array_intersect_key($this->rules, array_flip($fields));
if ($rules === []) {
throw new InvalidArgumentException("Validation rules not found for scene: {$scene}");
}
return $rules;
}
private function resolveExceptionClass(): string
{
$exceptionClass = $this->exceptionClass;
if ($exceptionClass === null) {
$exceptionClass = config(
'plugin.webman.validation.app.exception',
\support\validation\ValidationException::class
);
}
if (!is_string($exceptionClass) || $exceptionClass === '') {
throw new InvalidArgumentException('Validation exception must be a non-empty class string.');
}
// Cache validation result per class name to avoid repeated reflection checks.
if (isset(self::$validatedExceptionClasses[$exceptionClass])) {
return $exceptionClass;
}
if (!class_exists($exceptionClass)) {
throw new InvalidArgumentException("Validation exception class not found: {$exceptionClass}");
}
if (!is_subclass_of($exceptionClass, Throwable::class)) {
throw new InvalidArgumentException("Validation exception must implement Throwable: {$exceptionClass}");
}
self::$validatedExceptionClasses[$exceptionClass] = true;
return $exceptionClass;
}
}
@@ -0,0 +1,8 @@
<?php
use support\validation\ValidationException;
return [
'enable' => true,
'exception' => ValidationException::class,
];
@@ -0,0 +1,7 @@
<?php
use Webman\Validation\Command\MakeValidatorCommand;
return [
MakeValidatorCommand::class
];
@@ -0,0 +1,9 @@
<?php
use Webman\Validation\Middleware;
return [
'@' => [
Middleware::class,
],
];
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="../../autoload.php"
colors="true"
verbose="true">
<testsuites>
<testsuite name="Validation Test Suite">
<directory>Tests</directory>
</testsuite>
</testsuites>
</phpunit>
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace support\validation;
use Illuminate\Validation\Rule as IlluminateRule;
class Rule extends IlluminateRule
{
}
@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace support\validation;
class ValidationException extends \Webman\Validation\Exception\ValidationException
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace support\validation;
class Validator extends \Webman\Validation\Validator
{
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace support\validation\annotation;
use Attribute;
#[Attribute(Attribute::TARGET_PARAMETER)]
class Param extends \Webman\Validation\Annotation\Param
{
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace support\validation\annotation;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Validate extends \Webman\Validation\Annotation\Validate
{
}