Bladeren bron

Translated docs in French 🇫🇷 (#391)

* Starting french translation.

* Translated more pages and corrected grammar/typos.

* Translated actions page.

* Fixed typo.

* Translated more pages and fixed typos/mistakes.

* Fixing more mistakes and sentences

* More mistakes

* Some more mistakes. Should be complete
Julien Fradin 8 jaren geleden
bovenliggende
commit
f474251a16

+ 1 - 0
docs/LANGS.md

@@ -1,2 +1,3 @@
 * [2.0 - English](en/)
+* [2.0 - Français](fr/)
 * [1.0 Docs](old/)

+ 1 - 0
docs/fr/README.md

@@ -0,0 +1 @@
+SUMMARY.md

+ 22 - 0
docs/fr/SUMMARY.md

@@ -0,0 +1,22 @@
+# Vuex
+
+> Note : Ceci est la documentation pour vuex@2.x.
+
+- [Vous cherchez la documentation de la v1.0 ?](https://github.com/vuejs/vuex/tree/1.0/docs)
+- [Release Notes](https://github.com/vuejs/vuex/releases)
+- [Installation](installation.md)
+- [Qu'est-ce que Vuex ?](intro.md)
+- [Débuter](getting-started.md)
+- Concepts de base
+  - [State](state.md)
+  - [Getters](getters.md)
+  - [Mutations](mutations.md)
+  - [Actions](actions.md)
+  - [Modules](modules.md)
+- [Structure d'une application](structure.md)
+- [Plugins](plugins.md)
+- [Strict Mode](strict.md)
+- [Formulaires](forms.md)
+- [Tests](testing.md)
+- [Hot Reloading](hot-reload.md)
+- [Documentation API](api.md)

+ 174 - 0
docs/fr/actions.md

@@ -0,0 +1,174 @@
+# Actions
+
+Les actions sont similaires aux mutations, à la différence que :
+
+- Au lieu de modifier le state, les actions committent des mutations.
+- Les actions peuvent contenir des opérations asynchrones.
+
+Enregistrons une simple action :
+
+``` js
+const store = new Vuex.Store({
+  state: {
+    count: 0
+  },
+  mutations: {
+    increment (state) {
+      state.count++
+    }
+  },
+  actions: {
+    increment (context) {
+      context.commit('increment')
+    }
+  }
+})
+```
+
+Les handlers d'action reçoivent un objet contexte qui expose le même set de méthodes/propriétés que l'instance du store, donc vous pouvez appeler `context.commit` pour commiter une mutation, ou accéder au state et aux getters via `context.state` et `context.getters`. Nous verrons pourquoi cet objet contexte n'est pas l'instance du store elle-même lorsque nous présenterons les [Modules](moduels.md) plus tard.
+
+En pratique, nous utilisons souvent la [destructuration d'argument](https://github.com/lukehoban/es6features#destructuring) (*argument destructuring*) pour simplifier quelque peu le code (particulièrement si nous avons besoin d'appeler `commit` plusieurs fois) :
+
+``` js
+actions: {
+  increment ({ commit }) {
+    commit('increment')
+  }
+}
+```
+
+### Dispatcher des actions dans les composants
+
+Les actions sont déclenchées par la méthode `store.dispatch` :
+
+``` js
+store.dispatch('increment')
+```
+
+Cela peut sembler idiot au premier abord : si nous avons besoin d'incrémenter le compteur, pourquoi ne pas simplement appeler `store.commit('increment')` directement ? Et bien, vous rappelez-vous que **les mutations doivent être synchrones** ? Les actions ne suivent pas cette règle. Il est possible de procéder à des opérations **asynchrones** dans une action :
+
+``` js
+actions: {
+  incrementAsync ({ commit }) {
+    setTimeout(() => {
+      commit('increment')
+    }, 1000)
+  }
+}
+```
+
+Les actions prennent en charge le même format de payload et *object-style dispatch* :
+
+``` js
+// dispatcher avec un payload
+store.dispatch('incrementAsync', {
+  amount: 10
+})
+
+// dispatcher avec un object
+store.dispatch({
+  type: 'incrementAsync',
+  amount: 10
+})
+```
+
+Un exemple plus pratique d'une application du monde réel serait une action pour check-out un panier d'achats, ce qui implique **d'appeler une API asynchrone** et de **comitter de multiples mutations** :
+
+``` js
+actions: {
+  checkout ({ commit, state }, payload) {
+    // sauvegarder les articles actuellement dans le panier
+    const savedCartItems = [...state.cart.added]
+    // envoyer la requête de checkout, et vider le panier
+    commit(types.CHECKOUT_REQUEST)
+    // l'API du shop prend un callback success et un callback failure
+    shop.buyProducts(
+      products,
+      // handle success
+      () => commit(types.CHECKOUT_SUCCESS),
+      // handle failure
+      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
+    )
+  }
+}
+```
+
+Notez que nous procédons à un flux d'opérations asynchrones, et enregistrons les effets de bord (mutation du state) de l'action en les committant.
+
+### Dispatcher des actions dans les composants
+
+Vous pouvez dispatcher des actions dans les composants avec `this.$store.dispatch('xxx')`, ou en utilisant le helper `mapActions` qui attache les méthodes du composant aux appels de `store.dispatch` (nécessite l'injection de `store` à la racine) :
+
+``` js
+import { mapActions } from 'vuex'
+
+export default {
+  // ...
+  methods: {
+    ...mapActions([
+      'increment' // attacher this.increment() à this.$store.dispatch('increment')
+    ]),
+    ...mapActions({
+      add: 'increment' // attacher this.add() à this.$store.dispatch('increment')
+    })
+  }
+}
+```
+
+### Composer les actions
+
+Les actions sont souvent asynchrones, donc comment savoir lorsqu'une action est terminée ? Et plus important, comment composer plusieurs actions ensemble pour manipuler des flux asynchrones plus complexes ?
+
+La première chose à savoir est que `store.dispatch` retourne la valeur retournée par le handler de l'action déclenchée, vous pouvez donc retourner une Promise :
+
+``` js
+actions: {
+  actionA ({ commit }) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        commit('someMutation')
+        resolve()
+      }, 1000)
+    })
+  }
+}
+```
+
+Maintenant vous pouvez faire :
+
+``` js
+store.dispatch('actionA').then(() => {
+  // ...
+})
+```
+
+Et également dans une autre action :
+
+``` js
+actions: {
+  // ...
+  actionB ({ dispatch, commit }) {
+    return dispatch('actionA').then(() => {
+      commit('someOtherMutation')
+    })
+  }
+}
+```
+
+Pour finir, nous pouvons utiliser de [async / await](https://tc39.github.io/ecmascript-asyncawait/), une fonctionnalité JavaScript qui sera disponible très bientôt, nous pouvons composer nos actions ainsi :
+
+``` js
+// sachant que getData() et getOtherData() retournent des Promises
+
+actions: {
+  async actionA ({ commit }) {
+    commit('gotData', await getData())
+  },
+  async actionB ({ dispatch, commit }) {
+    await dispatch('actionA') // wait for actionA to finish
+    commit('gotOtherData', await getOtherData())
+  }
+}
+```
+
+> Il est possible pour un `store.dispatch` de déclencher plusieurs handlers d'action dans différents modules. Dans ce genre de cas, la valeur retournée sera une Promise qui se résoud quand tous les handlers déclenchés ont été résolus.

+ 177 - 0
docs/fr/api.md

@@ -0,0 +1,177 @@
+# Documentation API
+
+### Vuex.Store
+
+``` js
+import Vuex from 'vuex'
+
+const store = new Vuex.Store({ ...options })
+```
+
+### Options de constructeur de Vuex.Store
+
+- **state**
+
+  - type: `Object`
+
+    L'objet state racine pour le store Vuex.
+
+    [Détails](state.md)
+
+- **mutations**
+
+  - type: `{ [type: string]: Function }`
+
+    Enregistrer les mutations sur le store. La fonction handler reçoit toujours `state` comme premier argument (sera le state local du module si défini dans un module), et reçoit le `payload` en second argument s'il y en a un.
+
+    [Détails](mutations.md)
+
+- **actions**
+
+  - type: `{ [type: string]: Function }`
+
+    Enregistrer les actions sur le store. La fonction handler reçoit un objet `context` qui expose les propriétés suivantes :
+
+    ``` js
+    {
+      state,     // identique à store.state, ou au state local si dans des modules
+      rootState, // identique à store.state, seulement dans des modules
+      commit,    // identique à store.commit
+      dispatch,  // identique à store.dispatch
+      getters    // identique à store.getters
+    }
+    ```
+
+    [Détails](actions.md)
+
+- **getters**
+
+  - type: `{ [key: string]: Function }`
+
+    Enregistrer les getters sur le store. La fonction getter reçoit les arguments suivants :
+
+    ```
+    state,     // sera le state local du module si défini dans un module.
+    getters,   // indentique à store.getters
+    rootState  // indentique à store.state
+    ```
+    Les getters enregistrés sont exposés sur `store.getters`.
+
+    [Détails](getters.md)
+
+- **modules**
+
+  - type: `Object`
+
+    Un objet contenant des sous-modules qui seront regroupés dans le store, de la forme suivante :
+
+    ``` js
+    {
+      key: {
+        state,
+        mutations,
+        actions?,
+        getters?,
+        modules?
+      },
+      ...
+    }
+    ```
+
+    Chaque module peut contenir `state` et `mutations`, tout comme les options racine. Le state d'un module sera attaché au state racine du store en utilisant la clé du module. Les mutations et getters d'un module recevront seulement le state local du module en premier argument au lieu du state racine, et le `context.state` des actions du module pointeront également vers le state local.
+
+    [Détails](modules.md)
+
+- **plugins**
+
+  - type: `Array<Function>`
+
+    Un tableau de fonctions plugin qui seront appliqués au store. Un plugin reçoit simplement le store comme seul argument et peut soit écouter les mutations (pour la persistence de données, logging ou debugging) ou dispatcher des mutations (pour les données internes, i.e. websockets ou observables).
+
+    [Détails](plugins.md)
+
+- **strict**
+
+  - type: `Boolean`
+  - default: `false`
+
+    Force le store Vuex en mode strict. En mode strict, toute mutation du state en dehors des handlers de mutation lancera une Error.
+
+    [Détails](strict.md)
+
+### Propriétés d'instance de Vuex.Store
+
+- **state**
+
+  - type: `Object`
+
+    Le state racine. Lecture seule.
+
+- **getters**
+
+  - type: `Object`
+
+    Expose les getters enregistrés. Lecture seule.
+
+### Méthodes d'instance de Vuex.Store
+
+- **`commit(type: string, payload?: any) | commit(mutation: Object)`**
+
+  commiter une mutation. [Détails](mutations.md)
+
+- **`dispatch(type: string, payload?: any) | dispatch(action: Object)`**
+
+  Dispatcher une action. Retourne la valeur renvoyée par le handler d'action déclenché, ou une Promise si plusieurs handlers ont été déclenchés. [Détails](actions.md)
+
+- **`replaceState(state: Object)`**
+
+  Remplacer le state racine du store. Utiliser seulement pour hydrater le state ou voir le state dans le temps.
+
+- **`watch(getter: Function, cb: Function, options?: Object)`**
+
+  Observer de façon réactive la valeur de retour d'une fonction getter, et appeler le callback lorsque la valeur change. Le getter reçoit le state du store comme unique argument. Accepte un objet options optionnel qui prend les mêmes options que la méthode `vm.$watch` de Vue.
+
+  Pour arrêter d'observer, appeler la fonction retournée.
+
+- **`subscribe(handler: Function)`**
+
+  S'abonner aux mutations du store. Le `handler` est appelé après chaque mutation et reçoit le descripteur de mutation et le state post-mutation comme arguments :
+
+  ``` js
+  store.subscribe((mutation, state) => {
+    console.log(mutation.type)
+    console.log(mutation.payload)
+  })
+  ```
+
+  Utilisé plus communément dans les plugins. [Détails](plugins.md)
+
+- **`registerModule(path: string | Array<string>, module: Module)`**
+
+  Enregistrer un module dynamique. [Détails](modules.md#enregistrement-dynamique-de-module)
+
+- **`unregisterModule(path: string | Array<string>)`**
+
+  Supprimer un module dynamique. [Détails](modules.md#enregistrement-dynamique-de-module)
+
+- **`hotUpdate(newOptions: Object)`**
+
+  Remplacement à la volée des nouvelles actions et mutations. [Détails](hot-reload.md)
+
+### Helpers d'attachement au composant
+
+- **`mapState(map: Array<string> | Object): Object`**
+
+  Créer des computed properties qui retournent le sub tree du store Vuex au composant. [Détails](state.md#le-helper-mapstate)
+
+- **`mapGetters(map: Array<string> | Object): Object`**
+
+  Créer des computed properties qui retournent la valeur calculée d'un getter. [Détails](getters.md#le-helper-mapgetters)
+
+- **`mapActions(map: Array<string> | Object): Object`**
+
+  Créer des méthodes de composant qui dispatchent une action. [Détails](actions.md#dispatcher-des-actions-dans-les-composants)
+
+- **`mapMutations(map: Array<string> | Object): Object`**
+
+  Créer des méthodes de composant qui committent une mutation. [Détails](mutations.md#commiter-des-mutations-dans-les-composants)

+ 1 - 0
docs/fr/book.json

@@ -0,0 +1 @@
+../book.json

+ 57 - 0
docs/fr/forms.md

@@ -0,0 +1,57 @@
+# Formulaires
+
+Lorsque l'on utilise Vuex en mode strict, il peut être compliqué d'utiliser `v-model` sur une partie du state qui appartient à Vuex :
+
+``` html
+<input v-model="obj.message">
+```
+
+Supposons que `obj` est une computed property qui retourne un Object depuis le store, le `v-model` tentera de muter directement `obj.message` lorsque l'utilisateur saisit du texte dans le champ. En mode strict, cela produira une erreur car la mutation n'est pas effectuée dans un handler de mutation Vuex explicite.
+
+La "façon Vuex" de gérer ça est de binder la valeur de l'`input` est d'appeler une action sur l'event `input` ou `change` :
+
+``` html
+<input :value="message" @input="updateMessage">
+```
+``` js
+// ...
+computed: {
+  ...mapState({
+    message: state => state.obj.message
+  })
+},
+methods: {
+  updateMessage (e) {
+    this.$store.commit('updateMessage', e.target.value)
+  }
+}
+```
+
+Et voici le handler de mutation :
+
+``` js
+// ...
+mutations: {
+  updateMessage (state, message) {
+    state.obj.message = message
+  }
+}
+```
+
+### Computed property bi-directionnelle
+
+Admettons tout de même que l'exemple ci-dessus est plus verbeux que le `v-model` couplé au state local, et on perd quelques fonctionnalités pratiques de `v-model` au passage. Une approche alternative consiste à utiliser une computed property bi-directionnelle avec un setter :
+
+``` js
+// ...
+computed: {
+  message: {
+    get () {
+      return this.$store.state.obj.message
+    },
+    set (value) {
+      this.$store.commit('updateMessage', value)
+    }
+  }
+}
+```

+ 92 - 0
docs/fr/getters.md

@@ -0,0 +1,92 @@
+
+# Getters
+
+Parfois nous avons besoin de calculer des valeurs basées sur le state du store, par exemple pour filtrer une liste d'éléments et les compter :
+
+``` js
+computed: {
+  doneTodosCount () {
+    return this.$store.state.todos.filter(todo => todo.done).length
+  }
+}
+```
+
+Si plus d'un composant a besoin d'utiliser cela, il nous faut ou bien dupliquer cette fonction, ou bien l'extraire dans un helper séparé et l'importer aux endroits nécessaires &mdash; les deux idées sont loin d'être idéales.
+
+Vuex nous permet de définir des "getters" dans le store (voyez-les comme les computed properties des store). Les getters prennent le state en premier argument :
+
+``` js
+const store = new Vuex.Store({
+  state: {
+    todos: [
+      { id: 1, text: '...', done: true },
+      { id: 2, text: '...', done: false }
+    ]
+  },
+  getters: {
+    doneTodos: state => {
+      return state.todos.filter(todo => todo.done)
+    }
+  }
+})
+```
+
+Les getters seront exposé sur l'objet `store.getters` :
+
+``` js
+store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
+```
+
+Les getters recevront également les autres getters en second argument :
+
+``` js
+getters: {
+  // ...
+  doneTodosCount: (state, getters) => {
+    return getters.doneTodos.length
+  }
+}
+```
+
+``` js
+store.getters.doneTodosCount // -> 1
+```
+
+Nous pouvons maintenant facilement les utiliser dans n'importe quel composant :
+
+``` js
+computed: {
+  doneTodosCount () {
+    return this.$store.getters.doneTodosCount
+  }
+}
+```
+
+### Le helper `mapGetters`
+
+Le helper `mapGetters` attache simplement vos getters du store aux computed properties locales :
+
+``` js
+import { mapGetters } from 'vuex'
+
+export default {
+  // ...
+  computed: {
+    // rajouter les getters dans computed avec l'object spread operator
+    ...mapGetters([
+      'doneTodosCount',
+      'anotherGetter',
+      // ...
+    ])
+  }
+}
+```
+
+Si vous voulez attacher un getter avec un nom différent, utilisez un objet :
+
+``` js
+mapGetters({
+  // attacher this.doneCount à store.getters.doneTodosCount
+  doneCount: 'doneTodosCount'
+})
+```

+ 44 - 0
docs/fr/getting-started.md

@@ -0,0 +1,44 @@
+# Débuter
+
+Au cœur de chaque application Vuex, il y a le **store**. Un "store" est tout simplement un conteneur avec le **state** de votre application. Il y a deux choses qui différencient un store Vuex d'un simple objet global :
+
+1. Les stores Vuex sont réactifs. Quand les composants Vue y récupèrent le state, ils modifieront efficacement et de façon réactive si le state du store change.
+
+2. Vous ne pouvez pas muter directement le state du store. La seule façon de modifier le state d'un store est de **commiter** explicitement des **mutations**. Cela assure que chaque état laisse un enregistrement traçable, et permette à des outils de mieux nous aider à comprendre nos applications.
+
+### Le store le plus simple
+
+> **NOTE:** Nous allons utiliser la syntaxe ES2015 dans les exemples de code pour le reste de la documentation. Si vous ne vous êtes pas encore penché dessus, [vous devriez](https://babeljs.io/docs/learn-es2015/) !
+
+Après [avoir installé](installation.md) Vuex, nous allons créer un store. C'est assez simple &mdash; définissez juste un objet state initial et quelques mutations :
+
+``` js
+// Make sure to call Vue.use(Vuex) first if using a module system
+
+const store = new Vuex.Store({
+  state: {
+    count: 0
+  },
+  mutations: {
+    increment (state) {
+      state.count++
+    }
+  }
+})
+```
+
+Maintenant, vous pouvez accéder à l'objet state avec `store.state`, et déclencher un changement de state avec la méthode `store.commit` :
+
+``` js
+store.commit('increment')
+
+console.log(store.state.count) // -> 1
+```
+
+Encore une fois, la raison pour laquelle nous committons une mutation au lieu de modifier `store.state.count` directement, c'est parce que nous voulons le tracer explicitement. Cette simple convention rend votre intention plus explicite, ainsi vous pouvez raisonner plus facilement les changements de state en lisant votre code. De plus, cela nous donne l'opportunité d'implémenter des outils qui peuvent enregistrer chaque mutation, prendre des instantanés du state, ou même procéder à du debugging dans le temps.
+
+Utiliser le state du store dans un composant implique simplement de retourner le state dans une *computed property*, car le state du store est réactif. Déclencher des changements signifie simplement commiter des mutations dans les méthodes du composant.
+
+Voici un exemple de la [plus basique app Vuex de compteur](https://jsfiddle.net/yyx990803/n9jmu5v7/).
+
+Ensuite, nous allons examiner chaque concept de base plus en détails, et commençon avec le [State](state.md).

+ 44 - 0
docs/fr/hot-reload.md

@@ -0,0 +1,44 @@
+# Hot Reloading
+
+Vuex prend en charge le `hot-reloading` des mutations, modules, actions et getters durant le développement, en utilisant l'[API Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement.html) de Webpack. Vous pouvez également utiliser Browserify avec le plugin [browserify-hmr](https://github.com/AgentME/browserify-hmr/).
+
+Pour les mutations et les modules, vous aurez besoin d'utiliser la méthode d'API `store.hotUpdate()` :
+
+``` js
+// store.js
+import Vue from 'vue'
+import Vuex from 'vuex'
+import mutations from './mutations'
+import moduleA from './modules/a'
+
+Vue.use(Vuex)
+
+const state = { ... }
+
+const store = new Vuex.Store({
+  state,
+  mutations,
+  modules: {
+    a: moduleA
+  }
+})
+
+if (module.hot) {
+  // accept actions and mutations as hot modules
+  module.hot.accept(['./mutations', './modules/a'], () => {
+    // require the updated modules
+    // have to add .default here due to babel 6 module output
+    const newMutations = require('./mutations').default
+    const newModuleA = require('./modules/a').default
+    // swap in the new actions and mutations
+    store.hotUpdate({
+      mutations: newMutations,
+      modules: {
+        a: newModuleA
+      }
+    })
+  })
+}
+```
+
+Jetez un œil à [l'exemple counter-hot](https://github.com/vuejs/vuex/tree/dev/examples/counter-hot) pour jouer avec le hot-reload.

BIN
docs/fr/images/flow.png


BIN
docs/fr/images/vuex.png


+ 43 - 0
docs/fr/installation.md

@@ -0,0 +1,43 @@
+# Installation
+
+### Téléchargement direct / CDN
+
+[https://unpkg.com/vuex](https://unpkg.com/vuex)
+
+[Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur NPM. Le lien ci-dessus pointera toujours vers la dernière release sur NPM. Vous pouvez aussi utiliser un tag ou une version spécifique comme `https://unpkg.com/vuex@2.0.0`.
+
+Incluez `vuex` après Vue et l'installation sera automatique :
+
+``` html
+<script src="/path/to/vue.js"></script>
+<script src="/path/to/vuex.js"></script>
+```
+
+### NPM
+
+``` bash
+npm install vuex
+```
+
+Lorsque vous utilisez un système de modules, vous devez explicitement installer le router via `Vue.use()`:
+
+``` js
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+```
+
+Il n'est pas nécessaire de faire ceci lorsque vous utilisez des balises de script globales.
+
+### Environnement de dev
+
+Vous devrez cloner directement depuis GitHub et compiler `vuex` vous-même si
+vous souhaitez utiliser la dernière version de dev.
+
+``` bash
+git clone https://github.com/vuejs/vuex.git node_modules/vuex
+cd node_modules/vuex
+npm install
+npm run build
+```

+ 65 - 0
docs/fr/intro.md

@@ -0,0 +1,65 @@
+# Qu'est-ce que Vuex ?
+
+Vuex est un ***state management pattern* et une bibliothèque** pour des applications Vue.js. Il sert de store centralisé pour tous les composants dans une application, avec des règles pour s'assurer que l'état ne peut subir des mutations que d'une manière prévisible. Il s'intègre également avec [l'extension officielle](https://github.com/vuejs/vue-devtools) de Vue afin de fournir des fonctionnalités avancées comme voir l'état dans le temps et débugger sans configuration, ainsi que de prendre des instantanés, importer et exporter l'état.
+
+### Qu'est-ce qu'un "State Management Pattern"?
+
+Commençons avec une simple application Vue de compteur :
+
+``` js
+new Vue({
+  // state
+  data () {
+    return {
+      count: 0
+    }
+  },
+  // view
+  template: `
+    <div>{{ count }}</div>
+  `,
+  // actions
+  methods: {
+    increment () {
+      this.count++
+    }
+  }
+})
+```
+
+C'est une app auto-contenue avec les parties suivantes :
+
+- L'**État** (_state_), qui est la source de vérité qui dirige notre app;
+- La **Vue** (_view_), qui est juste un mapping déclaratif du **state**;
+- Les **actions**, qui sont les façons possibles pour le state de changer en réaction aux actions de l'utilisateur depuis la **vue**.
+
+Voici une représentation extrèmement simple du concept de "one-way data flow" (_flux de données unidirectionnel_) :
+
+<p style="text-align: center; margin: 2em">
+  <img style="max-width:450px;" src="./images/flow.png">
+</p>
+
+Cependant, la simplicité s'évapore rapidement lorsque nous avons **de multiples composants qui partagent le même state** :
+
+- Plusieurs vues peuvent dépendre de la même partie du state.
+- Des actions dans différentes vues peuvent avoir besoin de muter la même partie du state.
+
+Pour le premier problème, passer des propriétés peut être fastidieux pour les composants profondément imbriqués, et ça ne fonctionne tout simplement pas pour les composants d'un même parent. Pour le deuxième problème, on se retrouve souvent à se rabattre sur des solutions telles qu'accéder aux références d'instance du parent/enfant direct ou essayer de muter et synchroniser de multiples copies du state via des events. Ces deux patterns sont fragiles et posent rapidement des problèmes de maintenabilité du code.
+
+Alors pourquoi ne pas extraire le state partagé des composants, et le gérer dans un singleton global ? De cette manière, notre arbre de composant devient une grosse "view", et n'importe-quel composant peut accéder au state ou déclencher des actions, peu importe où il se trouve dans l'arbre !
+
+De plus, en définissant et en séparant les concepts impliqués dans la gestion d'un state et en appliquant certaines règles, on donne aussi une structure et une maintenabilité à notre code.
+
+Voilà l'idée de base derrière Vuex, inspiré par [Flux](https://facebook.github.io/flux/docs/overview.html), [Redux](http://redux.js.org/) et [l'Architecture Elm](https://guide.elm-lang.org/architecture/). À l'inverse des autres patterns, Vuex est aussi une bibliothèque d'implémentation conçue spécialement pour Vue.js afin de bénéficier de son système de réactivité granulaire pour des modifications efficaces.
+
+![vuex](./images/vuex.png)
+
+### Quand l'utiliser ?
+
+Bien que Vuex nous aide à gérer une state partagé, il apporte aussi le coût de nouveaux concepts et _boilerplate_. C'est un compromis entre la productivité à court terme et à long terme.
+
+Si vous n'avez jamais créé une _Single Page Application_ à grande échelle et que vous sautez directement dans Vuex, cela peut paraître verbeux et intimidant. C'est parfaitement normal &mdash; si votre application est simple, vous vous en sortirez sans doute très bien sans Vuex. Un simple [bus d'event global](http://vuejs.org/guide/components.html#Non-Parent-Child-Communication) pourrait très bien vous suffire. Mais si vous devez créer une SPA à moyenne ou grande échelle, il y a des chances que vous vous trouviez dans des situations qui vous feront penser à une meilleure façon de gérer le state en-dehors de votre composant Vue, et Vuex sera naturellement la prochaine étape pour vous. Voici une bonne citation de Dan Abramov, l'auteur de Redux :
+
+> Flux libraries are like glasses: you’ll know when you need them.
+>
+> _Les librairies Flux, c'est comme les lunettes : vous saurez quand vous en aurez besoin._

+ 138 - 0
docs/fr/modules.md

@@ -0,0 +1,138 @@
+# Modules
+
+Parce qu'il utilise un _single state tree_, tout le state de notre application est contenu dans un seul et même gros objet. Cependant, au fur et à mesure que notre application grandit, le store peut devenir très engorgé.
+
+Pour y remédier, Vuex nous permet de diviser notre store en **modules**. Chaque module peut contenir son propre state, mutations, actions, getters, et même d'autres modules.
+
+``` js
+const moduleA = {
+  state: { ... },
+  mutations: { ... },
+  actions: { ... },
+  getters: { ... }
+}
+
+const moduleB = {
+  state: { ... },
+  mutations: { ... },
+  actions: { ... }
+}
+
+const store = new Vuex.Store({
+  modules: {
+    a: moduleA,
+    b: moduleB
+  }
+})
+
+store.state.a // -> le state du module A
+store.state.b // -> le state du module B
+```
+
+### State local d'un module
+
+Dans les mutations et getters d'un module, le premier argument reçu sera le **state local du module**.
+
+``` js
+const moduleA = {
+  state: { count: 0 },
+  mutations: {
+    increment: (state) {
+      // state est le state du module local
+      state.count++
+    }
+  },
+
+  getters: {
+    doubleCount (state) {
+      return state.count * 2
+    }
+  }
+}
+```
+
+De façon similaire, dans les actions du module, `context.state` exposera le state local, et le state racine sera disponible avec `context.rootState` :
+
+``` js
+const moduleA = {
+  // ...
+  actions: {
+    incrementIfOdd ({ state, commit }) {
+      if (state.count % 2 === 1) {
+        commit('increment')
+      }
+    }
+  }
+}
+```
+
+Également, dans les getters du module, le state racine sera exposé en troisième argument :
+
+``` js
+const moduleA = {
+  // ...
+  getters: {
+    sumWithRootCount (state, getters, rootState) {
+      return state.count + rootState.count
+    }
+  }
+}
+```
+
+### Namespacing
+
+Notez que les actions, mutations et getters dans un module sont toujours enregistrés sous le **namespace global** &mdash; cela permet à plusieurs modules de réagir au même type de mutation/action. Vous pouvez répartir les modules dans des namespaces vous-mêmes afin d'éviter les conflits de nom en préfixant ou suffixant leurs noms. Et vous devriez probablement faire cela si vous utiliser un module Vuex réutilisable qui sera utilisé dans des environnements inconnus. Par exemple, nous voulons créer un module `todos` :
+
+``` js
+// types.js
+
+// on définit les noms des getters, actions et mutations en tant que constantes
+// et on les préfixe du nom du module `todos`
+export const DONE_COUNT = 'todos/DONE_COUNT'
+export const FETCH_ALL = 'todos/FETCH_ALL'
+export const TOGGLE_DONE = 'todos/TOGGLE_DONE'
+```
+
+``` js
+// modules/todos.js
+import * as types from '../types'
+
+// on définit les getters, actions et mutations en utilisant des noms préfixés
+const todosModule = {
+  state: { todos: [] },
+
+  getters: {
+    [types.DONE_COUNT] (state) {
+      // ...
+    }
+  },
+
+  actions: {
+    [types.FETCH_ALL] (context, payload) {
+      // ...
+    }
+  },
+
+  mutations: {
+    [types.TOGGLE_DONE] (state, payload) {
+      // ...
+    }
+  }
+}
+```
+
+### Enregistrement dynamique de module
+
+Vous pouvez enregistrer un module **après** que le store ait été créé avec la méthode `store.registerModule` :
+
+``` js
+store.registerModule('myModule', {
+  // ...
+})
+```
+
+Le state du module sera disponible dans `store.state.myModule`.
+
+L'enregistrement dynamique de module permet aux autres plugins Vue de bénéficier de la gestion de state de Vuex en attachant un module au store de l'application. Par exemple, la bibliothèque [`vuex-router-sync`](https://github.com/vuejs/vuex-router-sync) intègre vue-router avec vuex en gérant le state de la route d'application dans un module enregistré dynamiquement.
+
+Vous pouvez aussi supprimer un module enregistré dynamiquement avec `store.unregisterModule(moduleName)`. Notez que vous ne pouvez pas supprimer des modules statiques (déclarés à la création du store) avec cette méthode.

+ 186 - 0
docs/fr/mutations.md

@@ -0,0 +1,186 @@
+# Mutations
+
+La seule façon de vraiment modifier le state dans un store Vuex est de commiter une mutation. Les mutations Vuex sont très similaires aux events : chaque mutation a un **type** sous forme de chaîne de caractères et un **handler**. La fonction handler est là où nous procédons aux véritables modifications du state, et elle reçoit le state en premier argument :
+
+``` js
+const store = new Vuex.Store({
+  state: {
+    count: 1
+  },
+  mutations: {
+    increment (state) {
+      // mutate state
+      state.count++
+    }
+  }
+})
+```
+
+Vous ne pouvez pas appeler directement un handler de mutation. La façon de faire est plutôt comme un abonnement à un event : "Lorsqu'une mutation du type `increment` est déclenchée, appelle ce handler." Pour invoquer un handler de mutation, il faut appeler **store.commit** avec son type :
+
+``` js
+store.commit('increment')
+```
+
+### commiter avec un Payload
+
+Vous pouvez donner un autre argument à **store.commit** pour la mutation, qui s'appelle **payload** :
+
+``` js
+// ...
+mutations: {
+  increment (state, n) {
+    state.count += n
+  }
+}
+```
+``` js
+store.commit('increment', 10)
+```
+
+Dans la plupart des cas, le payload devrait être un objet, ainsi il peut contenir plusieurs champs, et les mutations enregistrées seront également plus descriptives :
+
+``` js
+// ...
+mutations: {
+  increment (state, payload) {
+    state.count += payload.amount
+  }
+}
+```
+``` js
+store.commit('increment', {
+  amount: 10
+})
+```
+
+### Object-Style Commit
+
+Une méthode alternative pour commiter une mutation est d'utiliser directement un objet qui a une propriété `type` :
+
+``` js
+store.commit({
+  type: 'increment',
+  amount: 10
+})
+```
+
+Lors de l'utlisation de l'object-style commit, l'objet entier sera fournit comme payload aux handlers de mutation, donc le handler reste inchangé :
+
+``` js
+mutations: {
+  increment (state, payload) {
+    state.count += payload.amount
+  }
+}
+```
+
+### Commit silencieux
+
+> Note : Cette fonctionnalité sera probablement dépréciée une fois que nous aurons implémenté le filtrage des mutations dans les devtools.
+
+Par défaut, chaque mutation committée est envoyée aux plugins (i.e. les devtools). Cependant dans certains scénarios vous pourriez ne pas vouloir que les plugins enregistrent chaque changement de state. Plusieurs commits dans le store en un court laps de temps n'ont pas toujours besoin d'être tracés. Dans ce genre de cas, vous pouvez passer un troisième argument à `store.commit` afin de rendre cette mutation silencieuse aux yeux des plugins :
+
+``` js
+store.commit('increment', {
+  amount: 1
+}, { silent: true })
+
+// with object-style dispatch
+store.commit({
+  type: 'increment',
+  amount: 1
+}, { silent: true })
+```
+
+### Les mutations suivent les règles de réactivité de Vue
+
+Puisqu'un state de store de Vuex est rendu réactif par Vue, lorsque nous mutons le state, les composants Vue observant ce state seront automatiquement mis à jour. Cela signifie également que les mutations Vuex sont sujettes aux mêmes inconvénients que lorsqu'on travaille avec Vue :
+
+1. Initialisez de préférences le state initial de votre state avec tous les champs désirés auparavant.
+
+2. Lorsque vous ajoutez de nouvelles propriétés à un Object, vous devriez soit :
+
+  - Utiliser `Vue.set(obj, 'newProp', 123)`, ou -
+
+  - Remplacer cet Object par un nouvel Object. Par exemple, en utilisant [object spread syntax](https://github.com/sebmarkbage/ecmascript-rest-spread) (stage-2), il est possible d'écrire :
+
+    ``` js
+    state.obj = { ...state.obj, newProp: 123 }
+    ```
+
+### Utilisation de constante pour les noms de mutation
+
+C'est une façon de faire régulière que d'utiliser des constantes pour les types de mutations dans diverses implémentations de Flux. Cela permet au code de bénéficier d'outils comme les linters, et écrire toutes ces constantes dans un seul fichier permet à vos collaborateurs d'avoir un aperçu de quelles mutations sont possibles dans toute l'application :
+
+``` js
+// mutation-types.js
+export const SOME_MUTATION = 'SOME_MUTATION'
+```
+
+``` js
+// store.js
+import Vuex from 'vuex'
+import { SOME_MUTATION } from './mutation-types'
+
+const store = new Vuex.Store({
+  state: { ... },
+  mutations: {
+    // we can use the ES2015 computed property name feature
+    // to use a constant as the function name
+    [SOME_MUTATION] (state) {
+      // mutate state
+    }
+  }
+})
+```
+
+Utiliser les constantes ou non relève de la préférence personnelle &mdash; cela peut être bénéfique sur un gros projet avec beaucoup de développeurs, mais c'est totalement optionnel si vous n'aimez pas cette pratique.
+
+### Les mutations doivent être synchrones
+
+Une règle importante à retenir est que **les fonctions handler de mutations doivent être synchrones**. Pourquoi ? Considérons l'exemple suivant :
+
+``` js
+mutations: {
+  someMutation (state) {
+    api.callAsyncMethod(() => {
+      state.count++
+    })
+  }
+}
+```
+
+Maintenant imaginons que nous debuggons l'application et que nous regardons dans les logs de mutation des devtools. Pour chaque mutation enregistrée, le devtool aura besoin de capturer un instantané du state "avant" et un instantané "après". Cependant, le callback asynchrone du l'exemple ci-dessus rend l'opération impossible : le callback n'est pas encore appelé lorsque la mutation est committée, et il n'y a aucun moyen pour le devtool de savoir quand le callback sera véritablement appelé &mdash; toute mutation du state effectuée dans le callack est essentiellement intraçable !
+
+### commiter des mutations dans les composants
+
+Vous pouvez commiter des mutations dans les composants avec `this.$store.commit('xxx')`, ou en utilisant le helper `mapMutations` qui attache les méthodes du composant aux appels de `store.commit` (nécessite l'injection de `store` à la racine) :
+
+``` js
+import { mapMutations } from 'vuex'
+
+export default {
+  // ...
+  methods: {
+    ...mapMutations([
+      'increment' // map this.increment() to this.$store.commit('increment')
+    ]),
+    ...mapMutations({
+      add: 'increment' // map this.add() to this.$store.commit('increment')
+    })
+  }
+}
+```
+
+### En avant vers les actions
+
+L'asynchronisme combiné à la mutation du state peut rendre votre program très difficile à comprendre. Par exemple, lorsque vous appelez deux méthodes avec toutes les deux des callbacks asynchrones qui changent le state, comment savez-vous quand elles sont appelées et quel callback est appelé en premier ? C'est exactement la raison pour laquelle nous voulons séparer les deux concepts. Avec Vuex, **les mutations sont des transactions synchrones** :
+
+``` js
+store.commit('increment')
+// any state change that the "increment" mutation may cause
+// should be done at this moment.
+```
+
+Pour gérer les opérations asynchrones, présentons les [Actions](actions.md).

+ 120 - 0
docs/fr/plugins.md

@@ -0,0 +1,120 @@
+# Plugins
+
+Les stores Vuex prennent une option `plugins` qui expose des hooks pour chaque mutation. Un plugin Vuex est simplement une fonction qui reçoit un store comme unique argument :
+
+``` js
+const myPlugin = store => {
+  // called when the store is initialized
+  store.subscribe((mutation, state) => {
+    // called after every mutation.
+    // The mutation comes in the format of { type, payload }.
+  })
+}
+```
+
+Et peut être utilisé ainsi :
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  plugins: [myPlugin]
+})
+```
+
+### Commiter des mutations dans des plugins
+
+Les plugins ne sont pas autorisés à muter directement le state &mdash; tout comme vos composants, ils peuvent simplement déclencher des changements en committant des mutations.
+
+En commitant des mutations, un plugin peut être utilisé pour synchroniser la source de données avec le store. Par exemple, pour synchroniser la source de données d'un websocket vers le store (c'est juste un exemple artificiel, en réalité la fonction `createPlugin` peut prendre des options additionnelles pour des tâches plus complexes) :
+
+``` js
+export default function createWebSocketPlugin (socket) {
+  return store => {
+    socket.on('data', data => {
+      store.commit('receiveData', data)
+    })
+    store.subscribe(mutation => {
+      if (mutation.type === 'UPDATE_DATA') {
+        socket.emit('update', mutation.payload)
+      }
+    })
+  }
+}
+```
+
+``` js
+const plugin = createWebSocketPlugin(socket)
+
+const store = new Vuex.Store({
+  state,
+  mutations,
+  plugins: [plugin]
+})
+```
+
+### Prendre des instantanés du state
+
+Parfois un plugin peut vouloir reçevoir des "instantanés" du state, et également comparer le state post-mutation avec le state pre-mutation. Pour faire ceci, vous aurez besoin d'effectuer une deep-copy sur l'objet du state :
+
+``` js
+const myPluginWithSnapshot = store => {
+  let prevState = _.cloneDeep(store.state)
+  store.subscribe((mutation, state) => {
+    let nextState = _.cloneDeep(state)
+
+    // compare prevState and nextState...
+
+    // save state for next mutation
+    prevState = nextState
+  })
+}
+```
+
+**Les plugins qui peuvent prendre des instantanés ne devraient être utilisés que pendant le développement.** Lorsqu'on utilise Webpack ou Browserify, on peut laisser nos devtools s'occuper de ça pour nous :
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  plugins: process.env.NODE_ENV !== 'production'
+    ? [myPluginWithSnapshot]
+    : []
+})
+```
+
+Le plugin sera utilisé par défaut. Pour la production, vous aurez besoin de [DefinePlugin](https://webpack.github.io/docs/list-of-plugins.html#defineplugin) pour Webpack ou de [envify](https://github.com/hughsk/envify) pour Browserify pour convertir la valeur de `process.env.NODE_ENV !== 'production'` à `false` pour le build final.
+
+### Plugin logger intégré
+
+> Si vous utilisez [vue-devtools](https://github.com/vuejs/vue-devtools) vous n'avez probablement pas besoin de ça.
+
+Vuex fournit un plugin de logger à des fins de debugging :
+
+``` js
+import createLogger from 'vuex/dist/logger'
+
+const store = new Vuex.Store({
+  plugins: [createLogger()]
+})
+```
+
+La fonction `createLogger` prend quelques options :
+
+``` js
+const logger = createLogger({
+  collapsed: false, // auto-expand logged mutations
+  transformer (state) {
+    // transform the state before logging it.
+    // for example return only a specific sub-tree
+    return state.subTree
+  },
+  mutationTransformer (mutation) {
+    // mutations are logged in the format of { type, payload }
+    // we can format it any way we want.
+    return mutation.type
+  }
+})
+```
+
+Le fichier logger peut aussi être inclus directement via une balise `script`, et exposera la fonction `createVuexLogger` globalement.
+
+Notez que le plugin logger peut prendre des instantanés du state, ne l'utilisez donc que durant le développement.

+ 109 - 0
docs/fr/state.md

@@ -0,0 +1,109 @@
+# State
+
+### Single State Tree
+
+Vuex utilise un **single state tree** &mdash; cet unique objet contient tout le state au niveau applicatif et sert de "source unique de vérité". Cela signifie également que vous n'aurez qu'un seul store pour chaque application. Un _single state tree_ rend rapide la localisation d'une partie de state spécifique, et nous permet de facilement prendre des instantanés du state actuel de l'application à des fins de debugging.
+
+Le _single state tree_ n'entre pas en conflit avec la modularité &mdash; dans les prochains chapitres, nous examinerons comment séparer votre state et vos mutations dans des sous-modules.
+
+### Récupérer le state Vuex dans des composants Vue
+
+Alors, comment affichons-nous le state du store dans nos composants Vue ? Puisque les stores Vuex sont réactifs, la façon la plus simple d'y "récupérer" le state est de simplement retourner une partie du state depuis une [computed property](http://vuejs.org/guide/computed.html) :
+
+``` js
+// créons un composant Counter
+const Counter = {
+  template: `<div>{{ count }}</div>`,
+  computed: {
+    count () {
+      return store.state.count
+    }
+  }
+}
+```
+
+Lorsque `store.state.count` change, cela entraînera la ré-évaluation de la computed property, et déclenchera les actions DOM associées.
+
+Cependant, ce pattern oblige le composant à compter sur le singleton global du store. Lorsqu'on utilise un système de module, il est nécessaire d'importer le store dans tous les composants qui utilisent le state du store, et il est également nécessaire de créer un mock lorsque l'on teste le composant.
+
+Vuex fournit un méchanisme pour "injecter" le store dans tous les composants enfants du composant racine avec l'option `store` (activée par `Vue.use(Vuex)`) :
+
+``` js
+const app = new Vue({
+  el: '#app',
+  // fournit le store avec l'option "store".
+  // cela injectera l'instance du store dans tous les composants enfants.
+  store,
+  components: { Counter },
+  template: `
+    <div class="app">
+      <counter></counter>
+    </div>
+  `
+})
+```
+
+En fournissant l'option `store` à l'instance racine, le store sera injecté dans tous les composants enfants de la racine et sera disponible sur ceux-ci avec `this.$store`. Mettons à jours notre implémentation de `Counter` :
+
+``` js
+const Counter = {
+  template: `<div>{{ count }}</div>`,
+  computed: {
+    count () {
+      return this.$store.state.count
+    }
+  }
+}
+```
+
+### Le helper `mapState`
+
+Lorsqu'un composant a besoin d'utiliser plusieurs propriétés ou getters du state du store, déclarer toutes ces computed properties peut devenir répétitif et verbeux. Afin de palier à ça, nous pouvons utiliser le helper `mapState` qui génère des fonctions getters pour nous et nous épargne quelques coups de clavier :
+
+``` js
+// dans la version standalone, les helpers sont exposés comme Vuex.mapState
+import { mapState } from 'vuex'
+
+export default {
+  // ...
+  computed: mapState({
+    // les fonctions fléchées peuvent rendre le code très succinct !
+    count: state => state.count,
+
+    // passer la valeur littérale 'count' revient à écrire `state => state.count`
+    countAlias: 'count',
+
+    // pour accéder au state local avec `this`, une fonction normale doit être utilisée
+    countPlusLocalState (state) {
+      return state.count + this.localCount
+    }
+  })
+}
+```
+
+Il est également possible de fournir un tableau de chaînes de caractères à `mapState` lorsque le nom de la propriété du state du store est la même que celui du composant.
+
+``` js
+computed: mapState([
+  // attacher this.count à store.state.count
+  'count'
+])
+```
+
+### Object Spread Operator
+
+Notez que `mapState` renvoie un objet. Comment l'utiliser en complément des autres computed properties locales ? Normalement, il faudrait utiliser un outil pour fusionner les multiples objets en un seul afin de passer cet objet final à `computed`. Cependant avec le [object spread operator](https://github.com/sebmarkbage/ecmascript-rest-spread) (qui est une proposition stage-3 ECMASCript), nous pouvons grandement simplifier la syntaxe :
+
+``` js
+computed: {
+  localComputed () { /* ... */ },
+  // rajouter cet objet dans l'objet `computed` avec l'object spread operator
+  ...mapState({
+    // ...
+  })
+}
+```
+
+### Les composants peuvent toujours avec un state local
+
+Utiliser Vuex ne signifie pas que vous devez mettre **tout** votre state dans Vuex. Bien que le fait de mettre plus de state dans Vuex rende vos mutations de state plus explicites et plus debuggables, parfois il peut aussi rendre le code plus verbeux et indirect. Si une partie de state appartient directement à un seul composant, il est parfaitement sain de la laisser dans le state local. Assurez vous de prendre en compte les avantages et inconvénients d'une telle décision afin de vous adaptez le mieux aux besoins de votre application.

+ 25 - 0
docs/fr/strict.md

@@ -0,0 +1,25 @@
+# Strict Mode
+
+Pour activer le mode strict, passez simplement l'option `strict: true` lorsque vous créez un store Vuex :
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  strict: true
+})
+```
+
+En mode strict, lorsque le state Vuex est modifié en dehors des handlers de mutation, une erreur sera lancée. Cela permet de s'assurer que toutes les mutations du state peuvent être explicitement tracées par les outils de debugging.
+
+### Développement vs. Production
+
+**N'activez pas le mode strict lorsque vous déployez en production !** Le mode strict lance une observation récursive du state tree pour détecter des mutations inappropriées &mdash; assurrez-vous de l'avoir désactivé en production pour éviter un coût sur les performances.
+
+Tout comme les plugins, nous pouvons laisser nos outils de build gérer ça :
+
+``` js
+const store = new Vuex.Store({
+  // ...
+  strict: process.env.NODE_ENV !== 'production'
+})
+```

+ 32 - 0
docs/fr/structure.md

@@ -0,0 +1,32 @@
+# Structure d'une application
+
+Vuex ne vous restreint pas vraiment dans la façon dont vous voulez structurer votre code. Il impose plutôt un set de principes de haut niveau :
+
+1. Le state d'application est centralisé dans le store.
+
+2. La seule façon de muter le state est de commiter des **mutations**, qui sont des transactions synchrones.
+
+3. La logique asynchrone doit être composée et encapsulée dans des **actions**.
+
+Tant que vous suivez ces règles, c'est à vous de structurer votre projet. Si votre fichier de store devient trop gros, commencez simplement par séparer les actions, mutations et getters dans des fichiers séparés.
+
+Pour une application non-triviale, nous aurons probablement besoin de faire appel à des modules. Voici un exemple de structure de projet :
+
+``` bash
+├── index.html
+├── main.js
+├── api
+│   └── ... # abstractions for making API requests
+├── components
+│   ├── App.vue
+│   └── ...
+└── store
+    ├── index.js          # Là où l'on assemble nos modules et exportons le store
+    ├── actions.js        # Actions racine
+    ├── mutations.js      # Mutations racine
+    └── modules
+        ├── cart.js       # cart module
+        └── products.js   # products module
+```
+
+Vous pouvez jeter à un œil à [l'exemple Shopping Cart](https://github.com/vuejs/vuex/tree/dev/examples/shopping-cart).

+ 216 - 0
docs/fr/testing.md

@@ -0,0 +1,216 @@
+# Tests
+
+Les parties principales que l'on veut couvrir par des tests unitaires en Vuex sont les mutations et les actions.
+
+### Tester les mutations
+
+Les mutations sont très simples à tester, parce que ce sont juste des fonctions qui se passent complètement sur leurs arguments. Une astuce est que si vous utilisez les modules ES2015 et mettez vos mutations dans votre fichier `store.js`, en plus de l'export par défaut, vous pouvez également vos mutations avec un export nommé :
+
+``` js
+const state = { ... }
+
+// export mutations as a named export
+export const mutations = { ... }
+
+export default new Vuex.Store({
+  state,
+  mutations
+})
+```
+
+Exemple de test de mutation utilisant Mocha + Chai (vous pouvez utiliser n'importe quel framework/bibliothèque d'assertion selon votre préférence) :
+
+``` js
+// mutations.js
+export const mutations = {
+  increment: state => state.count++
+}
+```
+
+``` js
+// mutations.spec.js
+import { expect } from 'chai'
+import { mutations } from './store'
+
+// destructure assign mutations
+const { increment } = mutations
+
+describe('mutations', () => {
+  it('INCREMENT', () => {
+    // mock state
+    const state = { count: 0 }
+    // apply mutation
+    increment(state)
+    // assert result
+    expect(state.count).to.equal(1)
+  })
+})
+```
+
+### Tester les actions
+
+Les actions sont un peu plus compliquées car elles peuvent faire appel à des APIs externes. Lorsque l'on teste des actions, on a souvent besoin de faire du mocking &mdash; par exemple, on peut abstraire l'appel API dans un service et mocker ce service dans nos tests. Afin de mocker facilement les dépendances, on peut utiliser Webpack et [inject-loader](https://github.com/plasticine/inject-loader) pour regrouper nos fichiers de test.
+
+Exemple de test d'une action asynchrone :
+
+``` js
+// actions.js
+import shop from '../api/shop'
+
+export const getAllProducts = ({ dispatch }) => {
+  dispatch('REQUEST_PRODUCTS')
+  shop.getProducts(products => {
+    dispatch('RECEIVE_PRODUCTS', products)
+  })
+}
+```
+
+``` js
+// actions.spec.js
+
+// use require syntax for inline loaders.
+// with inject-loader, this returns a module factory
+// that allows us to inject mocked dependencies.
+import { expect } from 'chai'
+const actionsInjector = require('inject!./actions')
+
+// create the module with our mocks
+const actions = actionsInjector({
+  '../api/shop': {
+    getProducts (cb) {
+      setTimeout(() => {
+        cb([ /* mocked response */ ])
+      }, 100)
+    }
+  }
+})
+
+// helper for testing action with expected mutations
+const testAction = (action, args, state, expectedMutations, done) => {
+  let count = 0
+
+  // mock commit
+  const commit = (type, payload) => {
+    const mutation = expectedMutations[count]
+    expect(mutation.type).to.equal(type)
+    if (payload) {
+      expect(mutation.payload).to.deep.equal(payload)
+    }
+    count++
+    if (count >= expectedMutations.length) {
+      done()
+    }
+  }
+
+  // call the action with mocked store and arguments
+  action({ commit, state }, ...args)
+
+  // check if no mutations should have been dispatched
+  if (expectedMutations.length === 0) {
+    expect(count).to.equal(0)
+    done()
+  }
+}
+
+describe('actions', () => {
+  it('getAllProducts', done => {
+    testAction(actions.getAllProducts, [], {}, [
+      { type: 'REQUEST_PRODUCTS' },
+      { type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } }
+    ], done)
+  })
+})
+```
+
+### Tester les getters
+
+Si vos getters font des calculs compliqués, il peut être judicieux de les tester. Les getters sont également très simples à tester, pour les mêmes raisons que les mutations.
+
+Exemple de test d'un getter :
+
+``` js
+// getters.js
+export const getters = {
+  filteredProducts (state, { filterCategory }) {
+    return state.products.filter(product => {
+      return product.category === filterCategory
+    })
+  }
+}
+```
+
+``` js
+// getters.spec.js
+import { expect } from 'chai'
+import { getters } from './getters'
+
+describe('getters', () => {
+  it('filteredProducts', () => {
+    // mock state
+    const state = {
+      products: [
+        { id: 1, title: 'Apple', category: 'fruit' },
+        { id: 2, title: 'Orange', category: 'fruit' },
+        { id: 3, title: 'Carrot', category: 'vegetable' }
+      ]
+    }
+    // mock getter
+    const filterCategory = 'fruit'
+
+    // get the result from the getter
+    const result = getters.filteredProducts(state, { filterCategory })
+
+    // assert the result
+    expect(result).to.deep.equal([
+      { id: 1, title: 'Apple', category: 'fruit' },
+      { id: 2, title: 'Orange', category: 'fruit' }
+    ])
+  })
+})
+```
+
+### Lancer les tests
+
+Si vos mutations et actions sont écrites comme il se doit, les tests ne devraient pas avoir de dépendance directe sur les APIs navigateur après un mocking préalable. Cela signifie que vous pouvez simplement regrouper les tests avec Webpack et les lancer directement dans Node. De façon alternative, vous pouvez utiliser `mocha-loader` ou Karma + `karma-webpack` afin d'effectuer les tests dans des vrais navigateurs.
+
+#### Lancer dans Node
+
+Créez la configuration webpack suivante (ainsi que le fichier [`.babelrc`](https://babeljs.io/docs/usage/babelrc/) qui correspond) :
+
+``` js
+// webpack.config.js
+module.exports = {
+  entry: './test.js',
+  output: {
+    path: __dirname,
+    filename: 'test-bundle.js'
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        loader: 'babel',
+        exclude: /node_modules/
+      }
+    ]
+  }
+}
+```
+
+Puis :
+
+``` bash
+webpack
+mocha test-bundle.js
+```
+
+#### Lancer dans un navigateur
+
+1. Installez `mocha-loader`
+2. Changez l'option `entry` de la configuration webpack ci-dessus pour `'mocha!babel!./test.js'`.
+3. Démarrez `webpack-dev-server` en utilisant cette configuration.
+4. Pointez votre navigateur sur `localhost:8080/webpack-dev-server/test-bundle`.
+
+#### Lancer dans un navigateur avec Karma + karma-webpack
+
+Consultez la procédure sur la [documentation vue-loader](http://vue-loader.vuejs.org/en/workflow/testing.html).