Comment internationaliser notre application

Prérequis

Afin de pouvoir suivre ce guide, il faut préalablement avoir intégré un système d’onglets.

Comment configurer @nuxtjs/i18n

Recommandation

Si vous n’êtes pas familier avec la configuration et l’utilisation de @nuxtjs/i18n, il est fortement conseillé de consulter la documentation associée disponible ici.

Modifier le fichier de configuration nuxt.config.js pour ajouter la configuration du plugin @nuxtjs/i18n :




















 
 
 
 
 
 
 
 
 


export default {
  // Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
  ssr: false,

  // Target: https://go.nuxtjs.dev/config-target
  target: "static",
  components: true,
  css: ["@/assets/css/bootstrap.scss", "@/assets/css/kendo.scss", "@/assets/css/main.scss"],
  modules: ["@nuxtjs/proxy", "@nuxtjs/i18n"],

  proxy: {
    "/locale/catalog.json": {
      target: "http://localhost:8080",
    },
    "/api/v2/**": {
      target: "http://localhost:8080",
    },
  },

  i18n: {
    locales: [
      { code: "en", file: "en.js" },
      { code: "fr", file: "fr.js" },
    ],
    strategy: "no_prefix",
    defaultLocale: "fr",
    langDir: "~/locales/",
  },
};

Créer le répertoire locales à la racine du projet et créer les fichiers locales/fr.js et locales/en.js. Ces fichiers contiendront les traductions pour chaque langue de l’application.

mkdir locales && touch locales/fr.js locales/en.js

Préparer le store pour l’internationalisation

Afin de garder une valeur persistante, la langue de l’application sera stockée dans le store Vue inclus avec Nuxt.js

Recommandation

Si vous n’êtes pas familier avec le concept de Vuex et de store, il est fortement recommandé de lire la documentation associée disponible ici.

Il est également recommandé de consulter la documentation d’utilisation du store avec Nuxt.js disponible ici.

Créer le fichier store/app.js :

touch store/app.js
import axios from "axios";
import Vue from "vue";

export const state = () => ({
  locale: "fr",
});

export const mutations = {
  setLocale(state, locale) {
    if (["fr", "en"].includes(locale)) {
      state.locale = locale;
    }
  },
};

export const getters = {
  getLocale: (state) => state.locale,
};

export const actions = {
  changeLocale({ commit }, locale) {
    // On change d’abord la langue d’Anakeen Platform 4
    return axios
      .post("/api/v2/restauratec/changeLocale", { locale })
      .then((res) => {
        // En cas de succès
        const locale = res.data.data.locale;
        this.$i18n.setLocale(locale); // La langue est modifiée côté client
        Vue.$_globalI18n.setLocale(locale); // La langue est modifiée côté client
        commit("setLocale", locale); // La langue est stockée dans le store
      })
      .catch((err) => {
        // En cas d’échec
        const locale = "fr"; // La langue par défaut est le français
        Vue.$_globalI18n.setLocale(locale); // La langue est modifiée côté client
        commit("setLocale", locale); // La langue est stockée dans le store
      });
  },
};

Utiliser l’internationalisation dans l’application

Nous allons utiliser l’internationalisation pour traduire les intitulés des sections. Nous allons également ajouter un bouton dans la barre de navigation pour basculer la langue de français à anglais.

Modifier le fichier layouts/default.vue :

<template>
  <div class="restauratec-main">
    <div class="restauratec-header">
      <div class="restauratec-header-nav-button restauratec-header-plat btn btn-outline-primary">
        <NuxtLink :to="localePath('/plat')">{{ $tc("plat", 2) }}</NuxtLink>
      </div>
      <div class="restauratec-header-nav-button restauratec-header-boisson btn btn-outline-primary">
        <NuxtLink :to="localePath('/boisson')">{{ $tc("boisson", 2) }}</NuxtLink>
      </div>
      <div class="restauratec-header-nav-button restauratec-header-menu btn btn-outline-primary">
        <NuxtLink :to="localePath('/menu')">{{ $tc("menu", 2) }}</NuxtLink>
      </div>
      <div class="restauratec-header-switch custom-control custom-switch">
        <input type="checkbox" class="custom-control-input" id="langSwitch" v-model="checked" />
        <label class="custom-control-label" for="langSwitch">{{ $t("lang") }}</label>
      </div>
    </div>
    <div class="restauratec-body">
      <Nuxt />
    </div>
  </div>
</template>
<script>
export default {
  computed: {
    checked: {
      get: function () {
        return this.$store.getters["app/getLocale"] === "fr";
      },
      set(checked) {
        let locale = "en";
        if (checked) {
          locale = "fr";
        }
        this.setLocale(locale);
      },
    },
  },
  methods: {
    setLocale(locale) {
      this.$store.dispatch("app/changeLocale", locale);
    },
  },
};
</script>

