Skip to content

Commit daca692

Browse files
Form Translation (#616)
* Form Translation * Support for other languages * Support locale for datepicker * Apply translation on select input * Apply translation on select input * Improve translation --------- Co-authored-by: Julien Nahum <julien@nahum.net>
1 parent c927a23 commit daca692

36 files changed

+1589
-32
lines changed

api/app/Http/Requests/AnswerFormRequest.php

+6
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,12 @@ private function getSelectPropertyOptions($property): array
252252

253253
protected function prepareForValidation()
254254
{
255+
// Set locale based on form language
256+
if ($this->form?->language && in_array($this->form->language, Form::LANGUAGES)) {
257+
app()->setLocale($this->form->language);
258+
}
259+
260+
255261
$receivedData = $this->toArray();
256262
$mergeData = [];
257263
$countryCodeMapper = json_decode(file_get_contents(resource_path('data/country_code_mapper.json')), true);

api/app/Http/Requests/UserFormRequest.php

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function rules()
2929
'visibility' => ['required', Rule::in(Form::VISIBILITY)],
3030

3131
// Customization
32+
'language' => ['required', Rule::in(Form::LANGUAGES)],
3233
'font_family' => 'string|nullable',
3334
'theme' => ['required', Rule::in(Form::THEMES)],
3435
'width' => ['required', Rule::in(Form::WIDTHS)],

api/app/Http/Resources/FormResource.php

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ private function getProtectedForm()
7070
'dark_mode' => $this->dark_mode,
7171
'transparent_background' => $this->transparent_background,
7272
'color' => $this->color,
73+
'language' => $this->language,
7374
'theme' => $this->theme,
7475
'is_password_protected' => true,
7576
'has_password' => $this->has_password,

api/app/Models/Forms/Form.php

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class Form extends Model implements CachableAttributes
4141

4242
public const VISIBILITY = ['public', 'draft', 'closed'];
4343

44+
public const LANGUAGES = ['en', 'fr', 'hi', 'es', 'ar', 'zh', 'ja'];
45+
4446
protected $fillable = [
4547
'workspace_id',
4648
'creator_id',
@@ -52,6 +54,7 @@ class Form extends Model implements CachableAttributes
5254
'visibility',
5355

5456
// Customization
57+
'language',
5558
'font_family',
5659
'custom_domain',
5760
'size',

api/config/app.php

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@
9494

9595
'locales' => [
9696
'en' => 'EN',
97+
'fr' => 'FR',
98+
'hi' => 'HI',
99+
'es' => 'ES',
100+
'ar' => 'AR',
101+
'zh' => 'ZH',
102+
'ja' => 'JA',
97103
],
98104

99105
/*

api/database/factories/FormFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function definition()
5050
'title' => $this->faker->text(30),
5151
'description' => $this->faker->randomHtml(1),
5252
'visibility' => 'public',
53+
'language' => 'en',
5354
'theme' => $this->faker->randomElement(Form::THEMES),
5455
'size' => $this->faker->randomElement(Form::SIZES),
5556
'border_radius' => $this->faker->randomElement(Form::BORDER_RADIUS),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class () extends Migration {
8+
/**
9+
* Run the migrations.
10+
*/
11+
public function up(): void
12+
{
13+
Schema::table('forms', function (Blueprint $table) {
14+
$table->string('language')->default('en');
15+
});
16+
}
17+
18+
/**
19+
* Reverse the migrations.
20+
*/
21+
public function down(): void
22+
{
23+
Schema::table('forms', function (Blueprint $table) {
24+
$table->dropColumn('language');
25+
});
26+
}
27+
};

api/resources/lang/ar/validation.php

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
return [
4+
'accepted' => 'يجب قبول :attribute.',
5+
'active_url' => ':attribute لا يُمثل رابطًا صحيحًا.',
6+
'after' => 'يجب على :attribute أن يكون تاريخًا لاحقًا للتاريخ :date.',
7+
'after_or_equal' => ':attribute يجب أن يكون تاريخاً لاحقاً أو مطابقاً للتاريخ :date.',
8+
'alpha' => 'يجب أن لا يحتوي :attribute سوى على حروف.',
9+
'alpha_dash' => 'يجب أن لا يحتوي :attribute سوى على حروف، أرقام ومطّات.',
10+
'alpha_num' => 'يجب أن يحتوي :attribute على حروفٍ وأرقامٍ فقط.',
11+
'array' => 'يجب أن يكون :attribute ًمصفوفة.',
12+
'before' => 'يجب على :attribute أن يكون تاريخًا سابقًا للتاريخ :date.',
13+
'before_or_equal' => ':attribute يجب أن يكون تاريخا سابقا أو مطابقا للتاريخ :date.',
14+
'between' => [
15+
'numeric' => 'يجب أن تكون قيمة :attribute بين :min و :max.',
16+
'file' => 'يجب أن يكون حجم الملف :attribute بين :min و :max كيلوبايت.',
17+
'string' => 'يجب أن يكون عدد حروف النّص :attribute بين :min و :max.',
18+
'array' => 'يجب أن يحتوي :attribute على عدد من العناصر بين :min و :max.',
19+
],
20+
'boolean' => 'يجب أن تكون قيمة :attribute إما true أو false.',
21+
'confirmed' => 'حقل التأكيد غير مُطابق للحقل :attribute.',
22+
'date' => ':attribute ليس تاريخًا صحيحًا.',
23+
'date_equals' => 'يجب أن يكون :attribute مطابقاً للتاريخ :date.',
24+
'date_format' => 'لا يتوافق :attribute مع الشكل :format.',
25+
'different' => 'يجب أن يكون الحقلان :attribute و :other مُختلفين.',
26+
'digits' => 'يجب أن يحتوي :attribute على :digits رقمًا/أرقام.',
27+
'digits_between' => 'يجب أن يحتوي :attribute بين :min و :max رقمًا/أرقام.',
28+
'dimensions' => 'الـ :attribute يحتوي على أبعاد صورة غير صالحة.',
29+
'distinct' => 'للحقل :attribute قيمة مُكرّرة.',
30+
'email' => 'يجب أن يكون :attribute عنوان بريد إلكتروني صحيح البُنية.',
31+
'ends_with' => 'يجب أن ينتهي :attribute بأحد القيم التالية: :values',
32+
'exists' => 'القيمة المحددة :attribute غير موجودة.',
33+
'file' => 'الـ :attribute يجب أن يكون ملفا.',
34+
'filled' => ':attribute إجباري.',
35+
'gt' => [
36+
'numeric' => 'يجب أن تكون قيمة :attribute أكبر من :value.',
37+
'file' => 'يجب أن يكون حجم الملف :attribute أكبر من :value كيلوبايت.',
38+
'string' => 'يجب أن يكون طول النّص :attribute أكثر من :value حروفٍ/حرفًا.',
39+
'array' => 'يجب أن يحتوي :attribute على أكثر من :value عناصر/عنصر.',
40+
],
41+
'gte' => [
42+
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أكبر من :value.',
43+
'file' => 'يجب أن يكون حجم الملف :attribute على الأقل :value كيلوبايت.',
44+
'string' => 'يجب أن يكون طول النص :attribute على الأقل :value حروفٍ/حرفًا.',
45+
'array' => 'يجب أن يحتوي :attribute على الأقل على :value عُنصرًا/عناصر.',
46+
],
47+
'image' => 'يجب أن يكون :attribute صورةً.',
48+
'in' => ':attribute غير موجود.',
49+
'in_array' => ':attribute غير موجود في :other.',
50+
'integer' => 'يجب أن يكون :attribute عددًا صحيحًا.',
51+
'ip' => 'يجب أن يكون :attribute عنوان IP صحيحًا.',
52+
'ipv4' => 'يجب أن يكون :attribute عنوان IPv4 صحيحًا.',
53+
'ipv6' => 'يجب أن يكون :attribute عنوان IPv6 صحيحًا.',
54+
'json' => 'يجب أن يكون :attribute نصًا من نوع JSON.',
55+
'lt' => [
56+
'numeric' => 'يجب أن تكون قيمة :attribute أصغر من :value.',
57+
'file' => 'يجب أن يكون حجم الملف :attribute أصغر من :value كيلوبايت.',
58+
'string' => 'يجب أن يكون طول النّص :attribute أقل من :value حروفٍ/حرفًا.',
59+
'array' => 'يجب أن يحتوي :attribute على أقل من :value عناصر/عنصر.',
60+
],
61+
'lte' => [
62+
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أصغر من :value.',
63+
'file' => 'يجب أن لا يتجاوز حجم الملف :attribute :value كيلوبايت.',
64+
'string' => 'يجب أن لا يتجاوز طول النّص :attribute :value حروفٍ/حرفًا.',
65+
'array' => 'يجب أن لا يحتوي :attribute على أكثر من :value عناصر/عنصر.',
66+
],
67+
'max' => [
68+
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أصغر من :max.',
69+
'file' => 'يجب أن لا يتجاوز حجم الملف :attribute :max كيلوبايت.',
70+
'string' => 'يجب أن لا يتجاوز طول النّص :attribute :max حروفٍ/حرفًا.',
71+
'array' => 'يجب أن لا يحتوي :attribute على أكثر من :max عناصر/عنصر.',
72+
],
73+
'mimes' => 'يجب أن يكون ملفًا من نوع : :values.',
74+
'mimetypes' => 'يجب أن يكون ملفًا من نوع : :values.',
75+
'min' => [
76+
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أكبر من :min.',
77+
'file' => 'يجب أن يكون حجم الملف :attribute على الأقل :min كيلوبايت.',
78+
'string' => 'يجب أن يكون طول النص :attribute على الأقل :min حروفٍ/حرفًا.',
79+
'array' => 'يجب أن يحتوي :attribute على الأقل على :min عُنصرًا/عناصر.',
80+
],
81+
'multiple_of' => ':attribute يجب أن يكون من مضاعفات :value',
82+
'not_in' => 'العنصر :attribute غير صحيح.',
83+
'not_regex' => 'صيغة :attribute غير صحيحة.',
84+
'numeric' => 'يجب على :attribute أن يكون رقمًا.',
85+
'password' => 'كلمة المرور غير صحيحة.',
86+
'present' => 'يجب تقديم :attribute.',
87+
'regex' => 'صيغة :attribute غير صحيحة.',
88+
'required' => ':attribute مطلوب.',
89+
'required_if' => ':attribute مطلوب في حال ما إذا كان :other يساوي :value.',
90+
'required_unless' => ':attribute مطلوب في حال ما لم يكن :other يساوي :values.',
91+
'required_with' => ':attribute مطلوب إذا توفّر :values.',
92+
'required_with_all' => ':attribute مطلوب إذا توفّر :values.',
93+
'required_without' => ':attribute مطلوب إذا لم يتوفّر :values.',
94+
'required_without_all' => ':attribute مطلوب إذا لم يتوفّر :values.',
95+
'same' => 'يجب أن يتطابق :attribute مع :other.',
96+
'size' => [
97+
'numeric' => 'يجب أن تكون قيمة :attribute مساوية لـ :size.',
98+
'file' => 'يجب أن يكون حجم الملف :attribute :size كيلوبايت.',
99+
'string' => 'يجب أن يحتوي النص :attribute على :size حروفٍ/حرفًا بالضبط.',
100+
'array' => 'يجب أن يحتوي :attribute على :size عنصرٍ/عناصر بالضبط.',
101+
],
102+
'starts_with' => 'يجب أن يبدأ :attribute بأحد القيم التالية: :values',
103+
'string' => 'يجب أن يكون :attribute نصًا.',
104+
'timezone' => 'يجب أن يكون :attribute نطاقًا زمنيًا صحيحًا.',
105+
'unique' => 'قيمة :attribute مُستخدمة من قبل.',
106+
'uploaded' => 'فشل في تحميل الـ :attribute.',
107+
'url' => 'صيغة الرابط :attribute غير صحيحة.',
108+
'uuid' => ':attribute يجب أن يكون بصيغة UUID سليمة.',
109+
110+
'custom' => [
111+
'attribute-name' => [
112+
'rule-name' => 'رسالة-مخصصة',
113+
],
114+
],
115+
116+
'attributes' => [],
117+
];

api/resources/lang/fr/validation.php

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
return [
4+
'accepted' => 'Le champ :attribute doit être accepté.',
5+
'active_url' => 'Le champ :attribute n\'est pas une URL valide.',
6+
'after' => 'Le champ :attribute doit être une date postérieure au :date.',
7+
'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale au :date.',
8+
'alpha' => 'Le champ :attribute doit contenir uniquement des lettres.',
9+
'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres, des tirets et des underscores.',
10+
'alpha_num' => 'Le champ :attribute doit contenir uniquement des lettres et des chiffres.',
11+
'array' => 'Le champ :attribute doit être un tableau.',
12+
'before' => 'Le champ :attribute doit être une date antérieure au :date.',
13+
'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.',
14+
'between' => [
15+
'numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.',
16+
'file' => 'La taille du fichier de :attribute doit être comprise entre :min et :max kilo-octets.',
17+
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
18+
'array' => 'Le tableau :attribute doit contenir entre :min et :max éléments.',
19+
],
20+
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
21+
'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.',
22+
'date' => 'Le champ :attribute n\'est pas une date valide.',
23+
'date_equals' => 'Le champ :attribute doit être une date égale à :date.',
24+
'date_format' => 'Le champ :attribute ne correspond pas au format :format.',
25+
'different' => 'Les champs :attribute et :other doivent être différents.',
26+
'digits' => 'Le champ :attribute doit contenir :digits chiffres.',
27+
'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.',
28+
'dimensions' => 'Les dimensions de l\'image :attribute ne sont pas valides.',
29+
'distinct' => 'Le champ :attribute a une valeur en double.',
30+
'email' => 'Le champ :attribute doit être une adresse e-mail valide.',
31+
'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values.',
32+
'exists' => 'Le champ :attribute sélectionné est invalide.',
33+
'file' => 'Le champ :attribute doit être un fichier.',
34+
'filled' => 'Le champ :attribute doit avoir une valeur.',
35+
'gt' => [
36+
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
37+
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
38+
'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
39+
'array' => 'Le tableau :attribute doit contenir plus de :value éléments.',
40+
],
41+
'gte' => [
42+
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
43+
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
44+
'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
45+
'array' => 'Le tableau :attribute doit contenir au moins :value éléments.',
46+
],
47+
'image' => 'Le champ :attribute doit être une image.',
48+
'in' => 'Le champ :attribute est invalide.',
49+
'in_array' => 'Le champ :attribute n\'existe pas dans :other.',
50+
'integer' => 'Le champ :attribute doit être un entier.',
51+
'ip' => 'Le champ :attribute doit être une adresse IP valide.',
52+
'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.',
53+
'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.',
54+
'json' => 'Le champ :attribute doit être un document JSON valide.',
55+
'lt' => [
56+
'numeric' => 'La valeur de :attribute doit être inférieure à :value.',
57+
'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.',
58+
'string' => 'Le texte :attribute doit contenir moins de :value caractères.',
59+
'array' => 'Le tableau :attribute doit contenir moins de :value éléments.',
60+
],
61+
'lte' => [
62+
'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.',
63+
'file' => 'La taille du fichier de :attribute doit être inférieure ou égale à :value kilo-octets.',
64+
'string' => 'Le texte :attribute doit contenir au plus :value caractères.',
65+
'array' => 'Le tableau :attribute doit contenir au plus :value éléments.',
66+
],
67+
'max' => [
68+
'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.',
69+
'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.',
70+
'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.',
71+
'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.',
72+
],
73+
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
74+
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
75+
'min' => [
76+
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.',
77+
'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.',
78+
'string' => 'Le texte :attribute doit contenir au moins :min caractères.',
79+
'array' => 'Le tableau :attribute doit contenir au moins :min éléments.',
80+
],
81+
'multiple_of' => 'La valeur de :attribute doit être un multiple de :value',
82+
'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.',
83+
'not_regex' => 'Le format du champ :attribute n\'est pas valide.',
84+
'numeric' => 'Le champ :attribute doit contenir un nombre.',
85+
'password' => 'Le mot de passe est incorrect.',
86+
'present' => 'Le champ :attribute doit être présent.',
87+
'regex' => 'Le format du champ :attribute est invalide.',
88+
'required' => 'Le champ :attribute est obligatoire.',
89+
'required_if' => 'Le champ :attribute est obligatoire quand :other est :value.',
90+
'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.',
91+
'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.',
92+
'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.',
93+
'required_without' => 'Le champ :attribute est obligatoire quand :values n\'est pas présent.',
94+
'required_without_all' => 'Le champ :attribute est requis quand aucun de :values n\'est présent.',
95+
'same' => 'Les champs :attribute et :other doivent être identiques.',
96+
'size' => [
97+
'numeric' => 'La valeur de :attribute doit être :size.',
98+
'file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.',
99+
'string' => 'Le texte de :attribute doit contenir :size caractères.',
100+
'array' => 'Le tableau :attribute doit contenir :size éléments.',
101+
],
102+
'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values.',
103+
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
104+
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
105+
'unique' => 'La valeur du champ :attribute est déjà utilisée.',
106+
'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.',
107+
'url' => 'Le format de l\'URL de :attribute n\'est pas valide.',
108+
'uuid' => 'Le champ :attribute doit être un UUID valide.',
109+
110+
'custom' => [
111+
'attribute-name' => [
112+
'rule-name' => 'message-personnalisé',
113+
],
114+
],
115+
116+
'attributes' => [],
117+
];

0 commit comments

Comments
 (0)