This commit is contained in:
Julien Nahum 2024-12-06 14:49:15 +01:00
commit 39b95f1683
39 changed files with 1592 additions and 35 deletions

View File

@ -252,6 +252,12 @@ class AnswerFormRequest extends FormRequest
protected function prepareForValidation()
{
// Set locale based on form language
if ($this->form?->language && in_array($this->form->language, Form::LANGUAGES)) {
app()->setLocale($this->form->language);
}
$receivedData = $this->toArray();
$mergeData = [];
$countryCodeMapper = json_decode(file_get_contents(resource_path('data/country_code_mapper.json')), true);

View File

@ -29,6 +29,7 @@ abstract class UserFormRequest extends \Illuminate\Foundation\Http\FormRequest
'visibility' => ['required', Rule::in(Form::VISIBILITY)],
// Customization
'language' => ['required', Rule::in(Form::LANGUAGES)],
'font_family' => 'string|nullable',
'theme' => ['required', Rule::in(Form::THEMES)],
'width' => ['required', Rule::in(Form::WIDTHS)],

View File

@ -70,6 +70,7 @@ class FormResource extends JsonResource
'dark_mode' => $this->dark_mode,
'transparent_background' => $this->transparent_background,
'color' => $this->color,
'language' => $this->language,
'theme' => $this->theme,
'is_password_protected' => true,
'has_password' => $this->has_password,

View File

@ -41,6 +41,8 @@ class Form extends Model implements CachableAttributes
public const VISIBILITY = ['public', 'draft', 'closed'];
public const LANGUAGES = ['en', 'fr', 'hi', 'es', 'ar', 'zh', 'ja'];
protected $fillable = [
'workspace_id',
'creator_id',
@ -52,6 +54,7 @@ class Form extends Model implements CachableAttributes
'visibility',
// Customization
'language',
'font_family',
'custom_domain',
'size',

View File

@ -94,6 +94,12 @@ return [
'locales' => [
'en' => 'EN',
'fr' => 'FR',
'hi' => 'HI',
'es' => 'ES',
'ar' => 'AR',
'zh' => 'ZH',
'ja' => 'JA',
],
/*

View File

@ -50,6 +50,7 @@ class FormFactory extends Factory
'title' => $this->faker->text(30),
'description' => $this->faker->randomHtml(1),
'visibility' => 'public',
'language' => 'en',
'theme' => $this->faker->randomElement(Form::THEMES),
'size' => $this->faker->randomElement(Form::SIZES),
'border_radius' => $this->faker->randomElement(Form::BORDER_RADIUS),

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('forms', function (Blueprint $table) {
$table->string('language')->default('en');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('forms', function (Blueprint $table) {
$table->dropColumn('language');
});
}
};

View File

@ -0,0 +1,117 @@
<?php
return [
'accepted' => 'يجب قبول :attribute.',
'active_url' => ':attribute لا يُمثل رابطًا صحيحًا.',
'after' => 'يجب على :attribute أن يكون تاريخًا لاحقًا للتاريخ :date.',
'after_or_equal' => ':attribute يجب أن يكون تاريخاً لاحقاً أو مطابقاً للتاريخ :date.',
'alpha' => 'يجب أن لا يحتوي :attribute سوى على حروف.',
'alpha_dash' => 'يجب أن لا يحتوي :attribute سوى على حروف، أرقام ومطّات.',
'alpha_num' => 'يجب أن يحتوي :attribute على حروفٍ وأرقامٍ فقط.',
'array' => 'يجب أن يكون :attribute ًمصفوفة.',
'before' => 'يجب على :attribute أن يكون تاريخًا سابقًا للتاريخ :date.',
'before_or_equal' => ':attribute يجب أن يكون تاريخا سابقا أو مطابقا للتاريخ :date.',
'between' => [
'numeric' => 'يجب أن تكون قيمة :attribute بين :min و :max.',
'file' => 'يجب أن يكون حجم الملف :attribute بين :min و :max كيلوبايت.',
'string' => 'يجب أن يكون عدد حروف النّص :attribute بين :min و :max.',
'array' => 'يجب أن يحتوي :attribute على عدد من العناصر بين :min و :max.',
],
'boolean' => 'يجب أن تكون قيمة :attribute إما true أو false.',
'confirmed' => 'حقل التأكيد غير مُطابق للحقل :attribute.',
'date' => ':attribute ليس تاريخًا صحيحًا.',
'date_equals' => 'يجب أن يكون :attribute مطابقاً للتاريخ :date.',
'date_format' => 'لا يتوافق :attribute مع الشكل :format.',
'different' => 'يجب أن يكون الحقلان :attribute و :other مُختلفين.',
'digits' => 'يجب أن يحتوي :attribute على :digits رقمًا/أرقام.',
'digits_between' => 'يجب أن يحتوي :attribute بين :min و :max رقمًا/أرقام.',
'dimensions' => 'الـ :attribute يحتوي على أبعاد صورة غير صالحة.',
'distinct' => 'للحقل :attribute قيمة مُكرّرة.',
'email' => 'يجب أن يكون :attribute عنوان بريد إلكتروني صحيح البُنية.',
'ends_with' => 'يجب أن ينتهي :attribute بأحد القيم التالية: :values',
'exists' => 'القيمة المحددة :attribute غير موجودة.',
'file' => 'الـ :attribute يجب أن يكون ملفا.',
'filled' => ':attribute إجباري.',
'gt' => [
'numeric' => 'يجب أن تكون قيمة :attribute أكبر من :value.',
'file' => 'يجب أن يكون حجم الملف :attribute أكبر من :value كيلوبايت.',
'string' => 'يجب أن يكون طول النّص :attribute أكثر من :value حروفٍ/حرفًا.',
'array' => 'يجب أن يحتوي :attribute على أكثر من :value عناصر/عنصر.',
],
'gte' => [
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أكبر من :value.',
'file' => 'يجب أن يكون حجم الملف :attribute على الأقل :value كيلوبايت.',
'string' => 'يجب أن يكون طول النص :attribute على الأقل :value حروفٍ/حرفًا.',
'array' => 'يجب أن يحتوي :attribute على الأقل على :value عُنصرًا/عناصر.',
],
'image' => 'يجب أن يكون :attribute صورةً.',
'in' => ':attribute غير موجود.',
'in_array' => ':attribute غير موجود في :other.',
'integer' => 'يجب أن يكون :attribute عددًا صحيحًا.',
'ip' => 'يجب أن يكون :attribute عنوان IP صحيحًا.',
'ipv4' => 'يجب أن يكون :attribute عنوان IPv4 صحيحًا.',
'ipv6' => 'يجب أن يكون :attribute عنوان IPv6 صحيحًا.',
'json' => 'يجب أن يكون :attribute نصًا من نوع JSON.',
'lt' => [
'numeric' => 'يجب أن تكون قيمة :attribute أصغر من :value.',
'file' => 'يجب أن يكون حجم الملف :attribute أصغر من :value كيلوبايت.',
'string' => 'يجب أن يكون طول النّص :attribute أقل من :value حروفٍ/حرفًا.',
'array' => 'يجب أن يحتوي :attribute على أقل من :value عناصر/عنصر.',
],
'lte' => [
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أصغر من :value.',
'file' => 'يجب أن لا يتجاوز حجم الملف :attribute :value كيلوبايت.',
'string' => 'يجب أن لا يتجاوز طول النّص :attribute :value حروفٍ/حرفًا.',
'array' => 'يجب أن لا يحتوي :attribute على أكثر من :value عناصر/عنصر.',
],
'max' => [
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أصغر من :max.',
'file' => 'يجب أن لا يتجاوز حجم الملف :attribute :max كيلوبايت.',
'string' => 'يجب أن لا يتجاوز طول النّص :attribute :max حروفٍ/حرفًا.',
'array' => 'يجب أن لا يحتوي :attribute على أكثر من :max عناصر/عنصر.',
],
'mimes' => 'يجب أن يكون ملفًا من نوع : :values.',
'mimetypes' => 'يجب أن يكون ملفًا من نوع : :values.',
'min' => [
'numeric' => 'يجب أن تكون قيمة :attribute مساوية أو أكبر من :min.',
'file' => 'يجب أن يكون حجم الملف :attribute على الأقل :min كيلوبايت.',
'string' => 'يجب أن يكون طول النص :attribute على الأقل :min حروفٍ/حرفًا.',
'array' => 'يجب أن يحتوي :attribute على الأقل على :min عُنصرًا/عناصر.',
],
'multiple_of' => ':attribute يجب أن يكون من مضاعفات :value',
'not_in' => 'العنصر :attribute غير صحيح.',
'not_regex' => 'صيغة :attribute غير صحيحة.',
'numeric' => 'يجب على :attribute أن يكون رقمًا.',
'password' => 'كلمة المرور غير صحيحة.',
'present' => 'يجب تقديم :attribute.',
'regex' => 'صيغة :attribute غير صحيحة.',
'required' => ':attribute مطلوب.',
'required_if' => ':attribute مطلوب في حال ما إذا كان :other يساوي :value.',
'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' => [
'numeric' => 'يجب أن تكون قيمة :attribute مساوية لـ :size.',
'file' => 'يجب أن يكون حجم الملف :attribute :size كيلوبايت.',
'string' => 'يجب أن يحتوي النص :attribute على :size حروفٍ/حرفًا بالضبط.',
'array' => 'يجب أن يحتوي :attribute على :size عنصرٍ/عناصر بالضبط.',
],
'starts_with' => 'يجب أن يبدأ :attribute بأحد القيم التالية: :values',
'string' => 'يجب أن يكون :attribute نصًا.',
'timezone' => 'يجب أن يكون :attribute نطاقًا زمنيًا صحيحًا.',
'unique' => 'قيمة :attribute مُستخدمة من قبل.',
'uploaded' => 'فشل في تحميل الـ :attribute.',
'url' => 'صيغة الرابط :attribute غير صحيحة.',
'uuid' => ':attribute يجب أن يكون بصيغة UUID سليمة.',
'custom' => [
'attribute-name' => [
'rule-name' => 'رسالة-مخصصة',
],
],
'attributes' => [],
];

View File

@ -0,0 +1,117 @@
<?php
return [
'accepted' => 'Le champ :attribute doit être accepté.',
'active_url' => 'Le champ :attribute n\'est pas une URL valide.',
'after' => 'Le champ :attribute doit être une date postérieure au :date.',
'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale au :date.',
'alpha' => 'Le champ :attribute doit contenir uniquement des lettres.',
'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres, des tirets et des underscores.',
'alpha_num' => 'Le champ :attribute doit contenir uniquement des lettres et des chiffres.',
'array' => 'Le champ :attribute doit être un tableau.',
'before' => 'Le champ :attribute doit être une date antérieure au :date.',
'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.',
'between' => [
'numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.',
'file' => 'La taille du fichier de :attribute doit être comprise entre :min et :max kilo-octets.',
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
'array' => 'Le tableau :attribute doit contenir entre :min et :max éléments.',
],
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.',
'date' => 'Le champ :attribute n\'est pas une date valide.',
'date_equals' => 'Le champ :attribute doit être une date égale à :date.',
'date_format' => 'Le champ :attribute ne correspond pas au format :format.',
'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' => 'Les dimensions de l\'image :attribute ne sont pas valides.',
'distinct' => 'Le champ :attribute a une valeur en double.',
'email' => 'Le champ :attribute doit être une adresse e-mail valide.',
'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values.',
'exists' => 'Le champ :attribute sélectionné est invalide.',
'file' => 'Le champ :attribute doit être un fichier.',
'filled' => 'Le champ :attribute doit avoir une valeur.',
'gt' => [
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
'array' => 'Le tableau :attribute doit contenir plus de :value éléments.',
],
'gte' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :value éléments.',
],
'image' => 'Le champ :attribute doit être une image.',
'in' => 'Le champ :attribute est invalide.',
'in_array' => 'Le champ :attribute n\'existe pas dans :other.',
'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 un document JSON valide.',
'lt' => [
'numeric' => 'La valeur de :attribute doit être inférieure à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir moins de :value caractères.',
'array' => 'Le tableau :attribute doit contenir moins de :value éléments.',
],
'lte' => [
'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au plus :value caractères.',
'array' => 'Le tableau :attribute doit contenir au plus :value éléments.',
],
'max' => [
'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.',
'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.',
'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.',
'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.',
],
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
'min' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :min caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :min éléments.',
],
'multiple_of' => 'La valeur de :attribute doit être un multiple de :value',
'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.',
'not_regex' => 'Le format du champ :attribute n\'est pas valide.',
'numeric' => 'Le champ :attribute doit contenir un nombre.',
'password' => 'Le mot de passe est incorrect.',
'present' => 'Le champ :attribute doit être présent.',
'regex' => 'Le format du champ :attribute est invalide.',
'required' => 'Le champ :attribute est obligatoire.',
'required_if' => 'Le champ :attribute est obligatoire quand :other est :value.',
'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.',
'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.',
'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.',
'required_without' => 'Le champ :attribute est obligatoire quand :values n\'est pas présent.',
'required_without_all' => 'Le champ :attribute est requis quand aucun de :values n\'est présent.',
'same' => 'Les champs :attribute et :other doivent être identiques.',
'size' => [
'numeric' => 'La valeur de :attribute doit être :size.',
'file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.',
'string' => 'Le texte de :attribute doit contenir :size caractères.',
'array' => 'Le tableau :attribute doit contenir :size éléments.',
],
'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values.',
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
'unique' => 'La valeur du champ :attribute est déjà utilisée.',
'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.',
'url' => 'Le format de l\'URL de :attribute n\'est pas valide.',
'uuid' => 'Le champ :attribute doit être un UUID valide.',
'custom' => [
'attribute-name' => [
'rule-name' => 'message-personnalisé',
],
],
'attributes' => [],
];

View File

@ -0,0 +1,117 @@
<?php
return [
'accepted' => ':attribute को स्वीकार किया जाना चाहिए।',
'active_url' => ':attribute एक मान्य URL नहीं है।',
'after' => ':attribute, :date के बाद की एक तारीख होनी चाहिए।',
'after_or_equal' => ':attribute, :date के बाद या उसके बराबर की तारीख होनी चाहिए।',
'alpha' => ':attribute में केवल अक्षर हो सकते हैं।',
'alpha_dash' => ':attribute में केवल अक्षर, संख्या, डैश और अंडरस्कोर हो सकते हैं।',
'alpha_num' => ':attribute में केवल अक्षर और संख्याएं हो सकती हैं।',
'array' => ':attribute एक एरे होना चाहिए।',
'before' => ':attribute, :date से पहले की एक तारीख होनी चाहिए।',
'before_or_equal' => ':attribute, :date से पहले या उसके बराबर की तारीख होनी चाहिए।',
'between' => [
'numeric' => ':attribute, :min और :max के बीच होना चाहिए।',
'file' => ':attribute, :min और :max किलोबाइट के बीच होना चाहिए।',
'string' => ':attribute, :min और :max वर्णों के बीच होना चाहिए।',
'array' => ':attribute, :min और :max आइटमों के बीच होना चाहिए।',
],
'boolean' => ':attribute फील्ड सही या गलत होना चाहिए।',
'confirmed' => ':attribute पुष्टिकरण मेल नहीं खा रहा है।',
'date' => ':attribute एक मान्य तारीख नहीं है।',
'date_equals' => ':attribute, :date के बराबर की तारीख होनी चाहिए।',
'date_format' => ':attribute फॉर्मेट :format से मेल नहीं खा रहा है।',
'different' => ':attribute और :other अलग होना चाहिए।',
'digits' => ':attribute, :digits अंक होना चाहिए।',
'digits_between' => ':attribute, :min और :max अंकों के बीच होना चाहिए।',
'dimensions' => ':attribute की अमान्य छवि आयाम हैं।',
'distinct' => ':attribute फील्ड का एक डुप्लिकेट मान है।',
'email' => ':attribute एक मान्य ईमेल पता होना चाहिए।',
'ends_with' => ':attribute निम्न में से किसी एक के साथ समाप्त होना चाहिए: :values',
'exists' => 'चयनित :attribute अमान्य है।',
'file' => ':attribute एक फ़ाइल होनी चाहिए।',
'filled' => ':attribute फील्ड आवश्यक है।',
'gt' => [
'numeric' => ':attribute, :value से बड़ा होना चाहिए।',
'file' => ':attribute, :value किलोबाइट से बड़ा होना चाहिए।',
'string' => ':attribute, :value वर्णों से बड़ा होना चाहिए।',
'array' => ':attribute, :value आइटमों से अधिक होना चाहिए।',
],
'gte' => [
'numeric' => ':attribute, :value से बड़ा या बराबर होना चाहिए।',
'file' => ':attribute, :value किलोबाइट से बड़ा या बराबर होना चाहिए।',
'string' => ':attribute, :value वर्णों से बड़ा या बराबर होना चाहिए।',
'array' => ':attribute में :value आइटम या अधिक होने चाहिए।',
],
'image' => ':attribute एक छवि होनी चाहिए।',
'in' => 'चयनित :attribute अमान्य है।',
'in_array' => ':attribute फील्ड, :other में मौजूद नहीं है।',
'integer' => ':attribute एक पूर्णांक होना चाहिए।',
'ip' => ':attribute एक मान्य IP पता होना चाहिए।',
'ipv4' => ':attribute एक मान्य IPv4 पता होना चाहिए।',
'ipv6' => ':attribute एक मान्य IPv6 पता होना चाहिए।',
'json' => ':attribute एक मान्य JSON स्ट्रिंग होना चाहिए।',
'lt' => [
'numeric' => ':attribute, :value से छोटा होना चाहिए।',
'file' => ':attribute, :value किलोबाइट से छोटा होना चाहिए।',
'string' => ':attribute, :value वर्णों से छोटा होना चाहिए।',
'array' => ':attribute, :value आइटमों से कम होना चाहिए।',
],
'lte' => [
'numeric' => ':attribute, :value से छोटा या बराबर होना चाहिए।',
'file' => ':attribute, :value किलोबाइट से छोटा या बराबर होना चाहिए।',
'string' => ':attribute, :value वर्णों से छोटा या बराबर होना चाहिए।',
'array' => ':attribute में :value आइटम से अधिक नहीं होना चाहिए।',
],
'max' => [
'numeric' => ':attribute, :max से बड़ा नहीं हो सकता है।',
'file' => ':attribute, :max किलोबाइट से बड़ा नहीं हो सकता है।',
'string' => ':attribute, :max वर्णों से बड़ा नहीं हो सकता है।',
'array' => ':attribute में :max से अधिक आइटम नहीं हो सकते हैं।',
],
'mimes' => ':attribute एक प्रकार की फ़ाइल: :values होनी चाहिए।',
'mimetypes' => ':attribute एक प्रकार की फ़ाइल: :values होनी चाहिए।',
'min' => [
'numeric' => ':attribute कम से कम :min होना चाहिए।',
'file' => ':attribute कम से कम :min किलोबाइट होना चाहिए।',
'string' => ':attribute कम से कम :min वर्ण होना चाहिए।',
'array' => ':attribute में कम से कम :min आइटम होने चाहिए।',
],
'multiple_of' => ':attribute, :value का गुणज होना चाहिए।',
'not_in' => 'चयनित :attribute अमान्य है।',
'not_regex' => ':attribute प्रारूप अमान्य है।',
'numeric' => ':attribute एक संख्या होनी चाहिए।',
'password' => 'पासवर्ड गलत है।',
'present' => ':attribute फील्ड मौजूद होना चाहिए।',
'regex' => ':attribute प्रारूप अमान्य है।',
'required' => ':attribute फील्ड आवश्यक है।',
'required_if' => ':attribute फील्ड आवश्यक है जब :other :value है।',
'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' => [
'numeric' => ':attribute, :size होना चाहिए।',
'file' => ':attribute, :size किलोबाइट होना चाहिए।',
'string' => ':attribute, :size वर्ण होना चाहिए।',
'array' => ':attribute में :size आइटम होने चाहिए।',
],
'starts_with' => ':attribute निम्न में से किसी एक से शुरू होना चाहिए: :values',
'string' => ':attribute एक स्ट्रिंग होनी चाहिए।',
'timezone' => ':attribute एक मान्य क्षेत्र होना चाहिए।',
'unique' => ':attribute पहले से ही लिया गया है।',
'uploaded' => ':attribute अपलोड करने में विफल रहा।',
'url' => ':attribute प्रारूप अमान्य है।',
'uuid' => ':attribute एक मान्य UUID होना चाहिए।',
'custom' => [
'attribute-name' => [
'rule-name' => 'कस्टम-संदेश',
],
],
'attributes' => [],
];

View File

@ -0,0 +1,117 @@
<?php
return [
'accepted' => ':attributeを承認してください。',
'active_url' => ':attributeは、有効なURLではありません。',
'after' => ':attributeには、:dateより後の日付を指定してください。',
'after_or_equal' => ':attributeには、:date以降の日付を指定してください。',
'alpha' => ':attributeには、アルファベッドのみ使用できます。',
'alpha_dash' => ':attributeには、英数字、ハイフン、アンダースコアのみ使用できます。',
'alpha_num' => ':attributeには、英数字のみ使用できます。',
'array' => ':attributeには、配列を指定してください。',
'before' => ':attributeには、:dateより前の日付を指定してください。',
'before_or_equal' => ':attributeには、:date以前の日付を指定してください。',
'between' => [
'numeric' => ':attributeには、:minから:maxまでの数字を指定してください。',
'file' => ':attributeには、:min KBから:max KBまでのサイズのファイルを指定してください。',
'string' => ':attributeは、:min文字から:max文字にしてください。',
'array' => ':attributeの項目は、:min個から:max個にしてください。',
],
'boolean' => ':attributeには、trueかfalseを指定してください。',
'confirmed' => ':attributeと確認用の値が一致しません。',
'date' => ':attributeは、正しい日付ではありません。',
'date_equals' => ':attributeは:dateと同じ日付でなければなりません。',
'date_format' => ':attributeの形式は、:formatと合いません。',
'different' => ':attributeと:otherには、異なった内容を指定してください。',
'digits' => ':attributeは、:digits桁にしてください。',
'digits_between' => ':attributeは、:min桁から:max桁にしてください。',
'dimensions' => ':attributeの画像サイズが無効です。',
'distinct' => ':attributeの値が重複しています。',
'email' => ':attributeには、有効なメールアドレスを指定してください。',
'ends_with' => ':attributeは、次のいずれかで終わる必要があります。: :values',
'exists' => '選択された:attributeは、有効ではありません。',
'file' => ':attributeには、ファイルを指定してください。',
'filled' => ':attributeには、値を指定してください。',
'gt' => [
'numeric' => ':attributeは、:valueより大きくなければなりません。',
'file' => ':attributeは、:value KBより大きくなければなりません。',
'string' => ':attributeは、:value文字より長くなければなりません。',
'array' => ':attributeの項目数は、:value個より多くなければなりません。',
],
'gte' => [
'numeric' => ':attributeは、:value以上でなければなりません。',
'file' => ':attributeは、:value KB以上でなければなりません。',
'string' => ':attributeは、:value文字以上でなければなりません。',
'array' => ':attributeの項目数は、:value個以上でなければなりません。',
],
'image' => ':attributeには、画像を指定してください。',
'in' => '選択された:attributeは、有効ではありません。',
'in_array' => ':attributeが:otherに存在しません。',
'integer' => ':attributeには、整数を指定してください。',
'ip' => ':attributeには、有効なIPアドレスを指定してください。',
'ipv4' => ':attributeはIPv4アドレスを指定してください。',
'ipv6' => ':attributeはIPv6アドレスを指定してください。',
'json' => ':attributeには、有効なJSON文字列を指定してください。',
'lt' => [
'numeric' => ':attributeは、:valueより小さくなければなりません。',
'file' => ':attributeは、:value KBより小さくなければなりません。',
'string' => ':attributeは、:value文字より短くなければなりません。',
'array' => ':attributeの項目数は、:value個より少なくなければなりません。',
],
'lte' => [
'numeric' => ':attributeは、:value以下でなければなりません。',
'file' => ':attributeは、:value KB以下でなければなりません。',
'string' => ':attributeは、:value文字以下でなければなりません。',
'array' => ':attributeの項目数は、:value個以下でなければなりません。',
],
'max' => [
'numeric' => ':attributeには、:max以下の数字を指定してください。',
'file' => ':attributeには、:max KB以下のファイルを指定してください。',
'string' => ':attributeは、:max文字以下にしてください。',
'array' => ':attributeの項目は、:max個以下にしてください。',
],
'mimes' => ':attributeには、:valuesタイプのファイルを指定してください。',
'mimetypes' => ':attributeには、:valuesタイプのファイルを指定してください。',
'min' => [
'numeric' => ':attributeには、:min以上の数字を指定してください。',
'file' => ':attributeには、:min KB以上のファイルを指定してください。',
'string' => ':attributeは、:min文字以上にしてください。',
'array' => ':attributeの項目は、:min個以上にしてください。',
],
'multiple_of' => ':attributeは:valueの倍数でなければなりません。',
'not_in' => '選択された:attributeは、有効ではありません。',
'not_regex' => ':attributeの形式が無効です。',
'numeric' => ':attributeには、数字を指定してください。',
'password' => 'パスワードが正しくありません。',
'present' => ':attributeが存在している必要があります。',
'regex' => ':attributeには、有効な正規表現を指定してください。',
'required' => ':attributeは、必ず指定してください。',
'required_if' => ':otherが:valueの場合、: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' => [
'numeric' => ':attributeには、:sizeを指定してください。',
'file' => ':attributeには、:size KBのファイルを指定してください。',
'string' => ':attributeは、:size文字にしてください。',
'array' => ':attributeの項目は、:size個にしてください。',
],
'starts_with' => ':attributeは、次のいずれかで始まる必要があります。: :values',
'string' => ':attributeには、文字を指定してください。',
'timezone' => ':attributeには、有効なタイムゾーンを指定してください。',
'unique' => '指定の:attributeは既に使用されています。',
'uploaded' => ':attributeのアップロードに失敗しました。',
'url' => ':attributeは、有効なURL形式で指定してください。',
'uuid' => ':attributeは、有効なUUIDでなければなりません。',
'custom' => [
'attribute-name' => [
'rule-name' => 'カスタムメッセージ',
],
],
'attributes' => [],
];

View File

@ -0,0 +1,117 @@
<?php
return [
'accepted' => ':attribute 必须被接受。',
'active_url' => ':attribute 不是一个有效的网址。',
'after' => ':attribute 必须是 :date 之后的日期。',
'after_or_equal' => ':attribute 必须是 :date 之后或相同的日期。',
'alpha' => ':attribute 只能由字母组成。',
'alpha_dash' => ':attribute 只能由字母、数字、短划线和下划线组成。',
'alpha_num' => ':attribute 只能由字母和数字组成。',
'array' => ':attribute 必须是一个数组。',
'before' => ':attribute 必须是 :date 之前的日期。',
'before_or_equal' => ':attribute 必须是 :date 之前或相同的日期。',
'between' => [
'numeric' => ':attribute 必须介于 :min - :max 之间。',
'file' => ':attribute 必须介于 :min - :max KB 之间。',
'string' => ':attribute 必须介于 :min - :max 个字符之间。',
'array' => ':attribute 必须只有 :min - :max 个单元。',
],
'boolean' => ':attribute 必须为布尔值。',
'confirmed' => ':attribute 两次输入不一致。',
'date' => ':attribute 不是一个有效的日期。',
'date_equals' => ':attribute 必须等于 :date。',
'date_format' => ':attribute 的格式必须为 :format。',
'different' => ':attribute 和 :other 必须不同。',
'digits' => ':attribute 必须是 :digits 位数字。',
'digits_between' => ':attribute 必须是介于 :min 和 :max 位的数字。',
'dimensions' => ':attribute 图片尺寸不正确。',
'distinct' => ':attribute 已经存在。',
'email' => ':attribute 不是一个合法的邮箱。',
'ends_with' => ':attribute 必须以 :values 为结尾。',
'exists' => ':attribute 不存在。',
'file' => ':attribute 必须是文件。',
'filled' => ':attribute 不能为空。',
'gt' => [
'numeric' => ':attribute 必须大于 :value。',
'file' => ':attribute 必须大于 :value KB。',
'string' => ':attribute 必须多于 :value 个字符。',
'array' => ':attribute 必须多于 :value 个元素。',
],
'gte' => [
'numeric' => ':attribute 必须大于或等于 :value。',
'file' => ':attribute 必须大于或等于 :value KB。',
'string' => ':attribute 必须多于或等于 :value 个字符。',
'array' => ':attribute 必须多于或等于 :value 个元素。',
],
'image' => ':attribute 必须是图片。',
'in' => '已选的属性 :attribute 无效。',
'in_array' => ':attribute 必须在 :other 中。',
'integer' => ':attribute 必须是整数。',
'ip' => ':attribute 必须是有效的 IP 地址。',
'ipv4' => ':attribute 必须是有效的 IPv4 地址。',
'ipv6' => ':attribute 必须是有效的 IPv6 地址。',
'json' => ':attribute 必须是正确的 JSON 格式。',
'lt' => [
'numeric' => ':attribute 必须小于 :value。',
'file' => ':attribute 必须小于 :value KB。',
'string' => ':attribute 必须少于 :value 个字符。',
'array' => ':attribute 必须少于 :value 个元素。',
],
'lte' => [
'numeric' => ':attribute 必须小于或等于 :value。',
'file' => ':attribute 必须小于或等于 :value KB。',
'string' => ':attribute 必须少于或等于 :value 个字符。',
'array' => ':attribute 必须少于或等于 :value 个元素。',
],
'max' => [
'numeric' => ':attribute 不能大于 :max。',
'file' => ':attribute 不能大于 :max KB。',
'string' => ':attribute 不能大于 :max 个字符。',
'array' => ':attribute 最多只有 :max 个单元。',
],
'mimes' => ':attribute 必须是一个 :values 类型的文件。',
'mimetypes' => ':attribute 必须是一个 :values 类型的文件。',
'min' => [
'numeric' => ':attribute 必须大于等于 :min。',
'file' => ':attribute 大小不能小于 :min KB。',
'string' => ':attribute 至少为 :min 个字符。',
'array' => ':attribute 至少有 :min 个单元。',
],
'multiple_of' => ':attribute 必须是 :value 的倍数',
'not_in' => '已选的属性 :attribute 非法。',
'not_regex' => ':attribute 的格式错误。',
'numeric' => ':attribute 必须是一个数字。',
'password' => '密码错误。',
'present' => ':attribute 必须存在。',
'regex' => ':attribute 格式不正确。',
'required' => ':attribute 不能为空。',
'required_if' => '当 :other 为 :value 时 :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' => [
'numeric' => ':attribute 大小必须为 :size。',
'file' => ':attribute 大小必须为 :size KB。',
'string' => ':attribute 必须是 :size 个字符。',
'array' => ':attribute 必须为 :size 个单元。',
],
'starts_with' => ':attribute 必须以 :values 为开头。',
'string' => ':attribute 必须是一个字符串。',
'timezone' => ':attribute 必须是一个合法的时区值。',
'unique' => ':attribute 已经存在。',
'uploaded' => ':attribute 上传失败。',
'url' => ':attribute 格式不正确。',
'uuid' => ':attribute 必须是有效的 UUID。',
'custom' => [
'attribute-name' => [
'rule-name' => '自定义消息',
],
],
'attributes' => [],
];

View File

@ -71,6 +71,7 @@
:max-date="maxDate"
:is-dark="props.isDark"
color="form-color"
:locale="props.locale"
@update:model-value="updateModelValue"
/>
<DatePicker
@ -84,6 +85,7 @@
:max-date="maxDate"
:is-dark="props.isDark"
color="form-color"
:locale="props.locale"
@update:model-value="updateModelValue"
/>
</template>

View File

@ -50,7 +50,7 @@
>
<Loader class="mx-auto h-6 w-6" />
<p class="mt-2 text-center text-sm text-gray-500">
Uploading your file...
{{ $t('forms.fileInput.uploadingFile') }}
</p>
</div>
<template v-else>
@ -95,10 +95,10 @@
</div>
<p class="mt-2 text-sm text-gray-500 font-medium select-none">
Click to choose {{ multiple ? 'file(s)' : 'a file' }} or drag here
{{ $t('forms.fileInput.chooseFiles', { n: multiple ? 1 : 0 }) }}
</p>
<p class="mt-1 text-xs text-gray-400 dark:text-gray-600 select-none">
Size limit: {{ mbLimit }}MB per file
{{ $t('forms.fileInput.sizeLimit', mbLimit) }}
</p>
</template>
</div>

View File

@ -74,7 +74,7 @@
'!text-gray-500 !cursor-not-allowed'
]"
>
No options available.
{{ $t('forms.select.noOptionAvailable') }}
</div>
</div>

View File

@ -25,7 +25,7 @@
>
<Loader class="mx-auto h-6 w-6" />
<p class="mt-2 text-center text-sm text-gray-500">
Uploading your file...
{{ $t('forms.fileInput.uploadingFile') }}
</p>
</div>
@ -76,7 +76,7 @@
:class="theme.default.help"
href="#"
@click.prevent="openFileUpload"
>Upload file instead</a>
>{{ $t('forms.signatureInput.uploadFileInstead') }}</a>
</small>
<small :class="theme.default.help">
@ -84,7 +84,7 @@
:class="theme.default.help"
href="#"
@click.prevent="clear"
>Clear</a>
>{{ $t('forms.signatureInput.clear') }}</a>
</small>
</template>

View File

@ -49,17 +49,16 @@
class="w-6 h-6"
/>
<p class="text-center font-bold">
Allow Camera Permission
{{ $t('forms.cameraUpload.allowCameraPermission') }}
</p>
<p class="text-xs">
You need to allow camera permission before you can take pictures. Go to
browser settings to enable camera permission on this page.
{{ $t('forms.cameraUpload.allowCameraPermissionDescription') }}
</p>
<UButton
color="white"
@click.stop="cancelCamera"
>
Got it!
{{ $t('forms.cameraUpload.gotIt') }}
</UButton>
</div>
@ -81,16 +80,16 @@
class="w-6 h-6"
/>
<p class="text-center font-bold">
Camera Device Error
{{ $t('forms.cameraUpload.cameraDeviceError') }}
</p>
<p class="text-xs">
An unknown error occurred when trying to start Webcam device.
{{ $t('forms.cameraUpload.cameraDeviceErrorDescription') }}
</p>
<UButton
color="white"
@click.stop="cancelCamera"
>
Go back
{{ $t('forms.cameraUpload.goBack') }}
</UButton>
</div>
</div>

View File

@ -120,7 +120,7 @@
v-model="searchTerm"
type="text"
class="flex-grow ltr:pl-3 ltr:pr-7 rtl:!pr-3 rtl:pl-7 py-2 w-full focus:outline-none dark:text-white"
placeholder="Search"
:placeholder="allowCreation ? $t('forms.select.searchOrTypeToCreateNew') : $t('forms.select.search')"
>
<div
v-if="!searchTerm"
@ -179,7 +179,7 @@
v-else-if="!loading && !(allowCreation && searchTerm)"
class="w-full text-gray-500 text-center py-2"
>
{{ (allowCreation ? 'Type something to add an option' : 'No option available') }}.
{{ (allowCreation ? $t('forms.select.typeSomethingToAddAnOption') : $t('forms.select.noOptionAvailable')) }}.
</p>
<div
v-if="allowCreation && searchTerm"
@ -192,7 +192,7 @@
class="text-gray-900 select-none relative py-2 cursor-pointer group hover:bg-gray-100 dark:hover:bg-gray-900 rounded focus:outline-none"
@click="createOption(searchTerm)"
>
Create <span class="px-2 bg-gray-100 border border-gray-300 rounded group-hover-text-black">{{
{{ $t('forms.select.create') }} <span class="px-2 bg-gray-100 border border-gray-300 rounded group-hover-text-black">{{
searchTerm
}}</span>
</li>

View File

@ -28,7 +28,8 @@ export const inputProps = {
help: {type: String, default: null},
helpPosition: {type: String, default: "below_input"},
color: {type: String, default: "#3B82F6"},
wrapperClass: {type: String, default: ""},
wrapperClass: { type: String, default: "" },
locale: { type: String, default: "en" },
}
export function useFormInput(props, context, options = {}) {

View File

@ -27,7 +27,7 @@
<div v-if="isPublicFormPage && form.is_password_protected">
<p class="form-description mb-4 text-gray-700 dark:text-gray-300 px-2">
This form is protected by a password.
{{ $t('forms.password_protected') }}
</p>
<div class="form-group flex flex-wrap w-full">
<div class="relative mb-3 w-full px-2">
@ -47,7 +47,7 @@
class="my-4"
@click="passwordEntered"
>
Submit
{{ $t('forms.submit') }}
</open-form-button>
</div>
</div>
@ -139,7 +139,7 @@
class="text-gray-400 hover:text-gray-500 dark:text-gray-600 dark:hover:text-gray-500 cursor-pointer hover:underline text-xs"
target="_blank"
>
Powered by <span class="font-semibold">OpnForm</span>
{{ $t('forms.powered_by') }} <span class="font-semibold">{{ $t('app.name') }}</span>
</a>
</p>
</div>
@ -186,7 +186,7 @@
href="https://opnform.com/?utm_source=form&utm_content=create_form_free"
class="text-nt-blue hover:underline"
>
Create your form for free with OpnForm
{{ $t('forms.create_form_free') }}
</a>
</p>
</div>
@ -202,7 +202,6 @@
<script>
import OpenForm from './OpenForm.vue'
import OpenFormButton from './OpenFormButton.vue'
import FormTimer from './FormTimer.vue'
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
import VTransition from '~/components/global/transitions/VTransition.vue'
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
@ -211,7 +210,7 @@ import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
import FirstSubmissionModal from '~/components/open/forms/components/FirstSubmissionModal.vue'
export default {
components: { VTransition, OpenFormButton, OpenForm, FormCleanings, FormTimer, FirstSubmissionModal },
components: { VTransition, OpenFormButton, OpenForm, FormCleanings, FirstSubmissionModal },
props: {
form: { type: Object, required: true },
@ -225,8 +224,11 @@ export default {
},
setup(props) {
const { setLocale } = useI18n()
const authStore = useAuthStore()
return {
setLocale,
authStore,
authenticated: computed(() => authStore.check),
isIframe: useIsIframe(),
@ -274,6 +276,17 @@ export default {
return this.authenticated && this.form && this.form.creator_id === this.authStore.user.id
}
},
watch: {
'form.language': {
handler(newLanguage) {
this.setLocale(newLanguage)
},
immediate: true
}
},
beforeUnmount() {
this.setLocale('en')
},
methods: {
submitForm (form, onFailure) {
@ -346,7 +359,7 @@ export default {
if (this.passwordForm.password !== '' && this.passwordForm.password !== null) {
this.$emit('password-entered', this.passwordForm.password)
} else {
this.addPasswordError('The Password field is required.')
this.addPasswordError(this.$t('forms.password_required'))
}
},
addPasswordError (msg) {

View File

@ -122,7 +122,7 @@
{{ currentFieldsPageBreak.next_btn_text }}
</open-form-button>
<div v-if="!currentFieldsPageBreak && !isLastPage">
Something is wrong with this form structure. If you're the form owner please contact us.
{{ $t('forms.wrong_form_structure') }}
</div>
</div>
</form>

View File

@ -329,7 +329,8 @@ export default {
theme: this.theme,
maxCharLimit: (field.max_char_limit) ? parseInt(field.max_char_limit) : null,
showCharLimit: field.show_char_limit || false,
isDark: this.darkMode
isDark: this.darkMode,
locale: (this.form?.language) ? this.form.language : 'en'
}
if (field.type === 'matrix') {

View File

@ -69,7 +69,7 @@
errorReport += ` And here are technical details about the error: \`\`\`${error.stack}\`\`\``
try {
crisp.openAndShowChat(errorReport)
crisp.showMessage(`Hi there, we're very sorry to hear you experienced an issue with NoteForms.
crisp.showMessage(`Hi there, we're very sorry to hear you experienced an issue with OpnForm.
We'll be in touch about it very soon! In the meantime, I recommend that you try going back one step, and save your changes.`, 2000)
} catch (e) {
console.error('Crisp error', e)

View File

@ -74,6 +74,14 @@
label="Uppercase Input Labels"
/>
<select-input
name="language"
class="mt-4"
:options="availableLocales"
:form="form"
label="Form Language"
/>
<EditorSectionHeader
icon="heroicons:rectangle-stack-16-solid"
title="Layout & Sizing"
@ -215,6 +223,7 @@ const form = storeToRefs(workingFormStore).content
const isMounted = ref(false)
const confetti = useConfetti()
const showGoogleFontPicker = ref(false)
const { $i18n } = useNuxtApp()
const user = computed(() => authStore.user)
const workspace = computed(() => workspacesStore.getCurrent)
@ -225,6 +234,10 @@ const isPro = computed(() => {
return workspace.value.is_pro
})
const availableLocales = computed(() => {
return $i18n.locales?.value.map(locale => ({ name: locale.name, value: locale.code })) ?? []
})
onMounted(() => {
isMounted.value = true
})

View File

@ -10,6 +10,7 @@ export const initForm = (defaultValue = {}, withDefaultProperties = false) => {
properties: withDefaultProperties ? getDefaultProperties() : [],
// Customization
language: 'en',
font_family: null,
theme: "default",
width: "centered",

38
client/i18n/lang/ar.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "أوبنفورم"
},
"forms": {
"powered_by": "مشغل بواسطة",
"password_protected": "هذا النموذج محمي بكلمة مرور.",
"invalid_password": "كلمة مرور غير صالحة.",
"password_required": "كلمة مرور مطلوبة.",
"create_form_free": "أنشأ نموذجك مجانًا باستخدام أوبنفورم",
"submit": "إرسال",
"wrong_form_structure": "هناك خطأ في بنية هذا النموذج. إذا كنت مالك النموذج، يرجى الاتصال بنا.",
"select": {
"search": "بحث",
"searchOrTypeToCreateNew": "بحث أو اكتب شيءً لإضافة خيار",
"typeSomethingToAddAnOption": "اكتب شيءً لإضافة خيار",
"noOptionAvailable": "لا يوجد خيارات متاحة",
"create": "إنشاء"
},
"fileInput": {
"chooseFiles": "انقر لاختيار الملفات أو قم بإسقاطها هنا | انقر لاختيار ملف أو قم بإسقاطه هنا",
"sizeLimit": "الحد الأقصى للحجم: {count} ميجابايت لكل ملف",
"uploadingFile": "جاري تحميل الملف الخاص بك..."
},
"cameraUpload": {
"allowCameraPermission": "السماح بالوصول إلى الكاميرا",
"allowCameraPermissionDescription": "يجب السماح بالوصول إلى الكاميرا قبل التقاط الصور. انتقل إلى إعدادات المتصفح لتمكين الوصول إلى الكاميرا على هذه الصفحة.",
"gotIt": "فهمت!",
"cameraDeviceError": "خطأ في الكاميرا",
"cameraDeviceErrorDescription": "حدث خطأ غير معروف أثناء محاولة تشغيل كاميرا الويب.",
"goBack": "رجوع"
},
"signatureInput": {
"uploadFileInstead": "تحميل ملف بدلاً من ذلك",
"clear": "مسح"
}
}
}

38
client/i18n/lang/en.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Powered by",
"password_protected": "This form is protected by a password.",
"invalid_password": "Invalid password.",
"password_required": "Password is required.",
"create_form_free": "Create your form for free with OpnForm",
"submit": "Submit",
"wrong_form_structure": "Something is wrong with this form structure. If you're the form owner please contact us.",
"select": {
"search": "Search",
"searchOrTypeToCreateNew": "Search or type to create new",
"typeSomethingToAddAnOption": "Type something to add an option",
"noOptionAvailable": "No option available",
"create": "Create"
},
"fileInput": {
"chooseFiles": "Click to choose file(s) or drag here | Click to choose a file or drag here",
"sizeLimit": "Size limit: {count}MB per file",
"uploadingFile": "Uploading your file..."
},
"cameraUpload": {
"allowCameraPermission": "Allow Camera Permission",
"allowCameraPermissionDescription": "You need to allow camera permission before you can take pictures. Go to browser settings to enable camera permission on this page.",
"gotIt": "Got it!",
"cameraDeviceError": "Camera Device Error",
"cameraDeviceErrorDescription": "An unknown error occurred when trying to start Webcam device.",
"goBack": "Go back"
},
"signatureInput": {
"uploadFileInstead": "Upload file instead",
"clear": "Clear"
}
}
}

38
client/i18n/lang/es.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Desarrollado por",
"password_protected": "Este formulario está protegido por una contraseña.",
"invalid_password": "Contraseña inválida.",
"password_required": "Se requiere una contraseña.",
"create_form_free": "Crea tu formulario gratis con OpnForm",
"submit": "Enviar",
"wrong_form_structure": "Algo está mal con la estructura de este formulario. Si eres el propietario del formulario, por favor contacta con nosotros.",
"select": {
"search": "Buscar",
"searchOrTypeToCreateNew": "Buscar o escribe algo para crear una nueva opción",
"typeSomethingToAddAnOption": "Escribe algo para agregar una opción",
"noOptionAvailable": "No hay opciones disponibles",
"create": "Crear"
},
"fileInput": {
"chooseFiles": "Haga clic para elegir archivo(s) o suéltelo(s) aquí | Haga clic para elegir un archivo o suéltelo aquí",
"sizeLimit": "Tamaño límite: {count}MB por archivo",
"uploadingFile": "Subiendo su archivo..."
},
"cameraUpload": {
"allowCameraPermission": "Permitir acceso a la cámara",
"allowCameraPermissionDescription": "Debe permitir el acceso a la cámara antes de poder tomar fotos. Vaya a la configuración del navegador para habilitar el acceso a la cámara en esta página.",
"gotIt": "¡Entendido!",
"cameraDeviceError": "Error de cámara",
"cameraDeviceErrorDescription": "Se produjo un error desconocido al intentar iniciar el dispositivo de la cámara web.",
"goBack": "Volver"
},
"signatureInput": {
"uploadFileInstead": "Subir un archivo en su lugar",
"clear": "Borrar"
}
}
}

38
client/i18n/lang/fr.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Powered by",
"password_protected": "Ce formulaire est protégé par un mot de passe.",
"invalid_password": "Mot de passe invalide.",
"password_required": "Le mot de passe est requis.",
"create_form_free": "Créez votre formulaire gratuitement avec OpnForm",
"submit": "Envoyer",
"wrong_form_structure": "Quelque chose ne va pas avec la structure de ce formulaire. Si vous êtes le propriétaire du formulaire, veuillez nous contacter.",
"select": {
"search": "Rechercher",
"searchOrTypeToCreateNew": "Rechercher ou écrivez quelque chose pour créer une nouvelle option",
"typeSomethingToAddAnOption": "Écrivez quelque chose pour ajouter une option",
"noOptionAvailable": "Aucune option disponible",
"create": "Créer"
},
"fileInput": {
"chooseFiles": "Cliquez pour choisir un/des fichier(s) ou déposez-le(s) ici | Cliquez pour choisir un fichier ou déposez-le ici",
"sizeLimit": "Taille limite: {count}MB par fichier",
"uploadingFile": "Envoi de votre fichier en cours..."
},
"cameraUpload": {
"allowCameraPermission": "Autoriser l'accès à la caméra",
"allowCameraPermissionDescription": "Vous devez autoriser l'accès à la caméra avant de pouvoir prendre des photos. Allez dans les paramètres du navigateur pour activer l'accès à la caméra sur cette page.",
"gotIt": "J'ai compris !",
"cameraDeviceError": "Erreur de caméra",
"cameraDeviceErrorDescription": "Une erreur inconnue est survenue lors de la tentative de démarrage du périphérique webcam.",
"goBack": "Retour"
},
"signatureInput": {
"uploadFileInstead": "Uploader un fichier à la place",
"clear": "Effacer"
}
}
}

38
client/i18n/lang/hi.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "ऑपनफॉर्म"
},
"forms": {
"powered_by": "ऑपनफॉर्म के साथ",
"password_protected": "यह फ़ॉर्म पासवर्ड से सुरक्षित है।",
"invalid_password": "अमान्य पासवर्ड।",
"password_required": "पासवर्ड आवश्यक है।",
"create_form_free": "अपनी फ़ॉर्म फ्री में ऑपनफॉर्म के साथ बनाएं",
"submit": "जमा करें",
"wrong_form_structure": "यह फ़ॉर्म संरचना में कुछ गलत है। यदि आप फ़ॉर्म का मालिक हैं, तो कृपया हमसे संपर्क करें।",
"select": {
"search": "खोजें",
"searchOrTypeToCreateNew": "खोजें या कुछ भी टाइप करें जो विकल्प जोड़ने के लिए",
"typeSomethingToAddAnOption": "कुछ भी टाइप करें जो विकल्प जोड़ने के लिए",
"noOptionAvailable": "कोई विकल्प उपलब्ध नहीं है",
"create": "बनाएं"
},
"fileInput": {
"chooseFiles": "फ़ाइलें चुनने के लिए क्लिक करें या यहां खींचें | फ़ाइल चुनने के लिए क्लिक करें या यहां खींचें",
"sizeLimit": "साइज़ सीमा: प्रति फ़ाइल {count}MB",
"uploadingFile": "आपकी फ़ाइल अपलोड हो रही है..."
},
"cameraUpload": {
"allowCameraPermission": "कैमरा एक्सेस की अनुमति दें",
"allowCameraPermissionDescription": "फ़ोटो लेने से पहले कैमरा एक्सेस की अनुमति देनी होगी। इस पेज पर कैमरा एक्सेस सक्षम करने के लिए ब्राउज़र सेटिंग्स में जाएं।",
"gotIt": "समझ गया!",
"cameraDeviceError": "कैमरा त्रुटि",
"cameraDeviceErrorDescription": "वेबकैम डिवाइस शुरू करने का प्रयास करते समय एक अज्ञात त्रुटि हुई।",
"goBack": "वापस जाएं"
},
"signatureInput": {
"uploadFileInstead": "इसके बजाय फ़ाइल अपलोड करें",
"clear": "साफ़ करें"
}
}
}

38
client/i18n/lang/ja.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Powered by",
"password_protected": "このフォームはパスワードで保護されています。",
"invalid_password": "無効なパスワード。",
"password_required": "パスワードが必要です。",
"create_form_free": "無料でOpnFormでフォームを作成",
"submit": "送信",
"wrong_form_structure": "このフォームの構造に問題があります。フォームの所有者である場合は、お問い合わせください。",
"select": {
"search": "検索",
"searchOrTypeToCreateNew": "検索または何かを入力して新しいオプションを作成",
"typeSomethingToAddAnOption": "オプションを追加するには何かを入力してください",
"noOptionAvailable": "オプションがありません",
"create": "作成"
},
"fileInput": {
"chooseFiles": "クリックしてファイルを選択するか、ここにドロップしてください | クリックしてファイルを選択するか、ここにドロップしてください",
"sizeLimit": "サイズ制限:ファイルあたり{count}MB",
"uploadingFile": "ファイルをアップロード中..."
},
"cameraUpload": {
"allowCameraPermission": "カメラへのアクセスを許可",
"allowCameraPermissionDescription": "写真を撮影する前にカメラへのアクセスを許可する必要があります。ブラウザの設定でこのページのカメラアクセスを有効にしてください。",
"gotIt": "了解しました!",
"cameraDeviceError": "カメラエラー",
"cameraDeviceErrorDescription": "ウェブカメラデバイスの起動時に不明なエラーが発生しました。",
"goBack": "戻る"
},
"signatureInput": {
"uploadFileInstead": "代わりにファイルをアップロード",
"clear": "クリア"
}
}
}

38
client/i18n/lang/pt.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Powered by",
"password_protected": "Este formulário está protegido por uma senha.",
"invalid_password": "Senha inválida.",
"password_required": "A senha é necessária.",
"create_form_free": "Crie seu formulário gratuitamente com OpnForm",
"submit": "Enviar",
"wrong_form_structure": "Algo está errado com a estrutura deste formulário. Se você é o proprietário do formulário, por favor entre em contato conosco.",
"select": {
"search": "Buscar",
"searchOrTypeToCreateNew": "Buscar ou escreva algo para criar uma nova opção",
"typeSomethingToAddAnOption": "Escreva algo para adicionar uma opção",
"noOptionAvailable": "Nenhuma opção disponível",
"create": "Criar"
},
"fileInput": {
"chooseFiles": "Clique para escolher arquivo(s) ou solte-os aqui | Clique para escolher um arquivo ou solte-o aqui",
"sizeLimit": "Limite de tamanho: {count}MB por arquivo",
"uploadingFile": "Enviando seu arquivo..."
},
"cameraUpload": {
"allowCameraPermission": "Permitir acesso à câmera",
"allowCameraPermissionDescription": "Você precisa permitir o acesso à câmera antes de tirar fotos. Vá para as configurações do navegador para ativar o acesso à câmera nesta página.",
"gotIt": "Entendi!",
"cameraDeviceError": "Erro de câmera",
"cameraDeviceErrorDescription": "Ocorreu um erro desconhecido ao tentar iniciar o dispositivo da webcam.",
"goBack": "Voltar"
},
"signatureInput": {
"uploadFileInstead": "Fazer upload de arquivo em vez disso",
"clear": "Limpar"
}
}
}

38
client/i18n/lang/zh.json Normal file
View File

@ -0,0 +1,38 @@
{
"app": {
"name": "OpnForm"
},
"forms": {
"powered_by": "Powered by",
"password_protected": "此表单受密码保护。",
"invalid_password": "无效密码。",
"password_required": "密码是必需的。",
"create_form_free": "免费使用OpnForm创建表单",
"submit": "提交",
"wrong_form_structure": "此表单的结构有问题。如果您是表单所有者,请与我们联系。",
"select": {
"search": "搜索",
"searchOrTypeToCreateNew": "搜索或输入以创建新选项",
"typeSomethingToAddAnOption": "输入一些东西以添加选项",
"noOptionAvailable": "没有选项可用",
"create": "创建"
},
"fileInput": {
"chooseFiles": "点击选择文件或将文件拖放到此处 | 点击选择文件或将文件拖放到此处",
"sizeLimit": "大小限制:每个文件 {count}MB",
"uploadingFile": "正在上传您的文件..."
},
"cameraUpload": {
"allowCameraPermission": "允许访问相机",
"allowCameraPermissionDescription": "在拍照之前,您需要允许访问相机。请转到浏览器设置以启用此页面的相机访问权限。",
"gotIt": "明白了!",
"cameraDeviceError": "相机错误",
"cameraDeviceErrorDescription": "尝试启动网络摄像头设备时发生未知错误。",
"goBack": "返回"
},
"signatureInput": {
"uploadFileInstead": "改为上传文件",
"clear": "清除"
}
}
}

View File

@ -15,11 +15,28 @@ export default defineNuxtConfig({
'@nuxtjs/sitemap',
'@nuxt/ui',
'nuxt-utm',
'@nuxtjs/i18n',
'@nuxt/icon',
...process.env.NUXT_PUBLIC_GTM_CODE ? ['@zadigetvoltaire/nuxt-gtm'] : [],
],
],
build: {
transpile: ["vue-notion", "query-builder-vue-3", "vue-signature-pad"],
},
i18n: {
locales: [
{ code: 'en', name: 'English', iso: 'en-US', file: 'en.json' },
{ code: 'fr', name: 'French', iso: 'fr-FR', file: 'fr.json' },
{ code: 'hi', name: 'Hindi', iso: 'hi-IN', file: 'hi.json' },
{ code: 'es', name: 'Spanish', iso: 'es-ES', file: 'es.json' },
{ code: 'ar', name: 'Arabic', iso: 'ar-EG', file: 'ar.json' },
{ code: 'zh', name: 'Chinese', iso: 'zh-CN', file: 'zh.json' },
{ code: 'ja', name: 'Japanese', iso: 'ja-JP', file: 'ja.json' },
],
defaultLocale: 'en',
lazy: true,
langDir: 'lang/',
strategy: 'no_prefix'
},
experimental: {
inlineRouteRules: true
},

576
client/package-lock.json generated
View File

@ -57,6 +57,7 @@
"@nuxt/devtools": "^1.6.1",
"@nuxt/eslint-config": "^0.2.0",
"@nuxt/icon": "^1.8.2",
"@nuxtjs/i18n": "^9.0.0",
"@nuxtjs/sitemap": "^6.1.3",
"@zadigetvoltaire/nuxt-gtm": "^0.0.13",
"autoprefixer": "^10.4.20",
@ -1530,6 +1531,358 @@
"vue": ">=3"
}
},
"node_modules/@intlify/bundle-utils": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-9.0.0.tgz",
"integrity": "sha512-19dunbgM4wuCvi2xSai2PKhXkcKGjlbJhNWm9BCQWkUYcPmXwzptNWOE0O7OSrhNlEDxwpkHsJzZ/vLbCkpElw==",
"dev": true,
"dependencies": {
"@intlify/message-compiler": "next",
"@intlify/shared": "next",
"acorn": "^8.8.2",
"escodegen": "^2.1.0",
"estree-walker": "^2.0.2",
"jsonc-eslint-parser": "^2.3.0",
"mlly": "^1.2.0",
"source-map-js": "^1.0.1",
"yaml-eslint-parser": "^1.2.2"
},
"engines": {
"node": ">= 18"
},
"peerDependenciesMeta": {
"petite-vue-i18n": {
"optional": true
},
"vue-i18n": {
"optional": true
}
}
},
"node_modules/@intlify/bundle-utils/node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"node_modules/@intlify/core": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/@intlify/core/-/core-10.0.4.tgz",
"integrity": "sha512-YVb0Hk0vgulAhC/uq5CMUXLMcmDL+24FWcjRk2RMn2EF8ZrM3ZV9+8dGOKextntN1/RtjK3RvoHicF48vMyYkA==",
"dev": true,
"dependencies": {
"@intlify/core-base": "10.0.4",
"@intlify/shared": "10.0.4"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/core-base": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.4.tgz",
"integrity": "sha512-GG428DkrrWCMhxRMRQZjuS7zmSUzarYcaHJqG9VB8dXAxw4iQDoKVQ7ChJRB6ZtsCsX3Jse1PEUlHrJiyQrOTg==",
"dev": true,
"dependencies": {
"@intlify/message-compiler": "10.0.4",
"@intlify/shared": "10.0.4"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/h3": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@intlify/h3/-/h3-0.6.0.tgz",
"integrity": "sha512-tWBm92pYLT+T2H5I2Uwz0dnylX1uRKuS6/n9CV4eTW43r/iAN2q07b0sY2cvgT61KYDetomY1pVRkzA2Rftv5g==",
"dev": true,
"dependencies": {
"@intlify/core": "^10.0.3",
"@intlify/utils": "^0.13.0"
},
"engines": {
"node": ">= 18"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.4.tgz",
"integrity": "sha512-AFbhEo10DP095/45EauinQJ5hJ3rJUmuuqltGguvc3WsvezZN+g8qNHLGWKu60FHQVizMrQY7VJ+zVlBXlQQkQ==",
"dev": true,
"dependencies": {
"@intlify/shared": "10.0.4",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.4.tgz",
"integrity": "sha512-ukFn0I01HsSgr3VYhYcvkTCLS7rGa0gw4A4AMpcy/A9xx/zRJy7PS2BElMXLwUazVFMAr5zuiTk3MQeoeGXaJg==",
"dev": true,
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/unplugin-vue-i18n": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-5.2.0.tgz",
"integrity": "sha512-pmRiPY2Nj9mmSrixT69aO45XxGUr5fDBy/IIw4ajLlDTJm5TSmQKA5YNdsH0uxVDCPWy5tlQrF18hkDwI7UJvg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@intlify/bundle-utils": "^9.0.0-beta.0",
"@intlify/shared": "next",
"@intlify/vue-i18n-extensions": "^7.0.0",
"@rollup/pluginutils": "^5.1.0",
"@typescript-eslint/scope-manager": "^7.13.0",
"@typescript-eslint/typescript-estree": "^7.13.0",
"debug": "^4.3.3",
"fast-glob": "^3.2.12",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
"pathe": "^1.0.0",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2",
"unplugin": "^1.1.0",
"vue": "^3.4"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"petite-vue-i18n": "*",
"vue": "^3.2.25",
"vue-i18n": "*"
},
"peerDependenciesMeta": {
"petite-vue-i18n": {
"optional": true
},
"vue-i18n": {
"optional": true
}
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/@typescript-eslint/scope-manager": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
"integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.18.0",
"@typescript-eslint/visitor-keys": "7.18.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/@typescript-eslint/types": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
"integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
"dev": true,
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/@typescript-eslint/typescript-estree": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
"integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.18.0",
"@typescript-eslint/visitor-keys": "7.18.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/@typescript-eslint/visitor-keys": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
"integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "7.18.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/unplugin": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz",
"integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==",
"dev": true,
"dependencies": {
"acorn": "^8.14.0",
"webpack-virtual-modules": "^0.6.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"webpack-sources": "^3"
},
"peerDependenciesMeta": {
"webpack-sources": {
"optional": true
}
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/webpack-virtual-modules": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
"dev": true
},
"node_modules/@intlify/utils": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@intlify/utils/-/utils-0.13.0.tgz",
"integrity": "sha512-8i3uRdAxCGzuHwfmHcVjeLQBtysQB2aXl/ojoagDut5/gY5lvWCQ2+cnl2TiqE/fXj/D8EhWG/SLKA7qz4a3QA==",
"dev": true,
"engines": {
"node": ">= 18"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/vue-i18n-extensions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-7.0.0.tgz",
"integrity": "sha512-MtvfJnb4aklpCU5Q/dkWkBT/vGsp3qERiPIwtTq5lX4PCLHtUprAJZp8wQj5ZcwDaFCU7+yVMjYbeXpIf927cA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.24.6",
"@intlify/shared": "^10.0.0",
"@vue/compiler-dom": "^3.2.45",
"vue-i18n": "^10.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@intlify/shared": "^9.0.0 || ^10.0.0",
"@vue/compiler-dom": "^3.0.0",
"vue": "^3.0.0",
"vue-i18n": "^9.0.0 || ^10.0.0"
},
"peerDependenciesMeta": {
"@intlify/shared": {
"optional": true
},
"@vue/compiler-dom": {
"optional": true
},
"vue": {
"optional": true
},
"vue-i18n": {
"optional": true
}
}
},
"node_modules/@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
@ -1819,6 +2172,19 @@
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@miyaneee/rollup-plugin-json5": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@miyaneee/rollup-plugin-json5/-/rollup-plugin-json5-1.2.0.tgz",
"integrity": "sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.1.0",
"json5": "^2.2.3"
},
"peerDependencies": {
"rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0"
}
},
"node_modules/@netlify/functions": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-2.8.2.tgz",
@ -2682,6 +3048,76 @@
"semver": "^7.6.3"
}
},
"node_modules/@nuxtjs/i18n": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@nuxtjs/i18n/-/i18n-9.0.0.tgz",
"integrity": "sha512-olAVD7ZPNVxWpOgj5VJrtWaqkVfKSDQmJENfed7t6TwDjVggPHPHMpmw4rbudDsr9cdCIBR30hvUEXm2m7s2BA==",
"dev": true,
"dependencies": {
"@intlify/h3": "^0.6.0",
"@intlify/shared": "^10.0.3",
"@intlify/unplugin-vue-i18n": "^5.2.0",
"@intlify/utils": "^0.13.0",
"@miyaneee/rollup-plugin-json5": "^1.2.0",
"@nuxt/kit": "^3.13.2",
"@rollup/plugin-yaml": "^4.1.2",
"@vue/compiler-sfc": "^3.5.5",
"debug": "^4.3.5",
"defu": "^6.1.2",
"estree-walker": "^3.0.3",
"is-https": "^4.0.0",
"knitwork": "^1.1.0",
"magic-string": "^0.30.10",
"mlly": "^1.7.1",
"pathe": "^1.1.1",
"scule": "^1.1.1",
"sucrase": "^3.35.0",
"ufo": "^1.3.1",
"unplugin": "^1.10.1",
"unplugin-vue-router": "^0.10.8",
"vue-i18n": "^10.0.3",
"vue-router": "^4.4.5"
},
"engines": {
"node": "^14.16.0 || >=16.11.0"
}
},
"node_modules/@nuxtjs/i18n/node_modules/magic-string": {
"version": "0.30.12",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
"integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/@nuxtjs/i18n/node_modules/unplugin": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.15.0.tgz",
"integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==",
"dev": true,
"dependencies": {
"acorn": "^8.14.0",
"webpack-virtual-modules": "^0.6.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"webpack-sources": "^3"
},
"peerDependenciesMeta": {
"webpack-sources": {
"optional": true
}
}
},
"node_modules/@nuxtjs/i18n/node_modules/webpack-virtual-modules": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
"dev": true
},
"node_modules/@nuxtjs/sitemap": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/@nuxtjs/sitemap/-/sitemap-6.1.5.tgz",
@ -3411,6 +3847,28 @@
}
}
},
"node_modules/@rollup/plugin-yaml": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz",
"integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"js-yaml": "^4.1.0",
"tosource": "^2.0.0-alpha.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz",
@ -5345,7 +5803,6 @@
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@ -7217,6 +7674,37 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/escodegen/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/eslint": {
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
@ -7525,6 +8013,19 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true,
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/esquery": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
@ -8664,6 +9165,12 @@
"node": ">=0.10.0"
}
},
"node_modules/is-https": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-https/-/is-https-4.0.0.tgz",
"integrity": "sha512-FeMLiqf8E5g6SdiVJsPcNZX8k4h2fBs1wp5Bb6uaNxn58ufK1axBqQZdmAQsqh0t9BuwFObybrdVJh6MKyPlyg==",
"dev": true
},
"node_modules/is-inside-container": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
@ -8948,6 +9455,24 @@
"node": ">=6"
}
},
"node_modules/jsonc-eslint-parser": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz",
"integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==",
"dev": true,
"dependencies": {
"acorn": "^8.5.0",
"eslint-visitor-keys": "^3.0.0",
"espree": "^9.0.0",
"semver": "^7.3.5"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
}
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@ -14482,6 +15007,15 @@
"node": ">=0.6"
}
},
"node_modules/tosource": {
"version": "2.0.0-alpha.3",
"resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz",
"integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
@ -16159,6 +16693,26 @@
"eslint": ">=6.0.0"
}
},
"node_modules/vue-i18n": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.4.tgz",
"integrity": "sha512-1xkzVxqBLk2ZFOmeI+B5r1J7aD/WtNJ4j9k2mcFcQo5BnOmHBmD7z4/oZohh96AAaRZ4Q7mNQvxc9h+aT+Md3w==",
"dev": true,
"dependencies": {
"@intlify/core-base": "10.0.4",
"@intlify/shared": "10.0.4",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-json-pretty": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/vue-json-pretty/-/vue-json-pretty-2.4.0.tgz",
@ -16424,8 +16978,24 @@
"node_modules/yaml-ast-parser": {
"version": "0.0.43",
"resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz",
"integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==",
"license": "Apache-2.0"
"integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="
},
"node_modules/yaml-eslint-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.2.3.tgz",
"integrity": "sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.0.0",
"lodash": "^4.17.21",
"yaml": "^2.0.0"
},
"engines": {
"node": "^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
}
},
"node_modules/yargs": {
"version": "15.4.1",

View File

@ -19,6 +19,7 @@
"@nuxt/devtools": "^1.6.1",
"@nuxt/eslint-config": "^0.2.0",
"@nuxt/icon": "^1.8.2",
"@nuxtjs/i18n": "^9.0.0",
"@nuxtjs/sitemap": "^6.1.3",
"@zadigetvoltaire/nuxt-gtm": "^0.0.13",
"autoprefixer": "^10.4.20",

View File

@ -93,6 +93,7 @@ const formLoading = computed(() => formsStore.loading)
const recordLoading = computed(() => recordsStore.loading)
const slug = useRoute().params.slug
const form = computed(() => formsStore.getByKey(slug))
const $t = useI18n()
const openCompleteForm = ref(null)
@ -106,7 +107,7 @@ const passwordEntered = function (password) {
nextTick(() => {
loadForm().then(() => {
if (form.value?.is_password_protected) {
openCompleteForm.value.addPasswordError('Invalid password.')
openCompleteForm.value.addPasswordError($t('forms.invalid_password'))
}
})
})
@ -235,7 +236,11 @@ useOpnSeoMeta({
return (form.value && form.value?.can_be_indexed) ? null : 'noindex, nofollow'
}
})
useHead({
htmlAttrs: {
lang: (form.value?.language) ? form.value.language : 'en'
},
titleTemplate: (titleChunk) => {
if (pageMeta.value.page_title) {
// Disable template if custom SEO title

View File

@ -199,7 +199,7 @@
<template #description>
<div class="flex flex-wrap sm:flex-nowrap gap-4 items-start">
<p class="flex-grow">
Remove NoteForms branding, customize forms further, use your custom domain, integrate with your
Remove OpnForm branding, customize forms further, use your custom domain, integrate with your
favorite tools, invite users, and more!
</p>
<UButton

View File

@ -44,7 +44,7 @@ const redirectIfSubscribed = () => {
}
const checkSubscription = () => {
// Fetch the user.
return noteFormsFetch('user').then((data) => {
return opnFetch('user').then((data) => {
authStore.setUser(data)
redirectIfSubscribed()
}).catch((error) => {