Modifier également le fichier de style assets/css/main.scss :


















































 
 
 
 
 
 
 
 
 
 
 










$primary: #8ae234;
@import "~@anakeen/user-interfaces/components/scss/AnkSmartElement.scss";
@import "~@anakeen/user-interfaces/components/scss/SmartElementGrid.scss";

body {
  height: 100vh;

  #__nuxt {
    height: 100%;

    #__layout {
      height: 100%;

      .restauratec-main {
        height: 100%;

        .restauratec-header {
          width: 100%;
          display: flex;
          justify-content: space-between;
          align-items: center;
          height: 3.5rem;

          .restauratec-header-nav-button {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;

            .nuxt-link-active {
              color: $primary;

              &:hover {
                color: black;
              }
            }

            a {
              width: 100%;
              height: 100%;
              text-decoration: auto;
              text-align: center;
              color: black;
              font-weight: bold;
              font-size: 1.5rem;
            }
          }

          .restauratec-header-switch {
            width: 8rem;
            height: 100%;
            flex: 1;
            display: flex;
            align-items: center;
            padding: 0 3.5rem;
            border-bottom: 1px solid;
            border-bottom-color: $primary;
            font-weight: bold;
          }
        }
        .restauratec-body {
          width: 100%;
          height: 100%;
        }
      }
    }
  }
}

Modifier la langue d’Anakeen Platform 4

Au changement de langue sur notre application Nuxt.js on souhaite modifier également la langue d’Anakeen Platform 4. Pour cela, nous allons créer une route.

Recommandation

Si vous n’êtes pas familier avec l’utilisation des routes Anakeen Platform 4, vous pouvez consulter la documentation associée disponible ici.

Modifier le fichier src/vendor/Cogip/Restauratec/Config/110-RestauratecRoutes.xml :










 
 
 
 
 
 




<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sde:config xmlns:sde="https://platform.anakeen.com/4/schemas/sde/1.0">
  <sde:routes namespace="Cogip">
    <sde:route name="RestauratecMain">
      <sde:callable>Cogip\Restauratec\Routes\Main</sde:callable>
      <sde:method>GET</sde:method>
      <sde:pattern>/Restauratec/main</sde:pattern>
      <sde:description>A route example for Restauratec</sde:description>
    </sde:route>
    <sde:route name="changeLocale">
      <sde:callable>Cogip\Restauratec\Routes\ChangeLocale</sde:callable>
      <sde:method>POST</sde:method>
      <sde:pattern>/api/v2/restauratec/changeLocale</sde:pattern>
      <sde:description>Change the locale of the application</sde:description>
    </sde:route>
  </sde:routes>
  <sde:accesses namespace="Cogip"/>
</sde:config>

Créer la classe PHP appelée par la route src/vendor/Cogip/Restauratec/Routes/ChangeLocale.php :

touch src/vendor/Cogip/Restauratec/Routes/ChangeLocale.php
<?php

namespace Cogip\Restauratec\Routes;



use Anakeen\Core\ContextManager;
use Anakeen\Exception;
use Anakeen\Router\ApiV2Response;

/**
 * Example route
 *
 * @note Used by route : POST /api/v2/restauratec/changeLocale
 */
class ChangeLocale
{
    /**
     * @param \Slim\Http\Request $request
     * @param \Slim\Http\Response $response
     * @param $args
     * @return mixed
     */
    public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, $args)
    {
        $localeParam = $request->getParam("locale");
        $locale = $this->completeLanguage($localeParam);
        if (empty($locale)) {
            throw new Exception(sprintf("Locale %s is not defined. Must be 'en' or 'fr'", $localeParam));
        }
        ContextParameterManager::setValue(\Anakeen\Core\Settings::NsSde, "CORE_LANG", $locale);
        return ApiV2Response::withData($response, [ "locale" => $localeParam]);
    }

    protected function completeLanguage($lang)
    {
        $locale = '';
        switch ($lang) {
            case "fr":
                $locale = "fr_FR";
                break;
            case "en":
                $locale = "en_US";
                break;
            default:
                break;
        }
        return $locale;
    }
}

Traduire les actions de la grille

Modifier le fichier components/RestauratecSection.vue :

