diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 249801fc6cb1368eb2ef4b23b9de4436e80135bf..733b0ae02e7932c4091cd685c9758d638519c620 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,12 +35,16 @@ build_development: - npm install --unsafe-perm - echo -e " NODE_ENV=development\n - VUE_APP_LOCALE=fr-FR\n DOMAIN=https://dev.pigma.neogeo.fr/fr\n VUE_APP_DOMAIN=https://dev.pigma.neogeo.fr/\n BASE_PATH=/\n - VUE_APP_NEXT_DEFAULT=/\n VUE_APP_BASE_PATH=${BASE_PATH}\n + VUE_APP_NEXT_DEFAULT=/\n + VUE_APP_LOCALE=fr-FR\n + VUE_APP_I18N_LOCALE=fr\n + VUE_APP_I18N_DEFAULT_LOCALE=fr\n + VUE_APP_I18N_FALLBACK_LOCALE=fr\n + VUE_APP_I18N_SUPPORTED_LOCALE=fr,en\n VUE_APP_LOGO=@/assets/logo.png\n VUE_APP_TITLE=Login - Pigma\n VUE_APP_CLIENT_NAME=PIGMA\n diff --git a/README.md b/README.md index d0d9524fba051f1a9d826900513e8baa9e602768..a9716158319ac07bd5165ee2999f663e22de3495 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ npm install ```ìni NODE_ENV=development -VUE_APP_LOCALE=fr-FR DOMAIN=http://127.0.0.0:80 VUE_APP_DOMAIN=${DOMAIN} @@ -20,6 +19,12 @@ VUE_APP_BASE_PATH=${BASE_PATH} VUE_APP_NEXT_DEFAULT=${BASE_PATH} +VUE_APP_LOCALE=fr-FR +VUE_APP_I18N_LOCALE=fr +VUE_APP_I18N_DEFAULT_LOCALE=fr +VUE_APP_I18N_FALLBACK_LOCALE=fr +VUE_APP_I18N_SUPPORTED_LOCALE=fr,en + # App title VUE_APP_TITLE=Onegeo-Suite diff --git a/package.json b/package.json index 4394865b441ad37ba2ef730e20d55ea0b9e0f17f..52a1238bcd100649e4cf236773fcb20be48ac383 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "toastr": "^2.1.4", "vee-validate": "^3.4.9", "vue": "~2.6.11", + "vue-i18n": "^8.27.2", "vue-multiselect": "~2.1.6", "vue-recaptcha": "^1.3.0", "vue-router": "~3.2.0", diff --git a/src/App.vue b/src/App.vue index 23903416c543e0c48c7e0ffa2766ad95737f72eb..eeb0b42415b6be4ed2a470627690204efeaab68f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,18 @@ <template> <div id="app"> + <LocaleChanger /> <router-view id="page"/> </div> </template> <script> +import LocaleChanger from '@/components/LocaleChanger.vue'; + export default { + components: { + LocaleChanger + } + }; </script> diff --git a/src/app.less b/src/app.less index 75dd74149cbc4bf54ebcbdf8aefb8eb9b0448de3..5d913f59491c973151d9ef79f0967125627a5ff3 100644 --- a/src/app.less +++ b/src/app.less @@ -10,6 +10,12 @@ body { height: 100%; } +#locale-changer { + position: absolute; + top: 1rem; + right: 5rem; +} + #page { height: 100%; overflow-y: auto; diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json new file mode 100644 index 0000000000000000000000000000000000000000..d53beae93399499c2648e08760c1ad2c9ae9a6e5 --- /dev/null +++ b/src/assets/locales/en.json @@ -0,0 +1,195 @@ +{ + "words": { + "username": "Username", + "password": "Password", + "firstname": "First name", + "lastname": "Last name", + "email": "E-mail address", + "phone": "Phone number", + "login": "Log in", + "logout": "Log out", + "validate": "Validate", + "cancel": "Cancel", + "retry": "Retry", + "accept": "Accept", + "decline": "Decline", + "save": "Save", + "goBack": "Go back", + "as": "as" + }, + "errors": { + "required": "This field is required", + "email": "Please enter a valid email address", + "confirmPassword": "Passwords must be identical", + "regexPassword": "Your password must be at least 8 characters long, including at least one upper case, one lower case and one number." + }, + "messages": { + "error": "An error has occured.", + "loginError": "Incorrect username and/or password", + "password": { + "success": "Your password has been changed." + }, + "reinitPwdRequest": { + "success": "An e-mail has been sent to your mailbox to reset your password.", + "error": "No account found for this e-mail address." + }, + "reinitPwdConfirm": { + "success": "Your password has been successfully reset. You will be redirected to the login page.", + "error": "Your token has expired. You must request a new one to change your password." + } + }, + "multiselect": { + "noResult": "No results found", + "noOptions": "Enter the first characters..." + }, + "importImage": { + "logo": "Add a logo", + "image": "Add an image", + "placeholder": "Upload file" + }, + "organisationCreation": { + "title": "Create a new organisation", + "form": { + "name": "Organisation name", + "nameHelp": "Spelled out the entire name", + "acronym": "Acronym", + "siret": "Identification number", + "type": "Organisation type", + "typePlaceholder": "Select a type", + "logo": "Organisation logo", + "logoHelp": "Import a logo either in JPG or PNG format (maximum size: 2 MB)", + "web": "Website", + "phone": "Phone number", + "address": "Address", + "description": "Organisation description", + "group": { + "title": "Add this organisation to groups of organisations", + "help": "Adding this organisation to an organisation group will provide access to the data and data collections associated with that organisation group.", + "placeholder": "Add this organisation to groups of organisations" + } + } + }, + "signin": { + "title": "Enter your {org} credentials", + "forgottenpassword": "Forgot your password", + "noaccount": "No account yet", + "warning": "You already had a {org} account and this is your first connection to the new platform? If so, for RGPD compliance reasons, you need to regenerate your password via the « Forgot your password? » link below. Please enter the email address linked to your {org} account. If you encounter a problem, please contact us at the following email address " + }, + "forgottenPassword": { + "title": "Reset your password", + "message": "Please enter your account email address", + "placeholder": "Email address", + "confirmationButton": "Send reinitialization email" + }, + "reinitPassword": { + "title": "Set a new Password", + "newLabel": "New password", + "confirmLabel": "Confirm password", + "passwordHelp": [ + "Your password must be different from your personal information.", + "Your password must contain at least 8 characters.", + "Your password cannot be a commonly used password.", + "Your password cannot be entirely numerical." + ] + }, + "signup": { + "title": "New account", + "subtitle": "Request an account", + "secondTitle": "A confirmation e-mail will be sent to your address", + "form": { + "personalDetails": { + "title": "Your personal details", + "reason": "Reason for registration" + }, + "loginDetails": { + "title": "Your login details", + "usernameHelp": "The username is automatically generated from the first letter of your firstname and your lastname. It cannot be changed.", + "passwordHelp": [ + "Your password must be different from your personal information.", + "Your password must contain at least 8 characters.", + "Your password cannot be a commonly used password.", + "Your password cannot be entirely numerical." + ] + }, + "organisation": { + "title": "Organisation", + "subtitle": "Select your organisation from the list below.", + "placeholder": "Search for an organisation...", + "noOptions": "Enter an organisation's first characters to start the search", + "help": { + "text": "If you can't find it on the list, you can", + "link": " specify a new organisation." + } + } + } + }, + "signupSuccess": { + "message": [ + "A confirmation e-mail has just been sent to the address indicated on the form.", + "Please follow given instructions to finalize your account creation." + ] + }, + "signout": { + "message": "You are now logged out.", + "toSignIn": "Go to login page" + }, + "signoutFailed": { + "message": "An error occurred during signout." + }, + "termsOfUse": { + "error": "An error has occurred. Please contact the site administrator.", + "download": "Download terms of use" + }, + "profile": { + "title": "Existing account", + "subtitle": "Edit your account informations", + "form": { + "personalDetails": { + "title": "Your personal details", + "reason": "Reason for registration", + "organisation": { + "label": "Organisation(s)/Group(s)", + "noOrganisation": "You are not affiliated with any organisation.", + "help": "For any modification request, please contact" + } + }, + "emailChange": { + "title": "Change your email address", + "label": "New email address", + "help": "You can request a change of your email address by entering a new address. A confirmation link will be sent to the new address." + }, + "passwordChange": { + "title": "Change your password", + "oldLabel": "Old password", + "oldPlaceholder": "Old password", + "newLabel": "New password", + "newPlaceholder": "New password", + "confirmPlaceholder": "Confirm password", + "passwordHelp": [ + "Your password must be different from your personal information.", + "Your password must contain at least 8 characters.", + "Your password cannot be a commonly used password.", + "Your password cannot be entirely numerical." + ] + } + } + }, + "validationEmail": { + "message": "Your new email address has been validated.", + "goToLogin": "Go to login", + "error": "An error has occurred. Please contact the site administrator." + }, + "validationRegistration": { + "messages": { + "success": [ + "Thank you for your registration.", + "You will receive a notification when your account is enabled by the platform administrator." + ], + "error": "An error has occurred. Please contact the site administrator." + }, + "confirmation": "To confirm your registration", + "clickHere": "Click here" + }, + "footer": "Powered by", + "403": "Access Denied" +} \ No newline at end of file diff --git a/src/assets/locales/fr.json b/src/assets/locales/fr.json new file mode 100644 index 0000000000000000000000000000000000000000..59fcfb867d18c6999ecd4ba023de13af82086420 --- /dev/null +++ b/src/assets/locales/fr.json @@ -0,0 +1,196 @@ +{ + "words": { + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "firstname": "Prénom", + "lastname": "Nom", + "email": "Adresse e-mail", + "phone": "Numéro de téléphone", + "login": "Se connecter", + "logout": "Se déconnecter", + "validate": "Valider", + "cancel": "Annuler", + "retry": "Réessayer", + "accept": "Accepter", + "decline": "Refuser", + "save": "Sauvegarder", + "goBack": "Revenir", + "as": "en tant que" + }, + "errors": { + "required": "Ce champ est requis", + "email": "Veuillez entrer une adresse e-mail valide", + "confirmPassword": "Les mots de passe doivent être identiques", + "regexPassword": "Votre mot de passe doit comporter au moins 8 caractères, dont au moins une majuscule, une minuscule et 1 chiffre." + }, + "messages": { + "error": "Une erreur est survenue", + "loginError": "Nom d'utilisateur et/ou mot de passe incorrect(s)", + "password": { + "success": "Votre mot de passe a bien été modifié." + }, + "reinitPwdRequest": { + "success": "Un e-mail vous a été envoyé sur votre messagerie pour réinitialiser votre mot de passe.", + "error": "Aucun compte trouvé pour cette adresse e-mail." + }, + "reinitPwdConfirm": { + "success": "Votre mot de passe a bien été réinitialisé. Vous allez être redirigé vers la page de connexion.", + "error": "Votre token a expiré. Vous devez en demander un nouveau pour changer votre mot de passe." + } + }, + "multiselect": { + "noResult": "Aucun résultat.", + "noOptions": "Saississez les premiers caractères ..." + }, + "importImage": { + "logo": "Ajouter un logo", + "image": "Ajouter un image", + "placeholder": "Importer un fichier" + }, + "organisationCreation": { + "title": "Créer une nouvelle organisation", + "form": { + "name": "Nom de l'organisation", + "nameHelp": "Inscrivez le nom complet et en toutes lettres ; par ex. « Communauté des Communes Rurales de l'Entre-Deux-Mers »", + "acronym": "Sigle", + "siret": "Numéro SIRET", + "type": "Type d'organisation", + "typePlaceholder": "Sélectionnez un type", + "logo": "Logo de l'organisation", + "logoHelp": "Importez un logo au format JPG ou PNG (taille maximale : 2 Mo)", + "web": "Site internet", + "phone": "Numéro de téléphone", + "address": "Adresse postale", + "description": "description de l'organisation", + "group": { + "title": "Ajouter cette organisation à des groupe d'organisations", + "help": "L'ajout de cette organisation à un groupe d'organisation donnera accès aux données et collections de données associées à ce groupe d'organisation.", + "placeholder": "Ajouter cette organisation à des groupes d'organisations" + } + } + }, + "signin": { + "title": "Saisissez vos identifiants {org}", + "forgottenpassword": "Mot de passe oublié", + "noaccount": "Pas encore de compte", + "warning": "Vous aviez déjà un compte {org} et il s’agit de votre première connexion sur la nouvelle plateforme ? Si tel est le cas, pour des raisons de conformité RGPD, il vous faut regénérer votre mot de passe via la fonction « Mot de passe oublié ? » disponible ci-dessous. Veuillez renseigner l’adresse mail liée à votre compte {org}. Si vous rencontrez un problème veuillez nous contacter à l’adresse mail suivante : " + }, + "forgottenPassword": { + "title": "Réinitialisez votre mot de passe", + "message": "Veuillez indiquer l'adresse e-mail de votre compte", + "placeholder": "Adresse e-mail", + "confirmationButton": "Envoyer un e-mail de réinitialisation" + + }, + "reinitPassword": { + "title": "Choisissez un nouveau mot de passe", + "newLabel": "Nouveau mot de passe", + "confirmLabel": "Confirmez le mot de passe", + "passwordHelp": [ + "Votre mot de passe ne peut pas ressembler à vos informations personnelles.", + "Votre mot de passe doit contenir au minimum 8 caractères.", + "Votre mot de passe ne peut pas être un mot de passe couramment utilisé.", + "Votre mot de passe ne peut pas être entièrement numérique." + ] + }, + "signup": { + "title": "Nouveau compte", + "subtitle": "Sollicitez la création d'un compte", + "secondTitle": "Un e-mail de confirmation sera envoyé à l'adresse indiquée", + "form": { + "personalDetails": { + "title": "Vos informations personnelles", + "reason": "Motif de l'inscription" + }, + "loginDetails": { + "title": "Vos identifiants", + "usernameHelp": "Le nom d'utilisateur est généré automatiquement à partir de la première lettre de votre prénom et de votre nom. Il n'est pas modifiable.", + "passwordHelp": [ + "Votre mot de passe ne peut pas ressembler à vos informations personnelles.", + "Votre mot de passe doit contenir au minimum 8 caractères.", + "Votre mot de passe ne peut pas être un mot de passe couramment utilisé.", + "Votre mot de passe ne peut pas être entièrement numérique." + ] + }, + "organisation": { + "title": "Organisation", + "subtitle": "Sélectionnez votre organisation dans la liste ci-dessous.", + "placeholder": "Recherchez une organisation ...", + "noOptions": "Saississez les premiers caractères d'une organisation pour lancer la recherche", + "help": { + "text": "Si celle-ci n'est pas dans la liste, vous pouvez", + "link": " indiquer une nouvelle organisation." + } + } + } + }, + "signupSuccess": { + "message": [ + "Un e-mail de confirmation vient d'être envoyé à l'adresse indiquée.", + "Merci de bien vouloir suivre les instructions données afin de finaliser la création de votre compte." + ] + }, + "signout": { + "message": "Vous êtes maintenant déconnecté.", + "toSignIn": "Ouvrir la page de connexion" + }, + "signoutFailed": { + "message": "Une erreur est survenue lors de la déconnexion." + }, + "termsOfUse": { + "error": "Une erreur est survenue. Veuillez contacter l'administrateur du site.", + "download": "Télécharger les conditions d'utilisation" + }, + "profile": { + "title": "Compte existant", + "subtitle": "Modifiez les informations relatives à votre compte", + "form": { + "personalDetails": { + "title": "Vos informations personnelles", + "reason": "Motif de l'inscription", + "organisation": { + "label": "Organisation(s)/Groupe(s)", + "noOrganisation": "Vous n'êtes rattaché à aucune organisation.", + "help": "Pour toute demande de modification, merci de contacter" + } + }, + "emailChange": { + "title": "Changer votre adresse e-mail", + "label": "Nouvelle adresse e-mail", + "help": "Vous pouvez demander le changement de votre adresse mail en renseignant une nouvelle adresse. Un lien de confirmation vous sera envoyé sur la nouvelle adresse." + }, + "passwordChange": { + "title": "Changer votre mot de passe", + "oldLabel": "Ancien mot de passe", + "oldPlaceholder": "Ancien mot de passe", + "newLabel": "Nouveau mot de passe", + "newPlaceholder": "Nouveau mot de passe", + "confirmPlaceholder": "Confirmez mot de passe", + "passwordHelp": [ + "Votre mot de passe ne peut pas ressembler à vos informations personnelles.", + "Votre mot de passe doit contenir au minimum 8 caractères.", + "Votre mot de passe ne peut pas être un mot de passe couramment utilisé.", + "Votre mot de passe ne peut pas être entièrement numérique." + ] + } + } + }, + "validationEmail": { + "message": "Votre nouvelle adresse e-mail est validée.", + "goToLogin": "Ouvrir la page de connexion", + "error": "Une erreur est survenue. Veuillez contacter l'administrateur du site." + }, + "validationRegistration": { + "messages": { + "success": [ + "Merci pour votre inscription.", + "Vous recevrez une notification lors de l'activation de votre compte par l'administrateur de la plateforme." + ], + "error": "Une erreur est survenue. Veuillez contacter l'administrateur du site." + }, + "confirmation": "Pour confirmer votre inscription", + "clickHere": "Cliquez ici" + }, + "footer": "Propulsé par", + "403": "Page non accessible" +} \ No newline at end of file diff --git a/src/components/ImportImage.vue b/src/components/ImportImage.vue index ac1b29467c0b470609476703ccb7fe1ac8d392b5..e0907712644e4bcec25f147f12824dfaa0104333 100644 --- a/src/components/ImportImage.vue +++ b/src/components/ImportImage.vue @@ -25,10 +25,10 @@ > <img src="@/assets/icons/file_document_sheet.svg" - alt="Icône fichier" + alt="File icon" /> - <div v-if="type === 'logo'"><b-icon-plus />Ajouter un logo</div> - <div v-if="type === 'image'"><b-icon-plus />Ajouter une image</div> + <div v-if="type === 'logo'"><b-icon-plus />{{ $t('importImage.logo') }}</div> + <div v-if="type === 'image'"><b-icon-plus />{{ $t('importImage.image') }}</div> </div> <div> <input @@ -36,7 +36,7 @@ :hidden="!(fileExist || filePreview)" type="file" accept="image/*" - placeholder="Importer un fichier" + placeholder="$t('importImage.placeholder')" @change="previewFile" > </div> @@ -44,7 +44,6 @@ </template> <script> -import { mapState } from 'vuex'; export default { name: 'ImportImage', diff --git a/src/components/LocaleChanger.vue b/src/components/LocaleChanger.vue new file mode 100644 index 0000000000000000000000000000000000000000..2cf517754386f3662af787b6a49b233d08496ca3 --- /dev/null +++ b/src/components/LocaleChanger.vue @@ -0,0 +1,37 @@ +<template> + <div id="locale-changer"> + <select v-model="$i18n.locale"> + <option + v-for="(lang, i) in langs" + :key="`Lang${i}`" + :value="lang" + > + {{ lang }} + </option> + </select> + </div> +</template> + +<script> + +export default { + + name: 'LocaleChanger', + + data () { + return { + langs: ['fr', 'en'], + } + }, + + watch: { + '$i18n.locale': function(newValue, oldValue) { + if (newValue !== oldValue) { + const to = this.$router.resolve({ params: { locale: newValue } }) + this.$router.push(to.location); + } + } + } + +} +</script> diff --git a/src/components/OrganisationCreation.vue b/src/components/OrganisationCreation.vue index de088c4623a2aa0947cdd81b3b5c32ff7db33fc7..9f79368e7c59a97ad0e478a6e6a701c0e9418725 100644 --- a/src/components/OrganisationCreation.vue +++ b/src/components/OrganisationCreation.vue @@ -1,15 +1,15 @@ <template> <div> <b-button-close @click="$emit('cancel')" class="close"/> - <h6>Créer une nouvelle organisation</h6> + <h6>{{ $t('organisationCreation.title') }}</h6> <br> <form> <div class="form-row"> <div class="form-group col-11"> <ValidationProvider rules="required" v-slot="{ classes, errors }"> <div class="control" :class="classes"> - <label class="required">Nom de l'organisation</label> - <p>Inscrivez le nom complet et en toutes lettres ; par ex. « Communauté des Communes Rurales de l'Entre-Deux-Mers »</p> + <label class="required">{{ $t('organisationCreation.form.name') }}</label> + <p>{{ $t('organisationCreation.form.nameHelp') }}</p> <input v-model="formData.name" class="form-control" @@ -24,7 +24,7 @@ <div class="form-row"> <div class="form-group col-6"> <div class="control"> - <label>Sigle</label> + <label>{{ $t('organisationCreation.form.acronym') }}</label> <input v-model="formData.sigle" class="form-control" @@ -36,7 +36,7 @@ </div> <div class="form-row"> <div class="form-group col-6"> - <label>Numéro SIRET</label> + <label>{{ $t('organisationCreation.form.siret') }}</label> <input v-model="formData.siret" class="form-control" @@ -49,7 +49,7 @@ <div class="form-group col-6"> <ValidationProvider rules="required" v-slot="{ classes, errors }"> <div class="control" :class="classes"> - <label class="required">Type d'organisation</label> + <label class="required">{{ $t('organisationCreation.form.type') }}</label> <Multiselect v-model="formData.type" :options="organisationsTypes" @@ -59,7 +59,7 @@ selectedLabel="" deselectLabel="" :searchable="false" - placeholder="Sélectionnez un type" + :placeholder="$t('organisationCreation.form.typePlaceholder')" /> <span class="form-errors">{{ errors[0] }}</span> </div> @@ -68,8 +68,8 @@ </div> <div class="form-row"> <div class="form-group col-6"> - <label>Logo de l'organisation</label> - <p>Importez un logo au format JPG ou PNG (taille maximale : 2 Mo)</p> + <label>{{ $t('organisationCreation.form.logo') }}</label> + <p>{{ $t('organisationCreation.form.logoHelp') }}</p> <ValidationProvider ref="thumbnail" v-slot="{ classes, errors }"> <div class="control" :class="classes"> <ImportImage @@ -85,7 +85,7 @@ </div> <div class="form-row"> <div class="form-group col-9"> - <label>Site internet</label> + <label>{{ $t('organisationCreation.form.web') }}</label> <input v-model="formData.web" class="form-control" @@ -98,7 +98,7 @@ </div> <div class="form-row"> <div class="form-group col-6"> - <label>Numéro de téléphone</label> + <label>{{ $t('organisationCreation.form.phone') }}</label> <input v-model="formData.tel" class="form-control" @@ -109,7 +109,7 @@ </div> <div class="form-row"> <div class="form-group col-11"> - <label>Adresse postale</label> + <label>{{ $t('organisationCreation.form.address') }}</label> <textarea v-model="formData.postalAddress" class="form-control" @@ -118,7 +118,7 @@ </div> <div class="form-row"> <div class="form-group col-11"> - <label>Description de l'organisation</label> + <label>{{ $t('organisationCreation.form.description') }}</label> <textarea v-model="formData.description" class="form-control" @@ -128,13 +128,14 @@ <div class="form-row"> <div class="form-group col-11"> <label> - Ajouter cette organisation à des groupe d'organisations + {{ $t('organisationCreation.form.group.title') }} </label> <p> - L'ajout de cette organisation à un groupe d'organisation donnera accès aux données et collections de données associées à ce groupe d'organisation. + {{ $t('organisationCreation.form.group.help') }} </p> <SearchUsergroups :type="'group-of-organisation'" + :placeholder="$t('organisationCreation.form.group.placeholder')" @select="addOrgToSphere" /> <div @@ -166,11 +167,12 @@ import { mapActions } from 'vuex'; +import i18n from '@/i18n'; + import ImportImage from '@/components/ImportImage'; import SearchUsergroups from '@/components/SearchUsergroups'; import { - ValidationObserver, ValidationProvider, extend, configure @@ -182,7 +184,7 @@ import { extend('required', { ...required, - message: 'Ce champ est requis' + message: () => i18n.t('errors.required') }); configure({ @@ -196,7 +198,6 @@ export default { name: 'OrganisationCreation', components: { - ValidationObserver, ValidationProvider, ImportImage, SearchUsergroups diff --git a/src/components/OrganisationSelector.vue b/src/components/OrganisationSelector.vue index cd036e616b31fd7d1c179080f7d84e46c8e2fa84..85498f4e3f89695900fc945b4707ecb62f960f96 100644 --- a/src/components/OrganisationSelector.vue +++ b/src/components/OrganisationSelector.vue @@ -1,9 +1,9 @@ <template> <div> - <h5>Organisation</h5> + <h5>{{ $tc('signup.form.organisation.title') }}</h5> <hr class="divider" /> <div v-if="!showCreationForm"> - <label>Sélectionnez votre organisation dans la liste ci-dessous.</label> + <label>{{ $tc('signup.form.organisation.subtitle') }}</label> <Multiselect v-model="organisation" style="margin-top: 0.5em;" @@ -18,7 +18,7 @@ selectedLabel="" deselectLabel="" :searchable="true" - placeholder="Recherchez une organisation ..." + :placeholder="$tc('signup.form.organisation.placeholder')" :showNoResults="true" :loading="loading" :clearOnSelect="false" @@ -35,16 +35,16 @@ </div> </template> <span slot="noResult"> - Aucun résultat. + {{ $t('multiselect.noResult') }} </span> <span slot="noOptions"> - Saississez les premiers caractères d'une organisation pour lancer la recherche + {{ $t('signup.form.organisation.noOptions') }} </span> </Multiselect> <label> - Si celle-ci n'est pas dans la liste, vous pouvez + {{ $t('signup.form.organisation.help.text') }} <b-link @click="showCreationForm = true"> - indiquer une nouvelle organisation. + {{ $t('signup.form.organisation.help.link') }} </b-link> </label> </div> diff --git a/src/components/SearchUsergroups.vue b/src/components/SearchUsergroups.vue index 58a4164ab5244a8fb085bee276651cb22bbc2ee5..7f9bd794621a67e57f5b97d2fcb0c1e34521b272 100644 --- a/src/components/SearchUsergroups.vue +++ b/src/components/SearchUsergroups.vue @@ -32,10 +32,10 @@ </div> </template> <span slot="noResult"> - Aucun résultat. + {{ $t('multiselect.noResult') }} </span> <span slot="noOptions"> - Saisissez les premiers caractères ... + {{ $t('multiselect.noOptions') }} </span> </Multiselect> </div> diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 0000000000000000000000000000000000000000..f61f8fa1db8750ad2b57b28cc9c0274ef2983bd3 --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,69 @@ +import Vue from 'vue'; +import VueI18n from 'vue-i18n'; + +import en from '@/assets/locales/en.json'; +import fr from '@/assets/locales/fr.json'; + +// const defaultImpl = VueI18n.prototype.getChoiceIndex; + +// VueI18n.prototype.getChoiceIndex = function(choice, choicesLength) { +// // this === VueI18n instance, so the locale property also exists here +// if (this.locale !== 'fr') { +// // proceed to the default implementation +// return defaultImpl.apply(this, arguments) +// } + +// if (choice === 0) { +// return 0; +// } + +// const teen = choice > 10 && choice < 20; +// const endsWithOne = choice % 10 === 1; + +// if (!teen && endsWithOne) { +// return 1; +// } + +// if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { +// return 2; +// } + +// return (choicesLength < 4) ? 2 : 3; +// } + +Vue.use(VueI18n); + +const dateTimeFormats = { + 'en': { + short: { + year: 'numeric', + month: 'short', + day: 'numeric' + } + }, + 'fr': { + short: { + year: 'numeric', + month: 'numeric', + day: 'numeric' + } + } +}; + +const numberFormats = { + 'fr': { + currency: { + style: 'currency', currency: 'EUR' + } + } +}; + +const i18n = new VueI18n({ + locale: process.env.VUE_APP_I18N_DEFAULT_LOCALE || 'fr', + fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'fr', + messages: { en, fr }, + dateTimeFormats, + numberFormats +}); + +export default i18n; diff --git a/src/main.js b/src/main.js index 2a6a2499f4c07fd25b65bc6fcc759d08fe7209a4..70b2c34660bc793f025440641fbb74516175cf8a 100644 --- a/src/main.js +++ b/src/main.js @@ -13,12 +13,14 @@ Vue.use(BootstrapVueIcons); Vue.component('Multiselect', Multiselect); import App from '@/App.vue'; +import i18n from './i18n'; import router from '@/router'; import store from '@/store'; Vue.config.productionTip = false; new Vue({ + i18n, router, store, render: (h) => h(App), diff --git a/src/router/index.js b/src/router/index.js index 9e6e3eccacf66bffe621820728db2718fddf2375..93af3fe1ba25bdb17a1117cd3a4487c465543f00 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,73 +1,97 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; +import i18n from '@/i18n'; Vue.use(VueRouter); const routes = [ { - path: '/', - redirect: { name: 'SignIn' }, + path: '/:locale', + component: () => import('@/views/Base.vue'), + beforeEnter: (to, from, next) => { + const locale = to.params.locale; + const supported_locales = process.env.VUE_APP_I18N_SUPPORTED_LOCALE.split(','); + if (!supported_locales.includes(locale)) { + return next('fr'); + } + if (i18n.locale !== locale) { + i18n.locale = locale; + } + return next(); + }, + children: [ + { + path: '', + redirect: { name: 'SignIn' }, + }, + { + path: 'signin', + name: 'SignIn', + component: () => import('@/views/SignIn.vue'), + }, + { + path: 'signup', + name: 'SignUp', + component: () => import('@/views/SignUp.vue'), + }, + { + path: 'terms-of-use', + name: 'TermsOfUse', + component: () => import('@/views/TermsOfUse.vue'), + }, + { + path: 'signout', + name: 'SignOut', + component: () => import('@/views/SignOut.vue'), + }, + { + path: 'signout-failed', + name: 'SignOutFailed', + component: () => import('@/views/SignOutFailed.vue'), + }, + { + path: 'signupsuccess', + name: 'SignUpSuccess', + component: () => import('@/views/SignUpSuccess.vue'), + }, + { + path: 'validateregistration', + name: 'ValidationRegistration', + component: () => import('@/views/ValidationRegistration.vue'), + }, + { + path: 'validate-email', + name: 'ValidationEmail', + component: () => import('@/views/ValidationEmail.vue'), + }, + { + path: 'forgottenpwd', + name: 'ForgottenPassword', + component: () => import('@/views/ForgottenPassword.vue'), + }, + { + path: 'reinitpwd', + name: 'ReinitPassword', + component: () => import('@/views/ReinitPassword.vue'), + }, + { + path: 'profile', + name: 'UserProfile', + component: () => import('@/views/UserProfile.vue'), + }, + { + path: '*', + name: 'NotFound', + component: () => import('@/views/NotFound.vue'), + }, + ] }, { - path: '/signin', - name: 'SignIn', - component: () => import('@/views/SignIn.vue'), - }, - { - path: '/signup', - name: 'SignUp', - component: () => import('@/views/SignUp.vue'), - }, - { - path: '/terms-of-use', - name: 'TermsOfUse', - component: () => import('@/views/TermsOfUse.vue'), - }, - { - path: '/signout', - name: 'SignOut', - component: () => import('@/views/SignOut.vue'), - }, - { - path: '/signout-failed', - name: 'SignOutFailed', - component: () => import('@/views/SignOutFailed.vue'), - }, - { - path: '/signupsuccess', - name: 'SignUpSuccess', - component: () => import('@/views/SignUpSuccess.vue'), - }, - { - path: '/validateregistration', - name: 'ValidationRegistration', - component: () => import('@/views/ValidationRegistration.vue'), - }, - { - path: '/validate-email', - name: 'ValidationEmail', - component: () => import('@/views/ValidationEmail.vue'), - }, - { - path: '/forgottenpwd', - name: 'ForgottenPassword', - component: () => import('@/views/ForgottenPassword.vue'), - }, - { - path: '/reinitpwd', - name: 'ReinitPassword', - component: () => import('@/views/ReinitPassword.vue'), - }, - { - path: '/profile', - name: 'UserProfile', - component: () => import('@/views/UserProfile.vue'), - }, - { - path: '/*', - name: 'NotFound', - component: () => import('@/views/NotFound.vue'), - }, + path: '*', + redirect() { + return process.env.VUE_APP_I18N_DEFAULT_LOCALE; + } + } ]; const router = new VueRouter({ diff --git a/src/store/modules/forgotten-pwd.store.js b/src/store/modules/forgotten-pwd.store.js index bb39585875c45520b054436eded51d652e121e8b..d9338ef7b70723c80abf46b0f9ba76f85e7e6370 100644 --- a/src/store/modules/forgotten-pwd.store.js +++ b/src/store/modules/forgotten-pwd.store.js @@ -3,6 +3,7 @@ import { ErrorService } from '@/services/error-service.js'; import Swal from "sweetalert2"; import "sweetalert2/dist/sweetalert2.min.css"; import router from '@/router'; +import i18n from '@/i18n'; const state = { error: null, @@ -23,9 +24,7 @@ const actions = { position: 'center', heightAuto: false, icon: 'success', - text: ` - Un e-mail vous a été envoyé sur votre messagerie pour réinitialiser votre mot de passe. - `, + text: i18n.t('messages.reinitPwdRequest.success'), showConfirmButton: true, confirmButtonText: 'OK', confirmButtonColor: '#187CC6' @@ -38,7 +37,7 @@ const actions = { if (response.status === 404) { commit('SET_ERROR', { response: response, - message: 'Aucun compte trouvé pour cette adresse e-mail.' + message: i18n.t('messages.reinitPwdRequest.error') }); } }, @@ -50,9 +49,7 @@ const actions = { position: 'center', heightAuto: false, icon: 'success', - text: `Votre mot de passe a bien été réinitialisé. - Vous allez être redirigé vers la page de connexion. - `, + text: i18n.t('messages.reinitPwdConfirm.success'), showConfirmButton: true, confirmButtonText: 'OK', confirmButtonColor: '#187CC6' @@ -66,9 +63,7 @@ const actions = { position: 'center', heightAuto: false, icon: 'error', - text: `Votre token a expiré. Vous devez en demander un nouveau - pour changer votre mot de passe. - `, + text: i18n.t('messages.reinitPwdConfirm.error'), showConfirmButton: true, confirmButtonText: 'OK', confirmButtonColor: '#187CC6' diff --git a/src/store/modules/sign-in.store.js b/src/store/modules/sign-in.store.js index fb3bd4cbd2169976a3b782e8ca2e5af217734584..89cb53d1234678ddbec8b07e883009c8ec5e87b5 100644 --- a/src/store/modules/sign-in.store.js +++ b/src/store/modules/sign-in.store.js @@ -1,4 +1,5 @@ import client from '@/api/loginAPI.js'; +import i18n from '@/i18n'; const state = { username: null, @@ -32,7 +33,7 @@ const actions = { commit( 'SET_ERROR', error.response.data.detail - || 'Nom d\'utilisateur et/ou mot de passe incorrect(s)', + || i18n.t('messages.loginError'), ); commit('SET_LOGGED', false); } diff --git a/src/store/modules/sign-out.store.js b/src/store/modules/sign-out.store.js index f9377c8c422164625d37646d779795fe134e8ea4..b60d81a0820a932994756721304964ea7c95c248 100644 --- a/src/store/modules/sign-out.store.js +++ b/src/store/modules/sign-out.store.js @@ -1,4 +1,5 @@ import client from '@/api/loginAPI.js'; +import i18n from '@/i18n'; const state = { logged: null, @@ -24,7 +25,7 @@ const actions = { commit( 'SET_ERROR', error.response.data - || 'Une erreur est survenue', + || i18n.t('messages.error'), ); commit('SET_LOGGED', true); }, diff --git a/src/store/modules/sign-up.store.js b/src/store/modules/sign-up.store.js index 12a14472901f60eeaa9ced5ea6bfa8ae44074ae2..494845bf7b2adfdf6e94dd6f6c916e1a288b0761 100644 --- a/src/store/modules/sign-up.store.js +++ b/src/store/modules/sign-up.store.js @@ -1,6 +1,7 @@ import client from '@/api/loginAPI.js'; import organisationAPI from '@/api/organisationsAPI.js'; import usergroupsAPI from '@/api/usergroupsAPI.js'; +import i18n from '@/i18n'; import { ErrorService } from '@/services/error-service.js'; @@ -43,7 +44,7 @@ const actions = { commit( 'SET_ERROR', error.response - || 'Une erreur est survenue', + || i18n.t('messages.error'), ); commit('SET_SIGNED', false); }); @@ -68,7 +69,7 @@ const actions = { commit( 'SET_ERROR', error.response - || 'Une erreur est survenue', + || i18n.t('messages.error'), ); commit('SET_SIGNED', false); }); @@ -82,7 +83,7 @@ const actions = { commit( 'SET_ERROR', error.response - || 'Une erreur est survenue', + || i18n.t('messages.error'), ); commit('SET_SIGNED', false); }, diff --git a/src/store/modules/terms-of-use.store.js b/src/store/modules/terms-of-use.store.js index 076d29fb39b23ab5aaad2a6cb6389ebbe914dcac..6d5f30a1548449223d59e918ecad5ac00b0fca45 100644 --- a/src/store/modules/terms-of-use.store.js +++ b/src/store/modules/terms-of-use.store.js @@ -1,6 +1,7 @@ import client from '@/api/loginAPI.js'; import { ErrorService } from '@/services/error-service.js'; import router from '@/router'; +import i18n from '@/i18n'; const state = { terms: null, @@ -27,7 +28,7 @@ const actions = { if (response.status === 404) { commit('SET_ERROR', { response: response, - message: 'Une erreur est survenue.' + message: i18n.t('messages.error') }); } }, @@ -43,7 +44,7 @@ const actions = { if (response.status === 404) { commit('SET_ERROR', { response: response, - message: 'Une erreur est survenue.' + message: i18n.t('messages.error') }); } } diff --git a/src/store/modules/user.store.js b/src/store/modules/user.store.js index e5b7ea602ced121c19f62093929fbf4f95df33a9..632050e334ab1f67342a584270f46263240ec1a2 100644 --- a/src/store/modules/user.store.js +++ b/src/store/modules/user.store.js @@ -1,6 +1,7 @@ import loginAPI from '@/api/loginAPI.js'; import { ErrorService } from '@/services/error-service.js'; +import i18n from '@/i18n'; // MUTATIONS export const SET_ERROR = 'SET_ERROR'; @@ -68,7 +69,7 @@ const actions = { commit('SET_ERROR', null); commit('SET_SUCCESS', { response: resp, - message: 'Votre mot de passe a bien été modifié.' + message: i18n.t('messages.password.success') }); } }) diff --git a/src/views/Base.vue b/src/views/Base.vue new file mode 100644 index 0000000000000000000000000000000000000000..0afb5bdcf92765b35f1863a28418ce7d9aaa2fea --- /dev/null +++ b/src/views/Base.vue @@ -0,0 +1,9 @@ +<template> + <router-view /> +</template> + +<script> +export default { + name: 'Base' +} +</script> \ No newline at end of file diff --git a/src/views/ForgottenPassword.vue b/src/views/ForgottenPassword.vue index 8c1cef9c36adbbaebc2e7d66b479f585b90cd933..787f46691f88085ba7806e0985ce13cbb5f9a36e 100644 --- a/src/views/ForgottenPassword.vue +++ b/src/views/ForgottenPassword.vue @@ -5,17 +5,17 @@ <img alt="logo" :src="logoPath"/> </div> <div class="form"> - <h5 class="title">Réinitialisez votre mot de passe</h5> + <h5 class="title">{{ $t('forgottenPassword.title') }}</h5> <form> <div class="form-row"> <label> - Veuillez indiquer l'adresse e-mail de votre compte + {{ $t('forgottenPassword.message') }} </label> <input v-model="email" class="form-control" type="text" - placeholder="Adresse e-mail" + :placeholder="$t('forgottenPassword.placeholder')" > </div> <button @@ -23,12 +23,12 @@ class="btn btn-primary" @click.prevent="reinitPassword" > - Envoyer un e-mail de réinitialisation + {{ $t('forgottenPassword.confirmationButton') }} </button> <button class="btn btn-outline-secondary" @click="$router.push({ name: 'SignIn' })"> - Annuler + {{ $t('words.cancel') }} </button> </form> </div> @@ -39,7 +39,7 @@ </div> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/ReinitPassword.vue b/src/views/ReinitPassword.vue index af22675d2f0054bb0cc8c7d6ccb1354a4cc51c95..1068e01ed8f5092e08a90b56056d6dec68270abc 100644 --- a/src/views/ReinitPassword.vue +++ b/src/views/ReinitPassword.vue @@ -15,8 +15,8 @@ > </b-overlay> <div class="form"> - <h5 class="title">Choisissez un nouveau mot de passe</h5> - <ValidationObserver v-slot="{ handleSubmit }"> + <h5 class="title">{{ $t('reinitPassword.title') }}</h5> + <ValidationObserver ref="form" v-slot="{ handleSubmit }"> <form> <div class="form-row"> <ValidationProvider @@ -27,14 +27,14 @@ > <div class="control" :class="classes"> <label class="required"> - Nouveau de mot de passe + {{ $t('reinitPassword.newLabel') }} </label> <div class="input-group flex-nowrap"> <input v-model="password1" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Mot de passe" + :placeholder="$t('words.password')" > <span v-if="!showPassword" @@ -63,14 +63,14 @@ <ValidationProvider ref="password" rules="required|confirmed:confirmation" v-slot="{ classes, errors }"> <div class="control" :class="classes"> <label class="required"> - Confirmez le mot de passe + {{ $t('reinitPassword.confirmLabel') }} </label> <div class="input-group flex-nowrap"> <input v-model="password2" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Mot de passe" + :placeholder="$t('words.password')" > <span v-if="!showPassword" @@ -97,10 +97,10 @@ </div> <div class="infos"> <ul> - <li>Votre mot de passe ne peut pas trop ressembler à vos autres informations personnelles.</li> - <li>Votre mot de passe doit contenir au minimum 8 caractères.</li> - <li>Votre mot de passe ne peut pas être un mot de passe couramment utilisé.</li> - <li>Votre mot de passe ne peut pas être entièrement numérique.</li> + <li>{{ $t('reinitPassword.passwordHelp')[0] }}</li> + <li>{{ $t('reinitPassword.passwordHelp')[1] }}</li> + <li>{{ $t('reinitPassword.passwordHelp')[2] }}</li> + <li>{{ $t('reinitPassword.passwordHelp')[3] }}</li> </ul> </div> <button @@ -108,14 +108,14 @@ class="btn btn-primary" @click.prevent="handleSubmit(sendNewPassword)" > - Sauvegarder + {{ $t('words.save') }} </button> <button type="button" class="btn btn-outline-secondary" @click="$router.push({ name: 'SignIn' })" > - Annuler + {{ $t('words.cancel') }} </button> </form> </ValidationObserver> @@ -130,7 +130,7 @@ </div> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> @@ -138,6 +138,8 @@ <script> import { mapState, mapActions } from 'vuex'; +import i18n from '@/i18n'; + import { ValidationObserver, ValidationProvider, @@ -149,12 +151,12 @@ import { required, confirmed, regex } from 'vee-validate/dist/rules'; extend('required', { ...required, - message: 'Ce champ est requis', + message: () => i18n.t('errors.required'), }); extend('confirmed', { ...confirmed, - message: 'Les mots de passe doivent être identiques', + message: () => i18n.t('errors.confirmPassword'), }); configure({ @@ -166,7 +168,7 @@ configure({ extend('regex', { ...regex, - message: 'Votre mot de passe doit comporter au moins 8 caractères, dont au moins une majuscule, une minuscule et 1 chiffre.', + message: i18n.t('errors.regexPassword'), }); export default { @@ -190,7 +192,19 @@ export default { ...mapState('forgotten-pwd', [ 'error', 'success' - ]) + ]), + + logoPath() { + return require(process.env.VUE_APP_LOGO); + } + }, + + watch: { + '$i18n.locale': function(newValue, oldValue) { + if (newValue !== oldValue) { + this.$refs.form.validate(); + } + }, }, methods: { diff --git a/src/views/SignIn.vue b/src/views/SignIn.vue index 32f0ad7a08e5fd05ac9eef3d5cfad1098d726718..20404fffeea14314e7cffde21b26157dc70766f1 100644 --- a/src/views/SignIn.vue +++ b/src/views/SignIn.vue @@ -5,14 +5,14 @@ <img alt="logo" :src="logoPath"/> </div> <div class="sign-in-form"> - <h5 class="title">Saississez vos identifiants {{ clientName }}</h5> + <h5 class="title">{{ $t('signin.title', { org: clientName}) }}</h5> <form> <div class="form-row"> <input v-model="form.username" class="form-control" type="text" - placeholder="Nom d'utilisateur" + :placeholder="$t('words.username')" v-on:keydown.enter.prevent="submit" > </div> @@ -22,7 +22,7 @@ v-model="form.password" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Mot de passe" + :placeholder="$t('words.password')" v-on:keydown.enter.prevent="submit" > <span @@ -58,7 +58,7 @@ variant="primary" block > - Se connecter + {{ $t('words.login') }} </b-button> </div> <div class="form-row attention-message"> @@ -67,27 +67,23 @@ height="100" /> <p> - Vous aviez déjà un compte {{ clientName }} et il s’agit de votre première connexion sur la nouvelle plateforme ? - Si tel est le cas, pour des raisons de conformité RGPD, il vous faut regénérer votre mot de passe via - la fonction « Mot de passe oublié ? » disponible ci-dessous. - Veuillez renseigner l’adresse mail liée à votre compte {{ clientName }}. - Si vous rencontrez un problème veuillez nous contacter à l’adresse mail suivante : + {{ $t('signin.warning', { org: clientName }) }} <a :href="`mailto:${clientMail}`">{{ clientMail }}</a> </p> </div> <div class="form-row"> <div class="btn-group-vertical"> <router-link - to="/signup" + to="signup" custom > - Pas encore de compte ? + {{ $t('signin.noaccount') }} ? </router-link> <router-link - to="/forgottenpwd" + to="forgottenpwd" custom > - Mot de passe oublié ? + {{ $t('signin.forgottenpassword') }} ? </router-link> </div> </div> @@ -95,7 +91,7 @@ </div> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/SignOut.vue b/src/views/SignOut.vue index e7c432ba6caf81412c5468677e057c05c1c44b3f..25c23b2183092582af2f58e5268d6ad9637b2830 100644 --- a/src/views/SignOut.vue +++ b/src/views/SignOut.vue @@ -6,15 +6,15 @@ <div class="center"> <b-container class="msg"> <p> - Vous êtes maintenant déconnecté.<br> + {{ $t('signout.message') }}<br> </p> <b-button type="button" variant="outline-primary" @click.prevent="$router.push({ name: 'SignIn' })"> - Ouvrir la page de connexion + {{ $t('signout.toSignIn') }} </b-button> </b-container> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/SignOutFailed.vue b/src/views/SignOutFailed.vue index fecd5de903398fce23cc8af88011fafffb70bd82..2b605523fae5afd435b4805b2c25f8320865c132 100644 --- a/src/views/SignOutFailed.vue +++ b/src/views/SignOutFailed.vue @@ -19,12 +19,12 @@ Une erreur est survenue lors de la déconnexion.<br> </p> <b-button type="button" variant="outline-primary" @click.prevent="retrySignout"> - Réessayer + {{ $t('words.retry') }} </b-button> </b-container> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/SignUp.vue b/src/views/SignUp.vue index b267c60be26e0b0b35d82564401430ad1371aad0..5c7d51e68c9081102a4dc41479a4b392f59729ed 100644 --- a/src/views/SignUp.vue +++ b/src/views/SignUp.vue @@ -16,18 +16,18 @@ </b-overlay> <div class="signup-form"> <h4 class="title"> - Nouveau compte - <span class="sub-title">Sollicitez la création d'un compte</span> + {{ $t('signup.title') }} + <span class="sub-title">{{ $t('signup.subtitle') }}</span> </h4> <hr class="divider"> - <h5>Un e-mail de confirmation sera envoyé à l'adresse indiquée</h5> - <ValidationObserver v-slot="{ handleSubmit }"> + <h5>{{ $t('signup.secondTitle') }}</h5> + <ValidationObserver ref="form" v-slot="{ handleSubmit }"> <form> - <h5>Vos informations personnelles</h5> + <h5>{{ $t('signup.form.personalDetails.title') }}</h5> <hr class="divider"> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label required">Prénom</label> + <label class="col-form-label required">{{ $t('words.lastname') }}</label> </div> <div class="col"> <ValidationProvider ref="first_name" rules="required" v-slot="{ classes, errors }"> @@ -36,7 +36,7 @@ v-model="form.first_name" type="text" class="form-control" - placeholder="Prénom" + :placeholder="$t('words.firstname')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -45,7 +45,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label required">Nom</label> + <label class="col-form-label required">{{ $t('words.lastname') }}</label> </div> <div class="col"> <ValidationProvider ref="last_name" rules="required" v-slot="{ classes, errors }"> @@ -54,7 +54,7 @@ v-model="form.last_name" type="text" class="form-control" - placeholder="Nom" + :placeholder="$t('words.lastname')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -63,7 +63,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label required">Adresse e-mail</label> + <label class="col-form-label required">{{ $t('words.email') }}</label> </div> <div class="col"> <ValidationProvider ref="email" rules="required|email" v-slot="{ classes, errors }"> @@ -72,7 +72,7 @@ v-model="form.email" type="mail" class="form-control" - placeholder="Adresse e-mail" + :placeholder="$t('words.email')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -81,7 +81,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Numéro de téléphone</label> + <label class="col-form-label">{{ $t('words.phone') }}</label> </div> <div class="col"> <ValidationProvider ref="phone_number" v-slot="{ classes, errors }"> @@ -99,7 +99,9 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Motif de l'inscription</label> + <label class="col-form-label"> + {{ $t('signup.form.personalDetails.reason') }} + </label> </div> <div class="col"> <textarea @@ -108,11 +110,11 @@ /> </div> </div> - <h5>Vos identifiants</h5> + <h5>{{ $t('signup.form.loginDetails.title') }}</h5> <hr class="divider"> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Nom d'utilisateur</label> + <label class="col-form-label">{{ $t('words.username') }}</label> </div> <div class="col"> <ValidationProvider ref="username" rules="required" v-slot="{ classes, errors }"> @@ -138,9 +140,7 @@ </div> <div class="col"> <p class="infos"> - Le nom d'utilisateur est généré automatiquement à partir de la - première lettre de votre prénom et de votre nom. - Il n'est pas modifiable. + {{ $t('signup.form.loginDetails.usernameHelp') }} </p> </div> </div> @@ -149,7 +149,7 @@ <label class="col-form-label required" style="padding: 0;" - >Mot de passe + >{{ $t('words.password') }} </label> </div> <div class="col"> @@ -165,7 +165,7 @@ v-model="form.password1" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Mot de passe" + :placeholder="$t('words.password')" > <span v-if="!showPassword" @@ -201,7 +201,7 @@ v-model="form.password2" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Mot de passe" + :placeholder="$t('words.password')" > <span v-if="!showPassword" @@ -233,10 +233,10 @@ <div class="col"> <div class="infos"> <ul> - <li>Votre mot de passe ne peut pas ressembler à vos informations personnelles.</li> - <li>Votre mot de passe doit contenir au minimum 8 caractères.</li> - <li>Votre mot de passe ne peut pas être un mot de passe couramment utilisé.</li> - <li>Votre mot de passe ne peut pas être entièrement numérique.</li> + <li>{{ $t('signup.form.loginDetails.passwordHelp')[0] }}</li> + <li>{{ $t('signup.form.loginDetails.passwordHelp')[1] }}</li> + <li>{{ $t('signup.form.loginDetails.passwordHelp')[2] }}</li> + <li>{{ $t('signup.form.loginDetails.passwordHelp')[3] }}</li> </ul> </div> </div> @@ -248,10 +248,10 @@ @click.prevent="handleSubmit(submit)" variant="primary" > - Valider + {{ $t('words.validate') }} </b-button> <b-button type="button" variant="outline-primary" @click.prevent="$router.push({ name: 'SignIn' })"> - Annuler + {{ $t('words.cancel') }} </b-button> </form> </ValidationObserver> @@ -259,7 +259,7 @@ </div> <small class="footer"> <p> - Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a> + {{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a> </p> </small> </div> @@ -290,6 +290,8 @@ import OrganisationSelector from '@/components/OrganisationSelector'; import { deburr } from 'lodash'; +import i18n from '@/i18n'; + import { ValidationObserver, ValidationProvider, @@ -301,22 +303,22 @@ import { required, email, confirmed, regex } from 'vee-validate/dist/rules'; extend('required', { ...required, - message: 'Ce champ est requis', + message: () => i18n.t('errors.required'), }); extend('email', { ...email, - message: 'Veuillez entrer une adresse e-mail valide', + message: () => i18n.t('errors.email'), }); extend('confirmed', { ...confirmed, - message: 'Les mots de passe doivent être identiques', + message: () => i18n.t('errors.confirmPassword'), }); extend('regex', { ...regex, - message: 'Votre mot de passe doit comporter au moins 8 caractères, dont au moins une majuscule, une minuscule et 1 chiffre.', + message: () => i18n.t('errors.regexPassword'), }); configure({ @@ -368,6 +370,12 @@ export default { } }, watch: { + '$i18n.locale': function(newValue, oldValue) { + if (newValue !== oldValue) { + this.$refs.form.validate(); + } + }, + 'form.first_name': { deep: true, handler(newValue) { diff --git a/src/views/SignUpSuccess.vue b/src/views/SignUpSuccess.vue index 4e09240f878a0eb1fe57c4cfaebcf6121302d6c8..5f272420969687fa6918534e3efd483b23a5a147 100644 --- a/src/views/SignUpSuccess.vue +++ b/src/views/SignUpSuccess.vue @@ -6,8 +6,8 @@ <div class="center"> <b-container class="msg"> <p> - Un e-mail de confirmation vient d'être envoyé à l'adresse indiquée.<br> - Merci de bien vouloir suivre les instructions données afin de finaliser la création de votre compte. + {{ $t('signupSuccess.message')[0] }}<br> + {{ $t('signupSuccess.message')[1] }} </p> <!-- <b-button type="button" variant="outline-primary" @click.prevent="$router.push({ name: 'SignIn' })"> Ouvrir la page de connexion @@ -15,7 +15,7 @@ </b-container> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/TermsOfUse.vue b/src/views/TermsOfUse.vue index 273df735440f5c372514ff83966e28539f4c5d00..e2a1dc2c3273d3eb8faab173eb8733c99207deb0 100644 --- a/src/views/TermsOfUse.vue +++ b/src/views/TermsOfUse.vue @@ -28,7 +28,9 @@ }) : '' }}<br> </div> <div v-html="terms ? terms.body : ''"></div> - <a v-if="terms && terms.file" :href="terms.file" target="_blank">Télécharger les conditions d'utilisation</a> + <a v-if="terms && terms.file" :href="terms.file" target="_blank"> + {{ $t('termsOfUse.download') }} + </a> </div> <div class="terms-footer"> <button @@ -36,14 +38,14 @@ class="btn btn-primary" @click.prevent="acceptTermsOfUse" > - Accepter + {{ $t('words.accept') }} </button> <button type="button" class="btn btn-outline-secondary" @click="$router.push({ name: 'SignIn' })" > - Refuser + {{ $t('words.decline') }} </button> </div> </b-overlay> @@ -51,7 +53,7 @@ <div v-else class="terms-container"> <b-container class="msg"> <p> - Une erreur est survenue. Veuillez contacter l'administrateur du site. + {{ $t('termsOfUse.error') }} </p> </b-container> </div> diff --git a/src/views/UserProfile.vue b/src/views/UserProfile.vue index 8894939d8cefa126c61971e918ffb19bde327534..85face7378edd84b5738aa85845af32a0aa20e71 100644 --- a/src/views/UserProfile.vue +++ b/src/views/UserProfile.vue @@ -6,7 +6,7 @@ @click="goBackToNext" > <b-icon-arrow-bar-left/> - Revenir + {{ $t('words.goBack') }} </b-button> <div class="signup-container"> <div class="signup-header"> @@ -14,11 +14,11 @@ </div> <div class="signup-form"> <h4 class="title"> - Compte existant - <span class="sub-title">Modifiez les informations relatives à votre compte</span> + {{ $t('profile.title') }} + <span class="sub-title">{{ $t('profile.subtitle') }}</span> </h4> <hr class="divider"> - <ValidationObserver v-slot="{ handleSubmit }"> + <ValidationObserver ref="form" v-slot="{ handleSubmit }"> <b-overlay :show="loadingUserInformation" rounded="lg" @@ -26,11 +26,11 @@ variant="white" > <form> - <h5>Vos informations personnelles</h5> + <h5>{{ $t('profile.form.personalDetails.title') }}</h5> <hr class="divider"> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Nom d'utilisateur</label> + <label class="col-form-label">{{ $t('words.username') }}</label> </div> <div class="col"> <div class="input-group flex-nowrap"> @@ -38,7 +38,7 @@ v-model="formUser.username" type="text" class="form-control" - placeholder="Nom d'utilisateur" + :placeholder="$t('words.username')" disabled > </div> @@ -46,7 +46,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label required">Prénom</label> + <label class="col-form-label required">{{ $t('words.firstname') }}</label> </div> <div class="col"> <ValidationProvider ref="first_name" rules="required" v-slot="{ classes, errors }"> @@ -55,7 +55,7 @@ v-model="formUser.first_name" type="text" class="form-control" - placeholder="Prénom" + :placeholder="$t('words.firstname')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -64,7 +64,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label required">Nom</label> + <label class="col-form-label required">{{ $t('words.lastname') }}</label> </div> <div class="col"> <ValidationProvider ref="last_name" rules="required" v-slot="{ classes, errors }"> @@ -73,7 +73,7 @@ v-model="formUser.last_name" type="text" class="form-control" - placeholder="Nom" + :placeholder="$t('words.lastname')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -83,7 +83,7 @@ <div class="row g-2 align-items-center"> <div class="col-3"> <label class="col-form-label"> - Adresse e-mail + {{ $t('words.email') }} </label> </div> <div class="col"> @@ -93,7 +93,7 @@ v-model="formUser.email" type="mail" class="form-control" - placeholder="Adresse e-mail" + :placeholder="$t('words.email')" disabled > <span class="form-errors">{{ errors[0] }}</span> @@ -103,7 +103,7 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Numéro de téléphone</label> + <label class="col-form-label">{{ $t('words.phone') }}</label> </div> <div class="col"> <ValidationProvider ref="phone_number" v-slot="{ classes, errors }"> @@ -121,7 +121,9 @@ </div> <div class="row g-2 align-items-center"> <div class="col-3"> - <label class="col-form-label">Motif de l'inscription</label> + <label class="col-form-label"> + {{ $t('profile.form.personalDetails.reason') }} + </label> </div> <div class="col"> <textarea @@ -132,11 +134,16 @@ </div> <div class="row g-2 align-items-center" style="margin-bottom: 0.5em;"> <div class="col-3"> - <label class="col-form-label" style="font-size: 0.87em;">Organisation(s)/Groupe(s)</label> + <label + class="col-form-label" + style="font-size: 0.87em;" + > + {{ $t('profile.form.personalDetails.organisation.label') }} + </label> </div> <div v-if="userData" class="col"> <div v-if="userData.usergroup_roles.length === 0"> - Vous n'êtes rattaché à aucune organisation. + {{ $t('profile.form.personalDetails.organisation.noOrganisation') }} </div> <b-list-group v-else @@ -147,7 +154,7 @@ disabled > <b>{{ usergroup.usergroup.display_name }}</b> - <em> en tant que </em> + <em> {{ $t('as') }} </em> <b>{{ organisationsRoles.find(el => el.choice === usergroup.role).label }}</b> </b-list-group-item> </b-list-group> @@ -160,7 +167,7 @@ <div style="margin: 0 0.5em 0.5em 0.1em; font-size: 0.9em; font-style: italic;" > - Pour toute demande de modification, merci de contacter <a :href="`mailto:${adminMail}`">{{ adminMail }}</a>. + {{ $t('profile.form.personalDetails.organisation.help') }} <a :href="`mailto:${adminMail}`">{{ adminMail }}</a>. </div> </div> </div> @@ -171,7 +178,7 @@ @click.prevent="handleSubmit(submitUserInformations)" variant="primary" > - Valider + {{ $t('words.validate') }} </b-button> </div> </b-overlay> @@ -185,11 +192,16 @@ :style="'padding: 5px;'" > <form> - <h5>Changer votre adresse e-mail</h5> + <h5>{{ $t('profile.form.emailChange.title') }}</h5> <hr class="divider"> <div class="row g-2 align-items-center"> <div class="col-3" style="padding-right: 0;"> - <label class="col-form-label required" style="font-size: 0.9em;">Nouvelle adresse e-mail</label> + <label + class="col-form-label required" + style="font-size: 0.9em;" + > + {{ $t('profile.form.emailChange.label') }} + </label> </div> <div class="col"> <ValidationProvider ref="email" rules="email" v-slot="{ classes, errors }"> @@ -198,7 +210,7 @@ v-model="formEmail.new_email" type="mail" class="form-control" - placeholder="Adresse e-mail" + :placeholder="$t('words.email')" > <span class="form-errors">{{ errors[0] }}</span> </div> @@ -212,8 +224,7 @@ <div style="margin: 0 0.5em 0.5em 0.5em; font-size: 0.8em; font-style: italic;" > - Vous pouvez demander le changement de votre adresse mail en renseignant une nouvelle adresse. - Un lien de confirmation vous sera envoyé sur la nouvelle adresse. + {{ $t('profile.form.emailChange.help') }} </div> </div> </div> @@ -225,7 +236,7 @@ @click.prevent="handleSubmit(submitNewEmail)" variant="primary" > - Valider + {{ $t('words.validate') }} </b-button> </div> </b-overlay> @@ -239,11 +250,13 @@ :style="'padding: 5px;'" > <form> - <h5>Changer votre mot de passe</h5> + <h5>{{ $t('profile.form.passwordChange.title') }}</h5> <hr class="divider"> <div class="row g-2 align-items-center"> <div class="col-3" style="padding-right: 0;"> - <label class="col-form-label required" style="font-size: 0.9em;">Ancien mot de passe</label> + <label class="col-form-label required" style="font-size: 0.9em;"> + {{ $t('profile.form.passwordChange.oldLabel') }} + </label> </div> <div class="col"> <ValidationProvider ref="password" rules="required" v-slot="{ classes, errors }"> @@ -253,7 +266,7 @@ v-model="formPassword.password" class="form-control" :type="showPassword ? 'text' : 'password'" - placeholder="Ancien mot de passe" + :placeholder="$t('profile.form.passwordChange.oldLabel')" > <span v-if="!showPassword" @@ -283,7 +296,7 @@ <div class="col-3" style="padding-right: 0;"> <label class="col-form-label required" - style="font-size: 0.9em;">Nouveau mot de passe</label> + style="font-size: 0.9em;">{{ $t('profile.form.passwordChange.newLabel') }}</label> </div> <div class="col"> <ValidationProvider ref="newPassword1" v-slot="{ classes, errors }" vid="confirmation"> @@ -293,7 +306,7 @@ v-model="formPassword.newPassword1" :type="showNewPassword2 ? 'text' : 'password'" class="form-control" - placeholder="Nouveau mot de passe" + :placeholder="$t('profile.form.passwordChange.newPlaceholder')" > <span v-if="!showNewPassword2" @@ -333,7 +346,7 @@ v-model="formPassword.newPassword2" class="form-control" :type="showNewPassword2 ? 'text' : 'password'" - placeholder="Confirmez le mot de passe" + :placeholder="$t('profile.form.passwordChange.confirmPlaceholder')" > <span v-if="!showNewPassword2" @@ -365,10 +378,10 @@ <div class="col"> <div class="infos"> <ul> - <li>Votre mot de passe ne peut pas trop ressembler à vos autres informations personnelles.</li> - <li>Votre mot de passe doit contenir au minimum 8 caractères.</li> - <li>Votre mot de passe ne peut pas être un mot de passe couramment utilisé.</li> - <li>Votre mot de passe ne peut pas être entièrement numérique.</li> + <li>{{ $t('profile.form.passwordChange.passwordHelp')[0] }}</li> + <li>{{ $t('profile.form.passwordChange.passwordHelp')[1] }}</li> + <li>{{ $t('profile.form.passwordChange.passwordHelp')[2] }}</li> + <li>{{ $t('profile.form.passwordChange.passwordHelp')[3] }}</li> </ul> </div> </div> @@ -381,7 +394,7 @@ @click.prevent="handleSubmit(submitNewPassword)" variant="primary" > - Valider + {{ $t('words.validate') }} </b-button> </div> </b-overlay> @@ -390,7 +403,7 @@ </div> <small class="footer"> <p> - Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a> + {{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a> </p> </small> </div> @@ -399,6 +412,8 @@ <script> import { mapState, mapMutations, mapActions } from 'vuex'; +import i18n from '@/i18n'; + import Swal from "sweetalert2"; import "sweetalert2/dist/sweetalert2.min.css"; @@ -413,17 +428,17 @@ import { required, email, confirmed } from 'vee-validate/dist/rules'; extend('required', { ...required, - message: 'Ce champ est requis', + message: () => i18n.t('errors.required') }); extend('email', { ...email, - message: 'Veuillez entrer une adresse e-mail valide', + message: () => i18n.t('errors.email'), }); extend('confirmed', { ...confirmed, - message: 'Les mots de passe doivent être identiques', + message: () => i18n.t('errors.confirmPassword'), }); configure({ @@ -485,6 +500,14 @@ export default { } }, + watch: { + '$i18n.locale': function(newValue, oldValue) { + if (newValue !== oldValue) { + this.$refs.form.validate(); + } + }, + }, + created() { this.SET_NEXT(this.$route.query.next || process.env.VUE_APP_NEXT_DEFAULT); if (!this.userData) { diff --git a/src/views/ValidationEmail.vue b/src/views/ValidationEmail.vue index bfc8f6482a8201c8aa5f1b8da0033f17710a1aea..df8eba4afe086cedfbe92a22381d5b24dd690952 100644 --- a/src/views/ValidationEmail.vue +++ b/src/views/ValidationEmail.vue @@ -6,24 +6,24 @@ <div class="center"> <b-container class="msg" v-if="status"> <p> - Votre nouvelle adresse e-mail est validée. + {{ $t('validationEmail.message') }} </p> <b-button type="button" variant="outline-primary" @click.prevent="$router.push({ name: 'SignIn' })" > - Ouvrir la page de connexion + {{ $t('validationEmail.goToLogin') }} </b-button> </b-container> <b-container class="msg" v-else> <p> - Une erreur est survenue. Veuillez contacter l'administrateur du site. + {{ $t('validationEmail.error') }} </p> </b-container> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template> diff --git a/src/views/ValidationRegistration.vue b/src/views/ValidationRegistration.vue index cfda015bbf795b2d3fbe121f12f020099a5c5778..3cbd4523f3961c457b427bb5c156dcd8a3eaac13 100644 --- a/src/views/ValidationRegistration.vue +++ b/src/views/ValidationRegistration.vue @@ -6,10 +6,10 @@ <div class="center"> <b-container class="msg" v-if="btnVariant=='success'"> <p> - Merci pour votre inscription. + {{ $t('validationRegistration.messages.success')[0] }} </p> <p> - Vous recevrez une notification lors de l'activation de votre compte par l'administrateur de la plateforme. + {{ $t('validationRegistration.messages.success')[1] }} </p> <!-- <b-button type="button" variant="outline-primary" @click.prevent="$router.push({ name: 'SignIn' })"> Ouvrir la page de connexion @@ -17,17 +17,17 @@ </b-container> <b-container class="msg" v-else-if="btnVariant=='danger'"> <p> - Une erreur est survenue. Veuillez contacter l'administrateur du site. + {{ $t('validationRegistration.messages.error') }} </p> </b-container> <b-container class="msg" v-else> <p> - Pour confirmer votre inscription <b-button :pressed="btnPressed" :variant="btnVariant" @click.prevent="submit">Cliquez ici</b-button> + {{ $t('validationRegistration.confirmation') }} <b-button :pressed="btnPressed" :variant="btnVariant" @click.prevent="submit">{{ $t('validationRegistration.clickHere') }}</b-button> </p> </b-container> </div> <small class="footer"> - <p>Propulsé par <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> + <p>{{ $t('footer') }} <a href="https://www.neogeo.fr/" target="_blank" rel="noopener">Neogeo-Technologies</a></p> </small> </div> </template>