diff --git a/ui2/.env b/ui2/.env deleted file mode 100644 index a20e25f134f48be2a4314477e25635006417b7cf..0000000000000000000000000000000000000000 --- a/ui2/.env +++ /dev/null @@ -1,2 +0,0 @@ -VUE_APP_I18N_LOCALE=fr -VUE_APP_I18N_FALLBACK_LOCALE=en diff --git a/ui2/src/App.vue b/ui2/src/App.vue index 5f1fcd5ac86f8dbca8d0e4a3c32344f70d421e58..4a8686e865e2e83f35e06749a390d8e500c54d89 100644 --- a/ui2/src/App.vue +++ b/ui2/src/App.vue @@ -19,5 +19,6 @@ export default class App extends Vue {} color: $text-default-color; display: flex; flex-direction: column; + height: 100%; } </style> diff --git a/ui2/src/components/login/Signin.vue b/ui2/src/components/login/Signin.vue index 9afed9a4d02df9d41603ae622c5e221103d26198..92da74c8499f132b487e79f365b68f647b068067 100644 --- a/ui2/src/components/login/Signin.vue +++ b/ui2/src/components/login/Signin.vue @@ -95,7 +95,7 @@ export default class SignIn extends Vue { } catch (error) { let message = this.$t("alert.server-error"); if (error.status === HttpStatusCodes.FORBIDDEN) { - message = this.$t("alert.user-uknown"); + message = this.$t("alert.user-unknown"); } this.alertService.toastError(message, error); } diff --git a/ui2/src/i18n.js b/ui2/src/i18n.js deleted file mode 100644 index 7841e446501ae5d51b69f9628b2e45d932436ed0..0000000000000000000000000000000000000000 --- a/ui2/src/i18n.js +++ /dev/null @@ -1,27 +0,0 @@ -import Vue from "vue"; -import VueI18n from "vue-i18n"; - -Vue.use(VueI18n); - -function loadLocaleMessages() { - const locales = require.context( - "./locales", - true, - /[A-Za-z0-9-_,\s]+\.json$/i - ); - const messages = {}; - locales.keys().forEach((key) => { - const matched = key.match(/([A-Za-z0-9-_]+)\./i); - if (matched && matched.length > 1) { - const locale = matched[1]; - messages[locale] = locales(key); - } - }); - return messages; -} - -export default new VueI18n({ - locale: process.env.VUE_APP_I18N_LOCALE || "en", - fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || "en", - messages: loadLocaleMessages(), -}); diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json new file mode 100644 index 0000000000000000000000000000000000000000..7ee339960ceb596a399a80a6209ea7e5764d0df1 --- /dev/null +++ b/ui2/src/locales/en.json @@ -0,0 +1,34 @@ +{ + "titles": { + "login-page": "Welcome to SI-ORE", + "applications-page": "My applications", + "references-page": "My references" + }, + "login": { + "signin": "Sign in", + "signup": "Sign up", + "login": "Login", + "login-placeholder": "Write down the login", + "pwd": "Password", + "pwd-placeholder": "Write down the password", + "pwd-forgotten": "Forgotten password ? " + }, + "validation": { + "obligatoire": "Mandatory", + "facultatif": "Optional", + "invalid-required": "Please fill the field" + }, + "alert": { + "cancel": "Cancel", + "server-error": "A server error occured", + "user-unknown": "Unknown user" + }, + "menu": { + "logout": "Log out", + "applications": "Applications", + "references": "References", + "french": "Français", + "english": "English", + "language": "Language" + } +} diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index f0f999445a0f227efe0cfdb4d2db34cfb61d8cb0..65bb79fea01ed2eddecae5fa06532b6d61895c1c 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -21,11 +21,14 @@ "alert": { "cancel": "Annuler", "server-error": "Une erreur serveur est survenue", - "user-uknown": "Identifiants inconnus" + "user-unknown": "Identifiants inconnus" }, "menu": { "logout": "Se déconnecter", "applications": "Applications", - "references": "Référentiels" + "references": "Référentiels", + "french": "Français", + "english": "English", + "language": "Langue" } } diff --git a/ui2/src/main.js b/ui2/src/main.js index a22b78599bce612d461b9af3de9a4af7b109f7e2..45f5d5e833294d9eb541717d3cc425e231946996 100644 --- a/ui2/src/main.js +++ b/ui2/src/main.js @@ -9,6 +9,7 @@ import { faExclamationCircle, faEye, faEyeSlash, + faGlobe, faPlus, faSignOutAlt, } from "@fortawesome/free-solid-svg-icons"; @@ -19,19 +20,42 @@ library.add( faPlus, faExclamationCircle, faCheck, - faSignOutAlt + faSignOutAlt, + faGlobe ); Vue.component("vue-fontawesome", FontAwesomeIcon); import "@/style/global.scss"; +// Translation +import { UserPreferencesService } from "./services/UserPreferencesService"; +import VueI18n from "vue-i18n"; +import i18n_en from "@/locales/en.json"; +import i18n_fr from "@/locales/fr.json"; + +Vue.use(VueI18n); +const userPreferencesService = UserPreferencesService.INSTANCE; +export const i18n = new VueI18n({ + locale: userPreferencesService.getUserPrefLocale(), + messages: { + en: i18n_en, + fr: i18n_fr, + }, +}); + // Validation import "vee-validate"; -import "@/services/validation/vee-validation-rules"; +import { required } from "vee-validate/dist/rules"; +import { extend } from "vee-validate"; +// Ici on surcharge les messages d'erreur de vee-validate. +// Pour plus de règles : https://logaretm.github.io/vee-validate/guide/rules.html -// Translation -import i18n from "@/i18n"; +extend("required", { + ...required, + message: i18n.t("validation.invalid-required"), +}); +// Buefy Vue.use(Buefy, { defaultIconComponent: "vue-fontawesome", defaultIconPack: "fas", diff --git a/ui2/src/services/Fetcher.js b/ui2/src/services/Fetcher.js index 14117b5bd1ec22ba6868ecc3c3e1b24d0ecf6e6c..36c308f597f15af54a8ce8226f30a576cec7bfd4 100644 --- a/ui2/src/services/Fetcher.js +++ b/ui2/src/services/Fetcher.js @@ -1,5 +1,8 @@ import config from "@/config"; import { HttpStatusCodes } from "@/utils/HttpUtils"; +import { Locales } from "@/utils/LocaleUtils"; + +export const LOCAL_STORAGE_LANG = "lang"; export class Fetcher { async post(url, data) { @@ -10,6 +13,9 @@ export class Fetcher { mode: "cors", credentials: "include", body: formData, + headers: { + "Accept-Language": this.getUserPrefLocale(), + }, }); return this._handleResponse(response); @@ -22,6 +28,9 @@ export class Fetcher { mode: "cors", credentials: "include", body: formData, + headers: { + "Accept-Language": this.getUserPrefLocale(), + }, }); return this._handleResponse(response); @@ -44,6 +53,9 @@ export class Fetcher { method: "GET", mode: "cors", credentials: "include", + headers: { + "Accept-Language": this.getUserPrefLocale(), + }, }); return this._handleResponse(response); @@ -56,6 +68,9 @@ export class Fetcher { mode: "cors", credentials: "include", body: formData, + headers: { + "Accept-Language": this.getUserPrefLocale(), + }, }); if (response.ok) { @@ -92,4 +107,14 @@ export class Fetcher { } return formData; } + + getUserPrefLocale() { + const browserLocale = window.navigator.language.substring(0, 2); + + return ( + localStorage.getItem(LOCAL_STORAGE_LANG) || + (Object.values(Locales).includes(browserLocale) && browserLocale) || + Locales.FRENCH + ); + } } diff --git a/ui2/src/services/UserPreferencesService.js b/ui2/src/services/UserPreferencesService.js new file mode 100644 index 0000000000000000000000000000000000000000..ffbe8deca0126f3641d3aeba7037766e9cc0820f --- /dev/null +++ b/ui2/src/services/UserPreferencesService.js @@ -0,0 +1,15 @@ +import app from "@/main"; +import { Fetcher, LOCAL_STORAGE_LANG } from "./Fetcher"; + +export class UserPreferencesService extends Fetcher { + static INSTANCE = new UserPreferencesService(); + + constructor() { + super(); + } + + setUserPrefLocale(locale) { + localStorage.setItem(LOCAL_STORAGE_LANG, locale); + app.$i18n.locale = locale; + } +} diff --git a/ui2/src/services/validation/vee-validation-rules.js b/ui2/src/services/validation/vee-validation-rules.js deleted file mode 100644 index 5233349ffd9417038159714e41d71f542dfd0530..0000000000000000000000000000000000000000 --- a/ui2/src/services/validation/vee-validation-rules.js +++ /dev/null @@ -1,10 +0,0 @@ -import i18n from "@/i18n"; -import { required } from "vee-validate/dist/rules"; -import { extend } from "vee-validate"; -// See https://logaretm.github.io/vee-validate/guide/rules.html -// For list of all availables rules - -extend("required", { - ...required, - message: i18n.t("validation.invalid-required"), -}); diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index f77569bf7d433fdd116661c3895093944a03e251..39ad81f12b509824b271993ad48ff2e54ed5dfcf 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -1,6 +1,13 @@ // A file for common css across the application +html, +body { + height: 100%; +} + .title { color: $primary; + margin-top: 1.5rem; + &.main-title { display: flex; align-items: center; diff --git a/ui2/src/utils/LocaleUtils.js b/ui2/src/utils/LocaleUtils.js new file mode 100644 index 0000000000000000000000000000000000000000..6ae7fa9b6692bc1738c2fa37687926a8e6e54c14 --- /dev/null +++ b/ui2/src/utils/LocaleUtils.js @@ -0,0 +1,4 @@ +export const Locales = { + FRENCH: "fr", + ENGLISH: "en", +}; diff --git a/ui2/src/views/common/MenuView.vue b/ui2/src/views/common/MenuView.vue index a6c9516f43ede007ae285300a69513a5f1be3f36..fb37e23ea6899d9ea4db20ef13e1f4d2032f67d2 100644 --- a/ui2/src/views/common/MenuView.vue +++ b/ui2/src/views/common/MenuView.vue @@ -17,34 +17,52 @@ }}</b-button> </div> </b-navbar-item> + <b-navbar-item tag="div"> + <b-field> + <b-select + v-model="chosenLocale" + :placeholder="$t('menu.language')" + icon="globe" + @input="setUserPrefLocale" + > + <option :value="locales.FRENCH">{{ $t("menu.french") }}</option> + <option :value="locales.ENGLISH">{{ $t("menu.english") }}</option> + </b-select> + </b-field> + </b-navbar-item> </template> </b-navbar> </template> <script> -import { User } from "@/model/User"; -import { LoginService } from "@/services/LoginService"; import { Component, Vue } from "vue-property-decorator"; +import { LoginService } from "@/services/LoginService"; +import { UserPreferencesService } from "@/services/UserPreferencesService"; + +import { Locales } from "@/utils/LocaleUtils.js"; + @Component({ components: {}, }) export default class MenuView extends Vue { loginService = LoginService.INSTANCE; + userPreferencesService = UserPreferencesService.INSTANCE; - loggedUser = new User(); + locales = Locales; + chosenLocale = ""; created() { - this.init(); - } - - async init() { - this.loggedUser = await this.loginService.getLoggedUser(); + this.chosenLocale = this.userPreferencesService.getUserPrefLocale(); } logout() { this.loginService.logout(); } + + setUserPrefLocale() { + this.userPreferencesService.setUserPrefLocale(this.chosenLocale); + } } </script> diff --git a/ui2/src/views/common/PageView.vue b/ui2/src/views/common/PageView.vue index d5c5ff80f638cdfc5b3c6b5c10dfa4957f1f6aa2..093f3246daabab85e00925ab45c56c5381c46545 100644 --- a/ui2/src/views/common/PageView.vue +++ b/ui2/src/views/common/PageView.vue @@ -1,5 +1,5 @@ <template> - <div> + <div class="PageView"> <MenuView v-if="hasMenu" /> <div class="container PageView-container"> <slot></slot> @@ -30,6 +30,10 @@ export default class PageView extends Vue { </script> <style lang="scss" scoped> +.PageView { + height: 100%; +} + .PageView-container { width: 100%; }