<template>
  <div class="restauratec-section">
    <ank-tabs
      v-model="selectedTab"
      ref="ankTabs"
      @tabRemove="onTabRemove"
      @tabClick="onTabClick"
      @tabAdd="onTabAdd"
      :addable="!isCreationTabOpened"
    >
      <ank-tab tabId="grid" label="Smart Elements">
        <div class="restauratec-grid">
          <ank-se-grid
            :collection="gridConfig.collection"
            :columns="gridConfig.columns"
            :actions="gridActions"
            @rowActionClick="onRowActionClick"
            :key="`grid_${delayedLocale}`"
          />
        </div>
      </ank-tab>
      <ank-smart-tab
        v-for="(tab, index) in tabs"
        :identifier="tab.initid"
        :tabId="tab.tabId"
        :key="tab.tabId"
        :viewId="tab.viewId"
        closable
      />
    </ank-tabs>
  </div>
</template>
<script>
import Vue from "vue";
import AnkTabs from "@anakeen/user-interfaces/components/lib/AnkTabs.esm";
import AnkSmartTab from "@anakeen/user-interfaces/components/lib/AnkSmartElementTab.esm";
import AnkTab from "@anakeen/user-interfaces/components/lib/AnkTab.esm";
import AnkSmartElementGrid from "@anakeen/user-interfaces/components/lib/AnkSmartElementGrid.esm";
import setup from "@anakeen/user-interfaces/components/lib/setup.esm";
Vue.use(setup);

export default {
  components: {
    "ank-se-grid": () =>
      new Promise((resolve) => {
        Vue.$_globalI18n.recordCatalog().then(() => {
          resolve(AnkSmartElementGrid);
        });
      }),
    AnkTabs,
    AnkSmartTab: () => AnkSmartTab,
    AnkTab,
  },
  props: ["section", "gridConfig"],
  computed: {
    selectedTab: {
      get: function () {
        return this.$route.params.tab ? this.$route.params.tab : "grid";
      },
      set(tab) {
        this.$router.push(`/${this.section}/${tab}`);
      },
    },
    tabs() {
      return this.$store.getters[`${this.section}/getTabs`];
    },
    isCreationTabOpened() {
      return this.tabs.filter((tab) => tab.tabId === "creation").length > 0;
    },
    locale() {
      return this.$store.getters["app/getLocale"];
    },
    gridActions() {
      return [
        {
          action: "openSETab",
          title: this.$t("consult"),
          iconClass: "fa fa-eye",
        },
      ];
    },
  },
  data() {
    return {
      delayedLocale: this.locale,
    };
  },
  methods: {
    onRowActionClick(e) {
      if (e.data.type === "openSETab") {
        e.preventDefault();
        const initid = e.data.row.properties.initid.toString();
        const tab = {
          initid,
          tabId: initid,
          viewId: "!defaultConsultation",
        };
        this.addTab(tab);
        this.selectTab(tab.tabId);
      }
    },
    addTab(tab) {
      this.$store.commit(`${this.section}/pushTab`, tab);
    },
    onTabRemove(tabId) {
      this.removeTab(tabId);
      const nextTabId = this.tabs.length ? this.tabs[this.tabs.length - 1].tabId : "grid";
      this.selectTab(nextTabId);
    },
    removeTab(tabId) {
      this.$store.commit(`${this.section}/removeTab`, tabId);
    },
    onTabClick(e) {
      this.selectTab(e.tabId);
    },
    selectTab(tabId) {
      this.selectedTab = tabId;
    },
    onTabAdd() {
      const tab = {
        initid: this.gridConfig.collection,
        tabId: "creation",
        viewId: "!defaultCreation",
      };
      this.addTab(tab);
      this.selectTab(tab.tabId);
    },
  },
  watch: {
    locale(val) {
      this.$nextTick(() => {
        this.delayedLocale = val;
      });
    },
  },
};
</script>
<style lang="scss">
.restauratec-section {
  display: flex;
  flex-direction: column;
  align-items: center;

  .ank-tabs {
    width: 100%;

    .ank-tab-pane {
      display: flex;
      flex-direction: column;
      align-items: center;

      .restauratec-grid {
        height: 39rem;
        width: 80%;
        margin-top: 5rem;
        border: 1px solid;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
      }
    }
  }
}
</style>

Définir les traductions

Les traductions sont définies dans les fichiers :

  • locales/fr.js :
export default {
  plat: "Plat | Plats",
  boisson: "Boisson | Boissons",
  menu: "Menu | Menus",
  lang: "Français",
  consult: "Consulter",
};
  • locales/en.js :
export default {
  plat: "Dish | Dishes",
  boisson: "Beverage | Beverages",
  menu: "Menu | Menus",
  lang: "English",
  consult: "View",
};

Tester l’internationalisation

Réinstaller l’application pour prendre en compte la nouvelle route :

make reinstall

Lancez le serveur Anakeen Platform 4 :

make env-start

Puis lancez le serveur de développement :

npm run dev

Vous pouvez voir l’internationalisation sous chaque section, par exemple http://localhost:3000/plat :

i18n

Et ensuite ?

Comment intégrer ma PWA